Compare commits

..

155 Commits
1.4.7 ... 1.2.2

Author SHA1 Message Date
Jean-Philippe Lang
b222430cb7 tagged version 1.2.2
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/tags/1.2.2@7778 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-11-11 13:01:35 +00:00
Jean-Philippe Lang
20e54f46b0 Merged r7776 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@7777 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-11-11 12:53:35 +00:00
Jean-Philippe Lang
88e918f64d Merged r7774 from trunk (#8615).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@7775 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-11-11 12:44:13 +00:00
Jean-Philippe Lang
b6f0d9da8a Merged r7772 from trunk (#8615).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@7773 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-11-11 12:33:15 +00:00
Jean-Philippe Lang
d6bf26ace7 Merged r6308 from trunk (#8884).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@7770 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-11-11 11:44:10 +00:00
Jean-Philippe Lang
21910aa428 Merged r6311 from trunk (#8880).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@7769 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-11-11 11:40:56 +00:00
Jean-Philippe Lang
f22ff1f1c2 Merged r6298 from trunk (#8865).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@7768 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-11-11 11:37:55 +00:00
Jean-Philippe Lang
83a2283b7d Merged r6300 and r6302 from trunk (#8836).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@7767 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-11-11 11:36:52 +00:00
Jean-Philippe Lang
03b2162e6c Merged r6324 and r6325 from trunk (#8651).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@7766 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-11-11 11:35:03 +00:00
Jean-Philippe Lang
c605477da5 Merged r6314 from trunk (#8633).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@7765 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-11-11 11:31:49 +00:00
Toshi MARUYAMA
35ec8e3570 Merged r7731 from trunk
Spanish translation updated by Ismael G Marin C.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@7732 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-11-05 06:57:52 +00:00
Toshi MARUYAMA
6e68bf8ab4 merge r7707 from trunk
add unit test of escaping image urls

Contributed by Holger Just.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@7709 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-11-02 07:41:12 +00:00
Toshi MARUYAMA
d181a2221a merge r6759 from trunk
remove trailing white-spaces from test/unit/lib/redmine/wiki_formatting/textile_formatter_test.rb

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@7708 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-11-02 07:40:49 +00:00
Toshi MARUYAMA
e4034bc74b backed out previous 1.2-stable dummy commit
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@7703 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-10-30 03:29:14 +00:00
Toshi MARUYAMA
310d4c52ce dummy commit to run test on CI server
Tests fail on CI Server, but I don't know the reason.
http://www.redmine.org/builds/build_1.2-stable-1.8.7-sqlite3_82.html

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@7702 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-10-30 03:28:46 +00:00
Toshi MARUYAMA
1d1b3fc77d Merged r7699 from trunk
Dutch translation updated by Pieter Nicolai

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@7700 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-10-30 00:05:29 +00:00
Toshi MARUYAMA
5e58434567 Merged r7660 from trunk
scm: git: fix typo of comments about fetching revisions

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@7661 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-10-27 01:37:09 +00:00
Toshi MARUYAMA
8da22803be Merged r7658 from trunk
scm: git: recovery and improve comments of fetching from 1.1
about harmful influence that git does not have the revision number.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@7659 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-10-27 00:41:37 +00:00
Toshi MARUYAMA
83ab216d70 Merged r7650 from trunk
scm: mercurial: drop supporting below Mercurial 1.1

On November 1st 2011, Mercurial 2.0 will be released.

On Mercurial 1.1.2, unit lib test fails with following error.

<pre>
Traceback (most recent call last):
  File "/WEB-DOWN/hg-repo/hg-crew/hg", line 20, in <module>
    mercurial.dispatch.run()
  File "/WEB-DOWN/hg-repo/hg-crew/mercurial/dispatch.py", line 20, in run
    sys.exit(dispatch(sys.argv[1:]))
  File "/WEB-DOWN/hg-repo/hg-crew/mercurial/dispatch.py", line 29, in dispatch
    return _runcatch(u, args)
  File "/WEB-DOWN/hg-repo/hg-crew/mercurial/dispatch.py", line 45, in _runcatch
    return _dispatch(ui, args)
  File "/WEB-DOWN/hg-repo/hg-crew/mercurial/dispatch.py", line 367, in _dispatch
    ret = _runcommand(ui, options, cmd, d)
  File "/WEB-DOWN/hg-repo/hg-crew/mercurial/dispatch.py", line 416, in _runcommand
    return checkargs()
  File "/WEB-DOWN/hg-repo/hg-crew/mercurial/dispatch.py", line 376, in checkargs
    return cmdfunc()
  File "/WEB-DOWN/hg-repo/hg-crew/mercurial/dispatch.py", line 361, in <lambda>
    d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
  File "/WEB-DOWN/hg-repo/hg-crew/mercurial/util.py", line 715, in check
    return func(*args, **kwargs)
  File "/REDMINE-1/hg-workdir/redmine-bb-all/lib/redmine/scm/adapters/mercurial/redminehelper.py", line 149, in rhlog
    if hg.util.version() >= '1.6':
AttributeError: 'module' object has no attribute 'version'

  1) Error:
test_nodes_in_branch(MercurialAdapterTest):
Redmine::Scm::Adapters::MercurialAdapter::HgCommandAborted: hg exited with non-zero status: 1
    lib/redmine/scm/adapters/mercurial_adapter.rb:306:in `hg'
    lib/redmine/scm/adapters/mercurial_adapter.rb:234:in `nodes_in_branch'
    test/unit/lib/redmine/scm/adapters/mercurial_adapter_test.rb:311:in `test_nodes_in_branch'
    test/unit/lib/redmine/scm/adapters/mercurial_adapter_test.rb:304:in `each'
    test/unit/lib/redmine/scm/adapters/mercurial_adapter_test.rb:304:in `test_nodes_in_branch'
</pre>

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@7651 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-10-25 07:20:14 +00:00
Toshi MARUYAMA
4104741383 Merged r7643 from trunk
scm: mercurial: skip failing unit model tests on below Mercurial 1.5

Tests of non ASCII nor alphabetic nor numeric *named branch* fails on below Mercurial 1.5.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@7647 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-10-24 11:29:38 +00:00
Toshi MARUYAMA
6dac4a499f Merged r7642 from trunk
scm: mercurial: skip failing unit lib tests on below Mercurial 1.5

Tests of non ASCII nor alphabetic nor numeric *named branch* fails on below Mercurial 1.5.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@7646 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-10-24 11:28:51 +00:00
Toshi MARUYAMA
e0a8442b09 Merged r7641 from trunk
scm: mercurial: switch rev parameter of extension rhlog() if above Mercurial 1.6 or not

On Mercurial 1.5, following error raises.

<pre>
hg --config extensions.redminehelper=lib/redmine/scm/adapters/mercurial/redminehelper.py \
  --rhbranch default --from default --to 0

abort: unknown revision '"default"'!
</pre>

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@7645 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-10-24 11:28:19 +00:00
Toshi MARUYAMA
6dd11c9558 Merged r7640 from trunk
scm: mercurial: fix extension cmdtable on Mercurial 1.5

Before Mercurial revision "40c06bbf58be":http://www.selenic.com/repo/hg-stable/rev/40c06bbf58be ,
following error raises.

<pre>
Traceback (most recent call last):
  File "/WEB-DOWN/hg-repo/hg-crew/hg", line 27, in <module>
    mercurial.dispatch.run()
  File "/WEB-DOWN/hg-repo/hg-crew/mercurial/dispatch.py", line 16, in run
    sys.exit(dispatch(sys.argv[1:]))
  File "/WEB-DOWN/hg-repo/hg-crew/mercurial/dispatch.py", line 30, in dispatch
    return _runcatch(u, args)
  File "/WEB-DOWN/hg-repo/hg-crew/mercurial/dispatch.py", line 47, in _runcatch
    return _dispatch(ui, args)
  File "/WEB-DOWN/hg-repo/hg-crew/mercurial/dispatch.py", line 398, in _dispatch
    cmd, func, args, options, cmdoptions = _parse(lui, args)
  File "/WEB-DOWN/hg-repo/hg-crew/mercurial/dispatch.py", line 277, in _parse
    args = fancyopts.fancyopts(args, c, cmdoptions, True)
  File "/WEB-DOWN/hg-repo/hg-crew/mercurial/fancyopts.py", line 62, in fancyopts
    for short, name, default, comment in options:
ValueError: too many values to unpack

  1) Error:
test_nodes_in_branch(MercurialAdapterTest):
Redmine::Scm::Adapters::MercurialAdapter::HgCommandAborted: hg exited with non-zero status: 1
    lib/redmine/scm/adapters/mercurial_adapter.rb:306:in `hg'
    lib/redmine/scm/adapters/mercurial_adapter.rb:234:in `nodes_in_branch'
    test/unit/lib/redmine/scm/adapters/mercurial_adapter_test.rb:311:in `test_nodes_in_branch'
    test/unit/lib/redmine/scm/adapters/mercurial_adapter_test.rb:304:in `each'
    test/unit/lib/redmine/scm/adapters/mercurial_adapter_test.rb:304:in `test_nodes_in_branch'
</pre>

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@7644 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-10-24 11:27:38 +00:00
Toshi MARUYAMA
1608ca3d98 Merged r7579 from trunk.
Restrict anonymous read access with Redmine.pm

Redmine.pm now also checks for public projects whether the anonymous
user has the browse_repository right for a read operation.

Contributed by Holger Just.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@7580 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-10-04 21:52:08 +00:00
Etienne Massip
0ef89ee4ea Merged r7570 from trunk (#9245).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@7578 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-10-04 19:29:00 +00:00
Toshi MARUYAMA
4813672728 Merged r7571 from trunk.
Fix typo of fix_issue() at Changeset model.

Contributed by Sylvain Utard.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@7572 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-10-04 10:55:32 +00:00
Etienne Massip
ade5409310 Merged r7568 from trunk (#9308).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@7569 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-10-03 20:19:44 +00:00
Etienne Massip
605f1745ff Merged #7563 from trunk (#7215).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@7564 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-10-02 17:28:33 +00:00
Etienne Massip
8158a26ee8 Merged #7560 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@7561 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-10-02 15:35:39 +00:00
Etienne Massip
4682acf040 Merged r7558 from trunk (#3276).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@7559 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-10-02 14:52:43 +00:00
Etienne Massip
3955f81b3f Merged r7538 from trunk (#8411).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@7539 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-09-26 17:47:50 +00:00
Etienne Massip
16f56a4677 Merged r7458 from trunk (#7613).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@7482 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-09-23 17:23:22 +00:00
Toshi MARUYAMA
f77b83451b Merged r7468 from trunk.
Norwegian translation of trunk r5957 and 1.2-stable r7455 updated.

Contributed by Lars Erik Gullerud.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@7471 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-09-23 10:45:28 +00:00
Toshi MARUYAMA
2f2a93c6ef Merged r7451 and r7454 from trunk.
Slovenian translation updated.

Contributed by Nejc Vidmar.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@7455 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-09-22 14:06:18 +00:00
Toshi MARUYAMA
5c282222a0 Backed out r7442.
Merged r7439 from trunk.

Slovenian translation of trunk r5957 and 1.2-stable r7434 updated.

Contributed by Nejc Vidmar.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@7449 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-09-22 12:28:30 +00:00
Toshi MARUYAMA
61b4f41539 Merged r7439 from trunk.
Slovenian translation of trunk r5957 and 1.2-stable r7434 updated.

Contributed by Nejc Vidmar.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@7442 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-09-22 01:52:04 +00:00
Toshi MARUYAMA
73e27a48e0 Merged r7432 from trunk.
fix typos in lib/redmine/hook.rb.

Contributed by Igor Zubkov.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@7434 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-09-21 09:57:26 +00:00
Toshi MARUYAMA
51d7c568b0 Merged r7407 from trunk.
remove trailing white-spaces from lib/redmine/hook.rb

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@7433 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-09-21 09:56:52 +00:00
Toshi MARUYAMA
51d772de70 Merged r7068 from trunk.
fix typo in imap examples at lib/tasks/email.rake.

Contributed by Nathan L Smith.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@7069 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-09-07 01:07:43 +00:00
Toshi MARUYAMA
86845b1989 Merged r7060 from trunk.
add "ruby test/unit/issue_test.rb" for an each test at doc/RUNNING_TESTS.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@7061 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-09-05 11:45:16 +00:00
Toshi MARUYAMA
723d60a1ce Merge r7033 from trunk.
remove duplicate "test_index_should_not_list_issues_when_module_disabled" from functional issues controller test.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@7038 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-09-02 01:30:33 +00:00
Toshi MARUYAMA
b128fcaf0d back out r7036.
I mistook merged revision of the revision comment.
r7035 is wrong. r7033 is correct.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@7037 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-09-02 01:29:54 +00:00
Toshi MARUYAMA
73dd0c49dd Merge r7035 from trunk.
remove duplicate "test_index_should_not_list_issues_when_module_disabled" from functional issues controller test.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@7036 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-09-02 01:15:12 +00:00
Toshi MARUYAMA
cc7274f38c Merge r6915 from trunk.
fix grammatical error of field_warn_on_leaving_unsaved in pt-BR.yml.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6916 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-08-31 14:59:35 +00:00
Toshi MARUYAMA
d0f3b8fa4d Merge r6600 from trunk.
Hungarian translation for 1.2.1 updated by Csaba Molnár.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6601 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-08-24 12:56:36 +00:00
Toshi MARUYAMA
2302955bc5 Merge r6021 from trunk (#8825).
scm: fix JRuby tests fail in unit changeset following test.

* test_invalid_utf8_sequences_in_paths_should_be_replaced

Following Subversion functional tests fail on Japanese Windows + JRuby 1.6.2 (ruby-1.8.7-p330).

* test_directory_diff
* test_revision_diff

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6453 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-08-17 11:14:03 +00:00
Toshi MARUYAMA
90f83db148 Merged r6005 from trunk.
scm: git: skip non UTF-8 path encoding test of unit model test in JRuby.

Git, Mercurial and CVS path encodings are binary.
Subversion supports URL encoding for path.
Redmine Mercurial adapter and extension use URL encoding.
Git accepts only binary path in command line parameter.
So, there is no way to use binary command line parameter in JRuby.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6452 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-08-17 08:16:23 +00:00
Toshi MARUYAMA
b6dbf26c86 Merged r6004 from trunk.
scm: git: skip non UTF-8 path encoding test of unit adapter test in JRuby.

Git, Mercurial and CVS path encodings are binary.
Subversion supports URL encoding for path.
Redmine Mercurial adapter and extension use URL encoding.
Git accepts only binary path in command line parameter.
So, there is no way to use binary command line parameter in JRuby.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6451 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-08-17 08:16:02 +00:00
Toshi MARUYAMA
8c7e0e279c Merged r6003 from trunk.
scm: git: skip non UTF-8 path encoding test of functional test in JRuby.

Git, Mercurial and CVS path encodings are binary.
Subversion supports URL encoding for path.
Redmine Mercurial adapter and extension use URL encoding.
Git accepts only binary path in command line parameter.
So, there is no way to use binary command line parameter in JRuby.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6450 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-08-17 08:15:37 +00:00
Toshi MARUYAMA
6f15634e12 Merged r6447 from trunk.
scm: add comment that configuration of SCM executable command does not work if contains spaces path to configuration.yml.example.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6449 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-08-16 00:18:14 +00:00
Toshi MARUYAMA
7e837aa34d Merged r6418 from trunk.
remove trailing white-spaces from config/configuration.yml.example.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6448 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-08-16 00:17:56 +00:00
Toshi MARUYAMA
6c89118bac Merged r6441 from trunk.
PDF: remove unused vendor/plugins/rfpdf/lib/barcode/*.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6443 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-08-12 09:57:07 +00:00
Toshi MARUYAMA
260c07eb73 Merged r6435 from trunk.
PDF: remove unused sjis.rb.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6442 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-08-12 08:29:47 +00:00
Toshi MARUYAMA
7914381cdd Merged r6434 from trunk.
PDF: remove unused zapfdingbats.rb.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6440 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-08-12 07:25:48 +00:00
Toshi MARUYAMA
65ce05326a Merged r6433 from trunk.
PDF: remove unused symbol.rb.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6439 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-08-12 07:25:06 +00:00
Toshi MARUYAMA
112c7a1b15 Merged r6432 from trunk.
PDF: remove unused courier.rb.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6438 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-08-12 07:24:27 +00:00
Toshi MARUYAMA
23a31dfe27 Merged r6431 from trunk.
PDF: remove unused times*.rb.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6437 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-08-12 07:23:43 +00:00
Toshi MARUYAMA
0dc50cb0b1 Merged r6430 from trunk.
PDF: remove unused vera*.rb.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6436 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-08-12 07:23:00 +00:00
Toshi MARUYAMA
88163849b4 Merged r6427 from trunk.
PDF: remove unused vera fonts.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6429 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-08-12 05:27:03 +00:00
Toshi MARUYAMA
058f36cc6f Merged r6426 from trunk.
PDF: remove unused FreeMono fonts.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6428 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-08-12 05:26:17 +00:00
Toshi MARUYAMA
11cf38a1a8 Merged r6402 from trunk.
PDF: remove unused DejaVu fonts except DejaVuSans-BoldOblique.

FreeSans has freesansbi.rb. But there is no dejavusansbi.rb.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6408 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-08-06 00:02:17 +00:00
Toshi MARUYAMA
37d7a9d62d Merged r6401 from trunk.
PDF: remove unused DejaVuSans-ExtraLight font.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6407 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-08-06 00:01:47 +00:00
Toshi MARUYAMA
1b299369bf Merged r6403 from trunk.
Italian translation for 1.2 updated.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6405 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-08-05 11:24:32 +00:00
Toshi MARUYAMA
cbe42633c9 Merged r6282 from trunk.
scm: bazaar: use "shell_quote_command" method at adapter for JRuby + Windows command name.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6294 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-07-15 23:48:38 +00:00
Toshi MARUYAMA
25657168b1 Merged r6281 from trunk.
scm: git: use self.class.sq_bin for command name at adpter scm_cmd().

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6293 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-07-15 23:48:06 +00:00
Toshi MARUYAMA
4852b38efc Merged r6280 from trunk.
scm: git: use "shell_quote_command" method at adapter for JRuby + Windows command name.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6292 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-07-15 23:47:34 +00:00
Toshi MARUYAMA
b882993d6a Merged r6279 from trunk.
scm: mercurial: use self.class.sq_bin for command name at adpter scm_cmd().

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6291 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-07-15 23:47:08 +00:00
Toshi MARUYAMA
b6c46aa19d Merged r6278 from trunk.
scm: mercurial: use "shell_quote_command" method at adapter for JRuby + Windows command name.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6290 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-07-15 23:46:35 +00:00
Toshi MARUYAMA
0903db52d9 Merged r6277 from trunk.
scm: cvs: use self.class.sq_bin for command name at adpter scm_cmd().

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6289 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-07-15 23:46:01 +00:00
Toshi MARUYAMA
93d256c4a8 Merged r6276 from trunk.
scm: cvs: use "shell_quote_command" method at adapter for JRuby + Windows command name.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6288 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-07-15 23:45:26 +00:00
Toshi MARUYAMA
64a68b86c6 Merged r6275 from trunk.
scm: darcs: use "shell_quote_command" method at adapter for JRuby + Windows command name.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6287 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-07-15 23:44:44 +00:00
Toshi MARUYAMA
1d79bbce44 Merged r6274 from trunk.
scm: subversion: use "shell_quote_command" method at adapter for JRuby + Windows command name.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6286 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-07-15 23:44:07 +00:00
Toshi MARUYAMA
87211bcb75 Merged r6272 from trunk.
scm: add new method "shell_quote_command" at abstract adapter.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6285 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-07-15 23:43:22 +00:00
Toshi MARUYAMA
68e97858cb Merge(backport) r6230 from trunk (#8825).
scm: catch all exceptions at adapter shellout() to fork scm command.

If scm command does not exist,
Linux JRuby 1.6.2 (ruby-1.8.7-p330) raises java.io.IOException in production environment.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6273 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-07-15 11:53:39 +00:00
Jean-Philippe Lang
f5a6b0cf93 Updates for 1.2.1 release.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6261 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-07-11 11:45:14 +00:00
Toshi MARUYAMA
3cf0bd713d Merged r6247 from trunk.
scm: mercurial: add functional test of destroying invalid repository.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6255 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-07-11 02:08:47 +00:00
Toshi MARUYAMA
36bce52f63 Merged r6246 from trunk.
scm: mercurial: do nothing in fetching if info is nil.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6254 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-07-11 02:08:23 +00:00
Toshi MARUYAMA
94f5c73746 Merged r6245 from trunk.
scm: mercurial: catch exception and return nil during getting info.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6253 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-07-11 02:08:03 +00:00
Toshi MARUYAMA
8b6fd63a59 Merged r6243 from trunk.
scm: mercurial: add functional test of destroying valid repository.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6252 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-07-11 02:07:44 +00:00
Toshi MARUYAMA
ba6b822e97 Merged r6242 from trunk.
scm: mercurial: add instance variable @project at functional test.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6251 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-07-11 02:07:19 +00:00
Toshi MARUYAMA
b44044a76a Merge(backport) r6130 from trunk (#8777).
scm: return nil at model default_branch and override at git model.

Redmine Git and Mercurial adapter support *branches*.
Mercurial default branch is *dafault*.
But, it is popular to show all revisions on the top page for Mercurial GUI.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6244 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-07-11 00:24:22 +00:00
Jean-Philippe Lang
eb3a4587c9 Merged r6208 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6209 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-07-09 18:49:13 +00:00
Jean-Philippe Lang
a8550ba8a6 Merged r6206 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6207 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-07-09 12:01:56 +00:00
Jean-Philippe Lang
0527e8cedf Merged r6203 and r6204 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6205 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-07-09 11:42:12 +00:00
Jean-Philippe Lang
2a799e8367 Merged r6201 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6202 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-07-09 11:26:35 +00:00
Jean-Philippe Lang
3e9ad22fad Merged r6199 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6200 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-07-09 10:51:57 +00:00
Jean-Philippe Lang
a5bcdf6d2c Backported r6197 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6198 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-07-09 09:11:13 +00:00
Jean-Philippe Lang
3dcd5d746a Merged r6187 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6188 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-07-06 19:06:03 +00:00
Jean-Philippe Lang
971aa889f9 Merged r6163, r6171, r6172, r6174 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6175 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-07-03 15:58:02 +00:00
Jean-Philippe Lang
cdc1dfb1e7 Merged r6170 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6173 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-07-03 15:37:01 +00:00
Jean-Philippe Lang
30255b8054 Merged r6073 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6162 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-07-03 10:18:18 +00:00
Jean-Philippe Lang
97b697a678 Merged r6126 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6161 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-07-02 17:43:08 +00:00
Jean-Philippe Lang
8e87cd6f77 Merged r6147 and r6149 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6160 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-07-02 17:29:54 +00:00
Jean-Philippe Lang
65bee7efa3 Merged r6148 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6159 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-07-02 17:28:33 +00:00
Jean-Philippe Lang
2325831d81 Merged r5991 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6158 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-07-02 17:28:00 +00:00
Jean-Philippe Lang
c7f8ea6a37 Merged r5969 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6157 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-07-02 17:25:51 +00:00
Toshi MARUYAMA
edc4f6d54b PDF: add dummy 'fill' parameter at rfpdf CJK Write method (#8737).
'fill' parameter is already added in trunk by r6131, r6132, r6133.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6156 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-07-02 15:44:02 +00:00
Toshi MARUYAMA
bf0046b5af PDF: fix ArgumentError MultiCell() in CJK (#8737).
add dummy ln parameter at rfpdf CJK MultiCell method.
ln parameter has already added in trunk by r6131, r6132, r6133.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6155 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-07-02 14:59:30 +00:00
Toshi MARUYAMA
3de4afe53a Merged r6146 from trunk.
PDF: add revision number/id of associated revisions on issue PDF.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6153 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-07-02 13:15:54 +00:00
Toshi MARUYAMA
d15912eb6a Merge(backport) r6136 from trunk (#8737).
PDF: use RDMMultiCell for drawing lines of issue description.

Contributed by Jun NAITOH.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6145 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-07-02 04:48:07 +00:00
Toshi MARUYAMA
656e651c33 Merge(backport) r6135 from trunk (#8737).
PDF: add 'ln' parameter for drawing lines at RDMMultiCell().

Contributed by Jun NAITOH.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6144 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-07-02 04:47:27 +00:00
Toshi MARUYAMA
9543e0a103 Merged r6138 from trunk.
PDF: code clean up lib/redmine/export/pdf.rb.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6143 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-07-02 04:46:47 +00:00
Toshi MARUYAMA
441f2f216f Merged r6139 from trunk.
Swedish translation updated by Nicklas Holm.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6140 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-07-01 09:02:37 +00:00
Toshi MARUYAMA
15228c9958 Merged r6127 from trunk.
Escape AuthSources in the list.

Contributed by MAEDA, Go

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6128 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-06-27 23:19:41 +00:00
Jean-Baptiste Barth
5f33a09320 Merged r6124 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6125 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-06-26 12:51:09 +00:00
Jean-Baptiste Barth
7d77c3544a Merged r6098 from trunk.
Added Project#enable_module! and Project#disable_module!

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6106 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-06-21 03:35:36 +00:00
Jean-Baptiste Barth
102c9afadf Revert r6104 "Merged r6098 from trunk."
I messed things up with a cherry-pick on master, I'll re-apply it after.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6105 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-06-21 03:16:49 +00:00
Jean-Baptiste Barth
16ba6bcb77 Merged r6098 from trunk.
Added Project#enable_module! and Project#disable_module!

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6104 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-06-20 19:39:59 +00:00
Toshi MARUYAMA
df37e5cfab Merged r6101 from trunk.
Russian translation update by SERGEY ERSHOV.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6102 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-06-20 08:54:23 +00:00
Jean-Baptiste Barth
f58b86bbb8 Merged r6099 from trunk.
Added a test to ensure 'Project' column can be removed on issues list

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6100 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-06-20 01:32:18 +00:00
Toshi MARUYAMA
95f68ba6d9 Merged r6096 from trunk.
pt-BR translation update by Enderson Maia.

Better translation to keep a pattern in translations.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6097 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-06-18 10:17:16 +00:00
Toshi MARUYAMA
171c4b2747 Merged r6094 from trunk.
German translation updated by Jens Martsch.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6095 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-06-17 07:13:51 +00:00
Toshi MARUYAMA
860a6ce78d Merged r6068 from trunk.
pt-BR translation update by Rodrigo Rosenfeld Rosas.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6069 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-06-11 21:15:05 +00:00
Toshi MARUYAMA
26d42c26fa Merged r6064 from trunk.
translate field_path_to_repository in pt-BR.yml.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6067 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-06-11 01:34:32 +00:00
Toshi MARUYAMA
c69c434609 Merged r6063 from trunk.
pt-BR translation update by Rodrigo Rosenfeld Rosas.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6066 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-06-11 01:33:51 +00:00
Toshi MARUYAMA
9381d30ae8 Merged r6062 from trunk.
pt-BR translation update by Enderson Maia.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6065 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-06-11 01:33:07 +00:00
Toshi MARUYAMA
136bf86307 Merged r6042 from trunk.
scm: mercurial: fix unit adapter annotate test fails on Windows Mercurial 1.8.4+29-e597ef52a7c2.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6046 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-06-10 08:33:23 +00:00
Toshi MARUYAMA
07f42c53d0 Merged r6041 from trunk.
scm: mercurial: add functional test of annotate file which does not exist in *tip* is not found.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6045 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-06-10 08:32:42 +00:00
Toshi MARUYAMA
22e143f0b3 Merged r6035 from trunk.
PDF: use DejaVuSans fonts in Vietnamese.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6037 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-06-10 02:15:47 +00:00
Toshi MARUYAMA
1701de9123 Merged r6034 from trunk.
PDF: back out r6018.

change Vietnamese vi.yml general_pdf_encoding from CP1258 to UTF-8.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6036 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-06-10 02:15:30 +00:00
Toshi MARUYAMA
0ce6793181 Merged r6031 from trunk.
PDF: fix font name in vendor/plugins/rfpdf/lib/fonts/dejavusansi.rb.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6033 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-06-09 23:51:42 +00:00
Toshi MARUYAMA
bacc44feab Merged r6030 from trunk.
PDF: Import DejaVu fonts.

http://rfpdf.googlecode.com/files/DejaVu.zip
Sep. 2007
fils size 3,685,721
md5sum: 54274d105b74f817d649035fc1541d8a

rename files.
* DejaVuSans.rb  to dejavusans.rb
* DejaVuSansb.rb to dejavusansb.rb
* DejaVuSansi.rb to dejavusansi.rb

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6032 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-06-09 23:44:34 +00:00
Toshi MARUYAMA
4f9a1cd614 Merged r6028 from trunk.
Spanish translation updated by Jorge López.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6029 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-06-09 08:06:24 +00:00
Toshi MARUYAMA
44ed963a46 Merged r6024 from trunk.
PDF: replace all non ASCII characters to '?' if Iconv error raise in JRuby.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6027 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-06-09 05:29:23 +00:00
Toshi MARUYAMA
113316cf1e Merged r6023 from trunk.
PDF: use SJIS instead of CP932 at unit pdf test test_rdm_pdf_iconv_invalid_utf8_should_be_replaced_ja in JRuby.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6026 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-06-09 03:51:23 +00:00
Toshi MARUYAMA
17ed97d580 Merged r6022 from trunk.
PDF: use SJIS instead of CP932 at unit pdf test test_rdm_pdf_iconv_cannot_convert_ja_cp932 in JRuby.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6025 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-06-09 03:50:35 +00:00
Toshi MARUYAMA
19c52bc671 Merged r6018 from trunk.
PDF: change Vietnamese vi.yml general_pdf_encoding from UTF-8 to CP1258.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6019 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-06-09 00:04:11 +00:00
Toshi MARUYAMA
d15de8ee12 Merged r6013 from trunk.
PDF: use SJIS instead of CP932 at unit pdf test_fix_text_encoding_nil in JRuby.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6017 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-06-08 23:53:42 +00:00
Toshi MARUYAMA
006c82267e Merged r6012 from trunk.
PDF: change Shift_JIS to SHIFT_JIS in lib/redmine/export/pdf.rb for Japanese ja.yml general_pdf_encoding.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6016 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-06-08 23:53:24 +00:00
Toshi MARUYAMA
887e7c6781 Merged r6011 from trunk.
PDF: add Japanese comment of ja.yml general_pdf_encoding *CP932* and *SJIS* in JRuby and CRuby (#8565, #61).

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6015 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-06-08 23:53:03 +00:00
Toshi MARUYAMA
6280d9e9b2 Merged r6010 from trunk.
PDF: add *SJIS* and *Shift_JIS* in lib/redmine/export/pdf.rb for Japanese ja.yml general_pdf_encoding.

JRuby 1.6.2 (ruby-1.8.7-p330) does not support CP932.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6014 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-06-08 23:52:43 +00:00
Toshi MARUYAMA
3996d5f42e Merged r6007 from trunk.
scm: add log message of config/configuration.yml if scm command raise Errno::ENOENT exception.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6009 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-06-08 13:24:38 +00:00
Toshi MARUYAMA
579abc1802 Merged r6006 from trunk.
scm: code clean up abstract adapter.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6008 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-06-08 13:23:56 +00:00
Toshi MARUYAMA
dc2efa503a Merged r5999 from trunk.
scm: catch all exceptions to get scm command version in repository model.

If scm command does not exist,
Linux jruby 1.6.2 (ruby-1.8.7-p330) raises java.io.IOException in production environment.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@6000 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-06-03 11:41:34 +00:00
Toshi MARUYAMA
0e6cdeb860 Merged r5992 and r5997 from trunk.
Turkish translation updated by Burak Yiğit Kaya.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@5998 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-06-02 08:26:02 +00:00
Toshi MARUYAMA
3b9ef033bf Backout r5994 Turkish translations.
Tests on CI server fails.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@5996 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-06-02 06:29:15 +00:00
Toshi MARUYAMA
2ae54a0fc1 Merged r5993 from trunk.
change Turkish general_csv_encoding from ISO-8859-1 to ISO-8859-9 by Burak Yiğit Kaya.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@5995 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-06-01 22:55:23 +00:00
Toshi MARUYAMA
a87e6e1265 Merged r5992 from trunk.
Turkish translation updated by Burak Yiğit Kaya.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@5994 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-06-01 22:54:58 +00:00
Toshi MARUYAMA
61b1146f9d Merged r5986 from trunk.
German translation updated by Jens Martsch.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@5987 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-06-01 08:09:23 +00:00
Toshi MARUYAMA
c509c744fd Merged r5970 from trunk.
Traditional Chinese translation updated by ChunChang Lo.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@5971 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-05-31 03:48:27 +00:00
Jean-Philippe Lang
1be21f2413 Merged r5965 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@5967 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-05-30 16:40:52 +00:00
Jean-Philippe Lang
eaf430b630 Merged r5964 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@5966 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-05-30 16:40:17 +00:00
Toshi MARUYAMA
d2789248f7 Merged r5962 from trunk.
Bulgarian translation updated by Ivan Cenov.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@5963 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-05-29 14:54:06 +00:00
Jean-Philippe Lang
f08f09a9d3 Merged r5950 to r5960 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@5961 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-05-29 12:06:05 +00:00
Toshi MARUYAMA
2c2760d041 Merged r5887 from trunk.
scm: replace a tab to two spaces in app/views/settings/_repositories.rhtml.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@5959 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-05-29 11:32:16 +00:00
Toshi MARUYAMA
218e2f32ee Merged r5886 from trunk.
scm: remove trailing white-spaces from app/views/settings/_repositories.rhtml.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@5958 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-05-29 11:31:48 +00:00
Jean-Philippe Lang
f2b01aeb4d Merged r5948 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@5949 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-05-29 08:15:18 +00:00
Jean-Philippe Lang
fabf54a368 Merged r5944 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@5947 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-05-29 07:47:25 +00:00
Jean-Philippe Lang
6b07f86883 Merged r5881 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@5946 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-05-29 07:24:15 +00:00
Jean-Philippe Lang
36d2ea9a6c Merged r5880 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@5945 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-05-29 07:18:05 +00:00
Toshi MARUYAMA
2dca48be53 Merged r5940 from trunk.
remove trailing white-spaces from rake redmine:email:test task source.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@5943 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-05-28 03:34:47 +00:00
Toshi MARUYAMA
7678aa9445 Merged r5939 from trunk.
handle a nil user in rake redmine:email:test task.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@5942 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-05-28 03:34:23 +00:00
Toshi MARUYAMA
9c355ee6c3 Merged r5938 from trunk.
use rake task parameter style message if redmine:email:test parameter is blank.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@5941 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-05-28 03:33:55 +00:00
Jean-Philippe Lang
723030e40a Set version to stable.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@5879 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-05-22 09:20:14 +00:00
Jean-Philippe Lang
e6ed112d9d 1.2-stable branch added.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.2-stable@5878 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-05-22 09:17:45 +00:00
1132 changed files with 45739 additions and 41851 deletions

7
.gitignore vendored
View File

@@ -18,15 +18,8 @@
/public/plugin_assets
/tmp/*
/tmp/cache/*
/tmp/pdf/*
/tmp/sessions/*
/tmp/sockets/*
/tmp/test/*
/vendor/cache
/vendor/rails
*.rbc
/.bundle
/Gemfile.lock
/Gemfile.local

View File

@@ -20,18 +20,10 @@ public/dispatch.*
public/plugin_assets
tmp/*
tmp/cache/*
tmp/pdf/*
tmp/sessions/*
tmp/sockets/*
tmp/test/*
vendor/cache
vendor/rails
*.rbc
.svn/
.git/
.bundle
Gemfile.lock
Gemfile.local

90
Gemfile
View File

@@ -1,90 +0,0 @@
source :rubygems
gem "rails", "2.3.15"
gem "i18n", "~> 0.4.2"
gem "coderay", "~> 1.0.6"
gem "fastercsv", "~> 1.5.0", :platforms => [:mri_18, :mingw_18, :jruby]
gem "tzinfo", "~> 0.3.31"
# Optional gem for LDAP authentication
group :ldap do
gem "net-ldap", "~> 0.3.1"
end
# Optional gem for OpenID authentication
group :openid do
gem "ruby-openid", "~> 2.1.4", :require => "openid"
end
# Optional gem for exporting the gantt to a PNG file, not supported with jruby
platforms :mri, :mingw do
group :rmagick do
# RMagick 2 supports ruby 1.9
# RMagick 1 would be fine for ruby 1.8 but Bundler does not support
# different requirements for the same gem on different platforms
gem "rmagick", ">= 2.0.0"
end
end
# Database gems
platforms :mri, :mingw do
group :postgresql do
gem "pg", ">= 0.11.0"
end
group :sqlite do
gem "sqlite3"
end
end
platforms :mri_18, :mingw_18 do
group :mysql do
gem "mysql", "~> 2.8.1"
end
end
platforms :mri_19, :mingw_19 do
group :mysql do
gem "mysql2", "~> 0.2.7"
end
end
platforms :jruby do
gem "jruby-openssl"
group :mysql do
gem "activerecord-jdbcmysql-adapter"
end
group :postgresql do
gem "activerecord-jdbcpostgresql-adapter"
end
group :sqlite do
gem "activerecord-jdbcsqlite3-adapter"
end
end
group :development do
gem "rdoc", ">= 2.4.2"
end
group :test do
gem "shoulda", "~> 2.10.3"
# Shoulda does not work nice on Ruby 1.9.3 and seems to need test-unit explicitely.
gem "test-unit", :platforms => [:mri_19]
gem "edavis10-object_daddy", :require => "object_daddy"
gem "mocha", "0.12.3"
end
local_gemfile = File.join(File.dirname(__FILE__), "Gemfile.local")
if File.exists?(local_gemfile)
puts "Loading Gemfile.local ..." if $DEBUG # `ruby -d` or `bundle -v`
instance_eval File.read(local_gemfile)
end
# Load plugins' Gemfiles
Dir.glob File.expand_path("../vendor/plugins/*/Gemfile", __FILE__) do |file|
puts "Loading #{file} ..." if $DEBUG # `ruby -d` or `bundle -v`
instance_eval File.read(file)
end

View File

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

View File

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

View File

@@ -1,24 +1,24 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# Copyright (C) 2006-2009 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class AccountController < ApplicationController
helper :custom_fields
include CustomFieldsHelper
include CustomFieldsHelper
# prevents login action to be filtered by check_if_login_required application scope filter
skip_before_filter :check_if_login_required
@@ -29,9 +29,6 @@ class AccountController < ApplicationController
else
authenticate_user
end
rescue AuthSourceException => e
logger.error "An error occured when authenticating #{params[:username]}: #{e.message}"
render_error :message => e.message
end
# Log out current user and redirect to welcome page
@@ -39,7 +36,7 @@ class AccountController < ApplicationController
logout_user
redirect_to home_url
end
# Enable user to choose a new password
def lost_password
redirect_to(home_url) && return unless Setting.lost_password?
@@ -54,7 +51,7 @@ class AccountController < ApplicationController
flash[:notice] = l(:notice_account_password_updated)
redirect_to :action => 'login'
return
end
end
end
render :template => "account/password_recovery"
return
@@ -76,7 +73,7 @@ class AccountController < ApplicationController
end
end
end
# User self-registration
def register
redirect_to(home_url) && return unless Setting.self_registration? || session[:auth_source_registration]
@@ -84,8 +81,7 @@ class AccountController < ApplicationController
session[:auth_source_registration] = nil
@user = User.new(:language => Setting.default_language)
else
@user = User.new
@user.safe_attributes = params[:user]
@user = User.new(params[:user])
@user.admin = false
@user.register
if session[:auth_source_registration]
@@ -100,7 +96,7 @@ class AccountController < ApplicationController
end
else
@user.login = params[:user][:login]
@user.password, @user.password_confirmation = params[:user][:password], params[:user][:password_confirmation]
@user.password, @user.password_confirmation = params[:password], params[:password_confirmation]
case Setting.self_registration
when '1'
@@ -113,7 +109,7 @@ class AccountController < ApplicationController
end
end
end
# Token based account activation
def activate
redirect_to(home_url) && return unless Setting.self_registration? && params[:token]
@@ -128,9 +124,17 @@ class AccountController < ApplicationController
end
redirect_to :action => 'login'
end
private
def logout_user
if User.current.logged?
cookies.delete :autologin
Token.delete_all(["user_id = ? AND action = ?", User.current.id, 'autologin'])
self.logged_user = nil
end
end
def authenticate_user
if Setting.openid? && using_open_id?
open_id_authenticate(params[:openid_url])
@@ -152,6 +156,7 @@ class AccountController < ApplicationController
end
end
def open_id_authenticate(openid_url)
authenticate_with_open_id(openid_url, :required => [:nickname, :fullname, :email], :return_to => signin_url) do |result, identity_url, registration|
if result.successful?
@@ -180,7 +185,7 @@ class AccountController < ApplicationController
register_manually_by_administrator(user) do
onthefly_creation_failed(user)
end
end
end
else
# Existing record
if user.active?
@@ -192,7 +197,7 @@ class AccountController < ApplicationController
end
end
end
def successful_authentication(user)
# Valid user
self.logged_user = user
@@ -203,7 +208,7 @@ class AccountController < ApplicationController
call_hook(:controller_account_success_authentication_after, {:user => user })
redirect_back_or_default :controller => 'my', :action => 'page'
end
def set_autologin_cookie(user)
token = Token.create(:user => user, :action => 'autologin')
cookie_name = Redmine::Configuration['autologin_cookie_name'] || 'autologin'
@@ -242,7 +247,7 @@ class AccountController < ApplicationController
yield if block_given?
end
end
# Automatically register a user
#
# Pass a block for behavior when a user fails to save
@@ -258,7 +263,7 @@ class AccountController < ApplicationController
yield if block_given?
end
end
# Manual activation by the administrator
#
# Pass a block for behavior when a user fails to save

View File

@@ -1,26 +1,22 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class AdminController < ApplicationController
layout 'admin'
menu_item :projects, :only => :projects
menu_item :plugins, :only => :plugins
menu_item :info, :only => :info
before_filter :require_admin
helper :sort
include SortHelper
@@ -30,12 +26,14 @@ class AdminController < ApplicationController
end
def projects
@status = params[:status] || 1
scope = Project.status(@status)
scope = scope.like(params[:name]) if params[:name].present?
@projects = scope.all(:order => 'lft')
@status = params[:status] ? params[:status].to_i : 1
c = ARCondition.new(@status == 0 ? "status <> 0" : ["status = ?", @status])
unless params[:name].blank?
name = "%#{params[:name].strip.downcase}%"
c << ["LOWER(identifier) LIKE ? OR LOWER(name) LIKE ?", name, name]
end
@projects = Project.find :all, :order => 'lft',
:conditions => c.conditions
render :action => "projects", :layout => false if request.xhr?
end
@@ -63,7 +61,7 @@ class AdminController < ApplicationController
# Force ActionMailer to raise delivery errors so we can catch it
ActionMailer::Base.raise_delivery_errors = true
begin
@test = Mailer.deliver_test_email(User.current)
@test = Mailer.deliver_test(User.current)
flash[:notice] = l(:notice_email_sent, User.current.mail)
rescue Exception => e
flash[:error] = l(:notice_email_error, e.message)
@@ -75,9 +73,11 @@ class AdminController < ApplicationController
def info
@db_adapter_name = ActiveRecord::Base.connection.adapter_name
@checklist = [
[:text_default_administrator_account_changed, User.default_admin_account_changed?],
[:text_default_administrator_account_changed,
User.find(:first,
:conditions => ["login=? and hashed_password=?", 'admin', User.hash_password('admin')]).nil?],
[:text_file_repository_writable, File.writable?(Attachment.storage_path)],
[:text_plugin_assets_writable, File.writable?(Redmine::Plugin.public_directory)],
[:text_plugin_assets_writable, File.writable?(Engines.public_directory)],
[:text_rmagick_available, Object.const_defined?(:Magick)]
]
end

View File

@@ -26,38 +26,21 @@ class ApplicationController < ActionController::Base
layout 'base'
exempt_from_layout 'builder', 'rsb'
protect_from_forgery
def handle_unverified_request
super
cookies.delete(:autologin)
end
# FIXME: Remove this when all of Rack and Rails have learned how to
# properly use encodings
before_filter :params_filter
def params_filter
if RUBY_VERSION >= '1.9' && defined?(Rails) && Rails::VERSION::MAJOR < 3
self.utf8nize!(params)
# Remove broken cookie after upgrade from 0.8.x (#4292)
# See https://rails.lighthouseapp.com/projects/8994/tickets/3360
# TODO: remove it when Rails is fixed
before_filter :delete_broken_cookies
def delete_broken_cookies
if cookies['_redmine_session'] && cookies['_redmine_session'] !~ /--/
cookies.delete '_redmine_session'
redirect_to home_path
return false
end
end
def utf8nize!(obj)
if obj.frozen?
obj
elsif obj.is_a? String
obj.respond_to?(:force_encoding) ? obj.force_encoding("UTF-8") : obj
elsif obj.is_a? Hash
obj.each {|k, v| obj[k] = self.utf8nize!(v)}
elsif obj.is_a? Array
obj.each {|v| self.utf8nize!(v)}
else
obj
end
end
before_filter :session_expiration, :user_setup, :check_if_login_required, :set_localization
before_filter :user_setup, :check_if_login_required, :set_localization
filter_parameter_logging :password
protect_from_forgery
rescue_from ActionController::InvalidAuthenticityToken, :with => :invalid_authenticity_token
rescue_from ::Unauthorized, :with => :deny_access
@@ -70,38 +53,6 @@ class ApplicationController < ActionController::Base
require_dependency "repository/#{scm.underscore}"
end
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
@@ -115,7 +66,10 @@ class ApplicationController < ActionController::Base
if session[:user_id]
# existing session
(User.active.find(session[:user_id]) rescue nil)
elsif user = try_to_autologin
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
@@ -133,38 +87,17 @@ class ApplicationController < ActionController::Base
end
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
def logged_user=(user)
reset_session
if user && user.is_a?(User)
User.current = user
start_user_session(user)
session[:user_id] = user.id
else
User.current = User.anonymous
end
end
# Logs out current user
def logout_user
if User.current.logged?
cookies.delete :autologin
Token.delete_all(["user_id = ? AND action = ?", User.current.id, 'autologin'])
self.logged_user = nil
end
end
# check if login is globally required to access the application
def check_if_login_required
# no check needed if user is already logged in
@@ -269,6 +202,8 @@ class ApplicationController < ActionController::Base
render_404 unless @object.present?
@project = @object.project
rescue ActiveRecord::RecordNotFound
render_404
end
def find_model_object
@@ -299,14 +234,23 @@ class ApplicationController < ActionController::Base
render_404
end
# Check if project is unique before bulk operations
def check_project_uniqueness
unless @project
# TODO: let users bulk edit/move/destroy issues from different projects
render_error 'Can not bulk edit/move/destroy issues from different projects'
return false
end
end
# make sure that the user is a member of the project (or admin) if project is private
# used as a before_filter for actions that do not require any particular permission on the project
def check_project_privacy
if @project && @project.active?
if @project.visible?
if @project.is_public? || User.current.member_of?(@project) || User.current.admin?
true
else
deny_access
User.current.logged? ? render_403 : require_login
end
else
@project = nil
@@ -366,19 +310,6 @@ class ApplicationController < ActionController::Base
format.json { head @status }
end
end
# Filter for actions that provide an API response
# but have no HTML representation for non admin users
def require_admin_or_api_request
return true if api_request?
if User.current.admin?
true
elsif User.current.logged?
render_error(:status => 406)
else
deny_access
end
end
# Picks which layout to use based on the request
#
@@ -399,10 +330,21 @@ class ApplicationController < ActionController::Base
@items.sort! {|x,y| y.event_datetime <=> x.event_datetime }
@items = @items.slice(0, Setting.feeds_limit.to_i)
@title = options[:title] || Setting.app_title
render :template => "common/feed.atom", :layout => false,
:content_type => 'application/atom+xml'
render :template => "common/feed.atom.rxml", :layout => false, :content_type => 'application/atom+xml'
end
# TODO: remove in Redmine 1.4
def self.accept_key_auth(*actions)
ActiveSupport::Deprecation.warn "ApplicationController.accept_key_auth is deprecated and will be removed in Redmine 1.4. Use accept_rss_auth (or accept_api_auth) instead."
accept_rss_auth(*actions)
end
# TODO: remove in Redmine 1.4
def accept_key_auth_actions
ActiveSupport::Deprecation.warn "ApplicationController.accept_key_auth_actions is deprecated and will be removed in Redmine 1.4. Use accept_rss_auth (or accept_api_auth) instead."
self.class.accept_rss_auth
end
def self.accept_rss_auth(*actions)
if actions.any?
write_inheritable_attribute('accept_rss_auth_actions', actions)
@@ -410,11 +352,11 @@ class ApplicationController < ActionController::Base
read_inheritable_attribute('accept_rss_auth_actions') || []
end
end
def accept_rss_auth?(action=action_name)
self.class.accept_rss_auth.include?(action.to_sym)
end
def self.accept_api_auth(*actions)
if actions.any?
write_inheritable_attribute('accept_api_auth_actions', actions)
@@ -422,7 +364,7 @@ class ApplicationController < ActionController::Base
read_inheritable_attribute('accept_api_auth_actions') || []
end
end
def accept_api_auth?(action=action_name)
self.class.accept_api_auth.include?(action.to_sym)
end
@@ -499,9 +441,9 @@ class ApplicationController < ActionController::Base
# Returns the API key present in the request
def api_key_from_request
if params[:key].present?
params[:key].to_s
params[:key]
elsif request.headers["X-Redmine-API-Key"].present?
request.headers["X-Redmine-API-Key"].to_s
request.headers["X-Redmine-API-Key"]
end
end
@@ -533,14 +475,24 @@ class ApplicationController < ActionController::Base
render_error "An error occurred while executing the query and has been logged. Please report this error to your Redmine administrator."
end
# Converts the errors on an ActiveRecord object into a common JSON format
def object_errors_to_json(object)
object.errors.collect do |attribute, error|
{ attribute => error }
end.to_json
end
# Renders API response on validation failure
def render_validation_errors(objects)
if objects.is_a?(Array)
@error_messages = objects.map {|object| object.errors.full_messages}.flatten
else
@error_messages = objects.errors.full_messages
end
render :template => 'common/error_messages.api', :status => :unprocessable_entity, :layout => false
def render_validation_errors(object)
options = { :status => :unprocessable_entity, :layout => false }
options.merge!(case params[:format]
when 'xml'; { :xml => object.errors }
when 'json'; { :json => {'errors' => object.errors} } # ActiveResource client compliance
else
raise "Unknown format #{params[:format]} in #render_validation_errors"
end
)
render options
end
# Overrides #default_template so that the api template

View File

@@ -16,34 +16,21 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class AttachmentsController < ApplicationController
before_filter :find_project, :except => :upload
before_filter :file_readable, :read_authorize, :only => [:show, :download]
before_filter :find_project
before_filter :file_readable, :read_authorize, :except => :destroy
before_filter :delete_authorize, :only => :destroy
before_filter :authorize_global, :only => :upload
accept_api_auth :show, :download, :upload
verify :method => :post, :only => :destroy
def show
respond_to do |format|
format.html {
if @attachment.is_diff?
@diff = File.new(@attachment.diskfile, "rb").read
@diff_type = params[:type] || User.current.pref[:diff_type] || 'inline'
@diff_type = 'inline' unless %w(inline sbs).include?(@diff_type)
# Save diff type as user preference
if User.current.logged? && @diff_type != User.current.pref[:diff_type]
User.current.pref[:diff_type] = @diff_type
User.current.preference.save
end
render :action => 'diff'
elsif @attachment.is_text? && @attachment.filesize <= Setting.file_max_size_displayed.to_i.kilobyte
@content = File.new(@attachment.diskfile, "rb").read
render :action => 'file'
else
download
end
}
format.api
if @attachment.is_diff?
@diff = File.new(@attachment.diskfile, "rb").read
render :action => 'diff'
elsif @attachment.is_text? && @attachment.filesize <= Setting.file_max_size_displayed.to_i.kilobyte
@content = File.new(@attachment.diskfile, "rb").read
render :action => 'file'
else
download
end
end
@@ -59,33 +46,7 @@ class AttachmentsController < ApplicationController
end
def upload
# Make sure that API users get used to set this content type
# as it won't trigger Rails' automatic parsing of the request body for parameters
unless request.content_type == 'application/octet-stream'
render :nothing => true, :status => 406
return
end
@attachment = Attachment.new(:file => request.raw_post)
@attachment.author = User.current
@attachment.filename = Redmine::Utils.random_hex(16)
if @attachment.save
respond_to do |format|
format.api { render :action => 'upload', :status => :created }
end
else
respond_to do |format|
format.api { render_validation_errors(@attachment) }
end
end
end
def destroy
if @attachment.container.respond_to?(:init_journal)
@attachment.container.init_journal(User.current)
end
# Make sure association callbacks are called
@attachment.container.attachments.delete(@attachment)
redirect_to :back

View File

@@ -1,47 +1,52 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class AuthSourcesController < ApplicationController
layout 'admin'
menu_item :ldap_authentication
before_filter :require_admin
# GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
verify :method => :post, :only => [ :destroy, :create, :update ],
:redirect_to => { :template => :index }
def index
@auth_source_pages, @auth_sources = paginate AuthSource, :per_page => 10
@auth_source_pages, @auth_sources = paginate auth_source_class.name.tableize, :per_page => 10
render "auth_sources/index"
end
def new
klass_name = params[:type] || 'AuthSourceLdap'
@auth_source = AuthSource.new_subclass_instance(klass_name, params[:auth_source])
@auth_source = auth_source_class.new
render 'auth_sources/new'
end
def create
@auth_source = AuthSource.new_subclass_instance(params[:type], params[:auth_source])
@auth_source = auth_source_class.new(params[:auth_source])
if @auth_source.save
flash[:notice] = l(:notice_successful_create)
redirect_to :action => 'index'
else
render :action => 'new'
render 'auth_sources/new'
end
end
def edit
@auth_source = AuthSource.find(params[:id])
render 'auth_sources/edit'
end
def update
@@ -50,17 +55,17 @@ class AuthSourcesController < ApplicationController
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'index'
else
render :action => 'edit'
render 'auth_sources/edit'
end
end
def test_connection
@auth_source = AuthSource.find(params[:id])
@auth_method = AuthSource.find(params[:id])
begin
@auth_source.test_connection
@auth_method.test_connection
flash[:notice] = l(:notice_successful_connection)
rescue Exception => e
flash[:error] = l(:error_unable_to_connect, e.message)
rescue => text
flash[:error] = l(:error_unable_to_connect, text.message)
end
redirect_to :action => 'index'
end
@@ -73,4 +78,10 @@ class AuthSourcesController < ApplicationController
end
redirect_to :action => 'index'
end
protected
def auth_source_class
AuthSource
end
end

View File

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

View File

@@ -5,25 +5,28 @@
# 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 BoardsController < ApplicationController
default_search_scope :messages
before_filter :find_project_by_project_id, :find_board_if_available, :authorize
before_filter :find_project, :find_board_if_available, :authorize
accept_rss_auth :index, :show
helper :messages
include MessagesHelper
helper :sort
include SortHelper
helper :watchers
include WatchersHelper
def index
@boards = @project.boards
# show the board if there is only one
@@ -40,14 +43,14 @@ class BoardsController < ApplicationController
sort_update 'created_on' => "#{Message.table_name}.created_on",
'replies' => "#{Message.table_name}.replies_count",
'updated_on' => "#{Message.table_name}.updated_on"
@topic_count = @board.topics.count
@topic_pages = Paginator.new self, @topic_count, per_page_option, params['page']
@topics = @board.topics.find :all, :order => ["#{Message.table_name}.sticky DESC", sort_clause].compact.join(', '),
:include => [:author, {:last_reply => :author}],
:limit => @topic_pages.items_per_page,
:offset => @topic_pages.current.offset
@message = Message.new(:board => @board)
@message = Message.new
render :action => 'show', :layout => !request.xhr?
}
format.atom {
@@ -58,32 +61,21 @@ class BoardsController < ApplicationController
}
end
end
verify :method => :post, :only => [ :destroy ], :redirect_to => { :action => :index }
def new
@board = @project.boards.build
@board.safe_attributes = params[:board]
end
def create
@board = @project.boards.build
@board.safe_attributes = params[:board]
if @board.save
@board = Board.new(params[:board])
@board.project = @project
if request.post? && @board.save
flash[:notice] = l(:notice_successful_create)
redirect_to_settings_in_projects
else
render :action => 'new'
end
end
def edit
end
def update
@board.safe_attributes = params[:board]
if @board.save
if request.post? && @board.update_attributes(params[:board])
redirect_to_settings_in_projects
else
render :action => 'edit'
end
end
@@ -91,12 +83,18 @@ class BoardsController < ApplicationController
@board.destroy
redirect_to_settings_in_projects
end
private
def redirect_to_settings_in_projects
redirect_to :controller => 'projects', :action => 'settings', :id => @project, :tab => 'boards'
end
def find_project
@project = Project.find(params[:project_id])
rescue ActiveRecord::RecordNotFound
render_404
end
def find_board_if_available
@board = @project.boards.find(params[:id]) if params[:id]
rescue ActiveRecord::RecordNotFound

View File

@@ -5,12 +5,12 @@
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
@@ -33,11 +33,11 @@ class CalendarsController < ApplicationController
@year = params[:year].to_i
if params[:month] and params[:month].to_i > 0 and params[:month].to_i < 13
@month = params[:month].to_i
end
end
end
@year ||= Date.today.year
@month ||= Date.today.month
@calendar = Redmine::Helpers::Calendar.new(Date.civil(@year, @month, 1), current_language, :month)
retrieve_query
@query.group_by = nil
@@ -47,10 +47,10 @@ class CalendarsController < ApplicationController
:conditions => ["((start_date BETWEEN ? AND ?) OR (due_date BETWEEN ? AND ?))", @calendar.startdt, @calendar.enddt, @calendar.startdt, @calendar.enddt]
)
events += @query.versions(:conditions => ["effective_date BETWEEN ? AND ?", @calendar.startdt, @calendar.enddt])
@calendar.events = events
end
render :action => 'show', :layout => false if request.xhr?
end
end

View File

@@ -1,20 +1,3 @@
# 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 CommentsController < ApplicationController
default_search_scope :news
model_object News
@@ -22,19 +5,18 @@ class CommentsController < ApplicationController
before_filter :find_project_from_association
before_filter :authorize
verify :method => :post, :only => :create, :render => {:nothing => true, :status => :method_not_allowed }
def create
raise Unauthorized unless @news.commentable?
@comment = Comment.new
@comment.safe_attributes = params[:comment]
@comment = Comment.new(params[:comment])
@comment.author = User.current
if @news.comments << @comment
flash[:notice] = l(:label_comment_added)
end
redirect_to :controller => 'news', :action => 'show', :id => @news
end
verify :method => :delete, :only => :destroy, :render => {:nothing => true, :status => :method_not_allowed }
def destroy
@news.comments.find(params[:comment_id]).destroy
redirect_to :controller => 'news', :action => 'show', :id => @news
@@ -50,4 +32,5 @@ class CommentsController < ApplicationController
@comment = nil
@news
end
end

View File

@@ -1,14 +1,20 @@
class ContextMenusController < ApplicationController
helper :watchers
helper :issues
def issues
@issues = Issue.visible.all(:conditions => {:id => params[:ids]}, :include => :project)
if (@issues.size == 1)
@issue = @issues.first
@allowed_statuses = @issue.new_statuses_allowed_to(User.current)
else
@allowed_statuses = @issues.map do |i|
i.new_statuses_allowed_to(User.current)
end.inject do |memo,s|
memo & s
end
end
@allowed_statuses = @issues.map(&:new_statuses_allowed_to).reduce(:&)
@projects = @issues.collect(&:project).compact.uniq
@project = @projects.first if @projects.size == 1
@@ -20,34 +26,19 @@ class ContextMenusController < ApplicationController
:delete => User.current.allowed_to?(:delete_issues, @projects)
}
if @project
if @issue
@assignables = @issue.assignable_users
else
@assignables = @project.assignable_users
end
@assignables = @project.assignable_users
@assignables << @issue.assigned_to if @issue && @issue.assigned_to && !@assignables.include?(@issue.assigned_to)
@trackers = @project.trackers
else
#when multiple projects, we only keep the intersection of each set
@assignables = @projects.map(&:assignable_users).reduce(:&)
@trackers = @projects.map(&:trackers).reduce(:&)
@assignables = @projects.map(&:assignable_users).inject{|memo,a| memo & a}
@trackers = @projects.map(&:trackers).inject{|memo,t| memo & t}
end
@priorities = IssuePriority.active.reverse
@priorities = IssuePriority.all.reverse
@statuses = IssueStatus.find(:all, :order => 'position')
@back = back_url
@options_by_custom_field = {}
if @can[:edit]
custom_fields = @issues.map(&:available_custom_fields).reduce(:&).select do |f|
%w(bool list user version).include?(f.field_format) && !f.multiple?
end
custom_fields.each do |field|
values = field.possible_values_options(@projects)
if values.any?
@options_by_custom_field[field] = values
end
end
end
render :layout => false
end
@@ -57,10 +48,11 @@ class ContextMenusController < ApplicationController
@projects = @time_entries.collect(&:project).compact.uniq
@project = @projects.first if @projects.size == 1
@activities = TimeEntryActivity.shared.active
@can = {:edit => User.current.allowed_to?(:edit_time_entries, @projects),
:delete => User.current.allowed_to?(:edit_time_entries, @projects)
@can = {:edit => User.current.allowed_to?(:log_time, @projects),
:update => User.current.allowed_to?(:log_time, @projects),
:delete => User.current.allowed_to?(:log_time, @projects)
}
@back = back_url
render :layout => false
end
end
end

View File

@@ -1,78 +1,64 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# Copyright (C) 2006-2009 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class CustomFieldsController < ApplicationController
layout 'admin'
before_filter :require_admin
before_filter :build_new_custom_field, :only => [:new, :create]
before_filter :find_custom_field, :only => [:edit, :update, :destroy]
def index
@custom_fields_by_type = CustomField.find(:all).group_by {|f| f.class.name }
@tab = params[:tab] || 'IssueCustomField'
end
def new
end
def create
@custom_field = begin
if params[:type].to_s.match(/.+CustomField$/)
params[:type].to_s.constantize.new(params[:custom_field])
end
rescue
end
(redirect_to(:action => 'index'); return) unless @custom_field.is_a?(CustomField)
if request.post? and @custom_field.save
flash[:notice] = l(:notice_successful_create)
call_hook(:controller_custom_fields_new_after_save, :params => params, :custom_field => @custom_field)
redirect_to :action => 'index', :tab => @custom_field.class.name
else
render :action => 'new'
@trackers = Tracker.find(:all, :order => 'position')
end
end
def edit
end
def update
if request.put? and @custom_field.update_attributes(params[:custom_field])
@custom_field = CustomField.find(params[:id])
if request.post? and @custom_field.update_attributes(params[:custom_field])
flash[:notice] = l(:notice_successful_update)
call_hook(:controller_custom_fields_edit_after_save, :params => params, :custom_field => @custom_field)
redirect_to :action => 'index', :tab => @custom_field.class.name
else
render :action => 'edit'
@trackers = Tracker.find(:all, :order => 'position')
end
end
def destroy
@custom_field.destroy
@custom_field = CustomField.find(params[:id]).destroy
redirect_to :action => 'index', :tab => @custom_field.class.name
rescue
flash[:error] = l(:error_can_not_delete_custom_field)
redirect_to :action => 'index'
end
private
def build_new_custom_field
@custom_field = CustomField.new_subclass_instance(params[:type], params[:custom_field])
if @custom_field.nil?
render_404
end
end
def find_custom_field
@custom_field = CustomField.find(params[:id])
rescue ActiveRecord::RecordNotFound
render_404
end
end

View File

@@ -1,16 +1,16 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
@@ -18,13 +18,13 @@
class DocumentsController < ApplicationController
default_search_scope :documents
model_object Document
before_filter :find_project_by_project_id, :only => [:index, :new, :create]
before_filter :find_model_object, :except => [:index, :new, :create]
before_filter :find_project_from_association, :except => [:index, :new, :create]
before_filter :find_project, :only => [:index, :new]
before_filter :find_model_object, :except => [:index, :new]
before_filter :find_project_from_association, :except => [:index, :new]
before_filter :authorize
helper :attachments
def index
@sort_by = %w(category date title author).include?(params[:sort_by]) ? params[:sort_by] : 'category'
documents = @project.documents.find :all, :include => [:attachments, :category]
@@ -41,47 +41,34 @@ class DocumentsController < ApplicationController
@document = @project.documents.build
render :layout => false if request.xhr?
end
def show
@attachments = @document.attachments.find(:all, :order => "created_on DESC")
end
def new
@document = @project.documents.build
@document.safe_attributes = params[:document]
end
def create
@document = @project.documents.build
@document.safe_attributes = params[:document]
@document.save_attachments(params[:attachments])
if @document.save
@document = @project.documents.build(params[:document])
if request.post? and @document.save
attachments = Attachment.attach_files(@document, params[:attachments])
render_attachment_warning_if_needed(@document)
flash[:notice] = l(:notice_successful_create)
redirect_to :action => 'index', :project_id => @project
else
render :action => 'new'
end
end
def edit
end
def update
@document.safe_attributes = params[:document]
if request.put? and @document.save
@categories = DocumentCategory.all
if request.post? and @document.update_attributes(params[:document])
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'show', :id => @document
else
render :action => 'edit'
end
end
end
def destroy
@document.destroy if request.delete?
@document.destroy
redirect_to :controller => 'documents', :action => 'index', :project_id => @project
end
def add_attachment
attachments = Attachment.attach_files(@document, params[:attachments])
render_attachment_warning_if_needed(@document)
@@ -89,4 +76,11 @@ class DocumentsController < ApplicationController
Mailer.deliver_attachments_added(attachments[:files]) if attachments.present? && attachments[:files].present? && Setting.notified_events.include?('document_added')
redirect_to :action => 'show', :id => @document
end
private
def find_project
@project = Project.find(params[:project_id])
rescue ActiveRecord::RecordNotFound
render_404
end
end

View File

@@ -1,57 +1,76 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class EnumerationsController < ApplicationController
layout 'admin'
before_filter :require_admin
before_filter :build_new_enumeration, :only => [:new, :create]
before_filter :find_enumeration, :only => [:edit, :update, :destroy]
helper :custom_fields
include CustomFieldsHelper
def index
list
render :action => 'list'
end
# GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
verify :method => :post, :only => [ :destroy, :create, :update ],
:redirect_to => { :action => :list }
def list
end
def new
begin
@enumeration = params[:type].constantize.new
rescue NameError
@enumeration = Enumeration.new
end
end
def create
if request.post? && @enumeration.save
@enumeration = Enumeration.new(params[:enumeration])
@enumeration.type = params[:enumeration][:type]
if @enumeration.save
flash[:notice] = l(:notice_successful_create)
redirect_to :action => 'index', :type => @enumeration.type
redirect_to :action => 'list', :type => @enumeration.type
else
render :action => 'new'
end
end
def edit
@enumeration = Enumeration.find(params[:id])
end
def update
if request.put? && @enumeration.update_attributes(params[:enumeration])
@enumeration = Enumeration.find(params[:id])
@enumeration.type = params[:enumeration][:type] if params[:enumeration][:type]
if @enumeration.update_attributes(params[:enumeration])
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'index', :type => @enumeration.type
redirect_to :action => 'list', :type => @enumeration.type
else
render :action => 'edit'
end
end
def destroy
@enumeration = Enumeration.find(params[:id])
if !@enumeration.in_use?
# No associated objects
@enumeration.destroy
@@ -64,22 +83,9 @@ class EnumerationsController < ApplicationController
return
end
end
@enumerations = @enumeration.class.all - [@enumeration]
end
private
def build_new_enumeration
class_name = params[:enumeration] && params[:enumeration][:type] || params[:type]
@enumeration = Enumeration.new_subclass_instance(class_name, params[:enumeration])
if @enumeration.nil?
render_404
end
end
def find_enumeration
@enumeration = Enumeration.find(params[:id])
rescue ActiveRecord::RecordNotFound
render_404
@enumerations = @enumeration.class.find(:all) - [@enumeration]
#rescue
# flash[:error] = 'Unable to delete enumeration'
# redirect_to :action => 'index'
end
end

View File

@@ -5,12 +5,12 @@
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
@@ -29,16 +29,16 @@ class GanttsController < ApplicationController
helper :sort
include SortHelper
include Redmine::Export::PDF
def show
@gantt = Redmine::Helpers::Gantt.new(params)
@gantt.project = @project
retrieve_query
@query.group_by = nil
@gantt.query = @query if @query.valid?
basename = (@project ? "#{@project.identifier}-" : '') + 'gantt'
respond_to do |format|
format.html { render :action => "show", :layout => !request.xhr? }
format.png { send_data(@gantt.to_image, :disposition => 'inline', :type => 'image/png', :filename => "#{basename}.png") } if @gantt.respond_to?('to_image')

View File

@@ -1,27 +1,27 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# Copyright (C) 2006-2009 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class GroupsController < ApplicationController
layout 'admin'
before_filter :require_admin
helper :custom_fields
# GET /groups
# GET /groups.xml
def index
@@ -48,7 +48,7 @@ class GroupsController < ApplicationController
# GET /groups/new.xml
def new
@group = Group.new
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => @group }
@@ -67,10 +67,8 @@ class GroupsController < ApplicationController
respond_to do |format|
if @group.save
format.html {
flash[:notice] = l(:notice_successful_create)
redirect_to(params[:continue] ? new_group_path : groups_path)
}
flash[:notice] = l(:notice_successful_create)
format.html { redirect_to(groups_path) }
format.xml { render :xml => @group, :status => :created, :location => @group }
else
format.html { render :action => "new" }
@@ -107,37 +105,37 @@ class GroupsController < ApplicationController
format.xml { head :ok }
end
end
def add_users
@group = Group.find(params[:id])
users = User.find_all_by_id(params[:user_ids])
@group.users << users if request.post?
respond_to do |format|
format.html { redirect_to :controller => 'groups', :action => 'edit', :id => @group, :tab => 'users' }
format.js {
render(:update) {|page|
format.js {
render(:update) {|page|
page.replace_html "tab-content-users", :partial => 'groups/users'
users.each {|user| page.visual_effect(:highlight, "user-#{user.id}") }
}
}
end
end
def remove_user
@group = Group.find(params[:id])
@group.users.delete(User.find(params[:user_id])) if request.delete?
@group.users.delete(User.find(params[:user_id])) if request.post?
respond_to do |format|
format.html { redirect_to :controller => 'groups', :action => 'edit', :id => @group, :tab => 'users' }
format.js { render(:update) {|page| page.replace_html "tab-content-users", :partial => 'groups/users'} }
end
end
def autocomplete_for_user
@group = Group.find(params[:id])
@users = User.active.not_in_group(@group).like(params[:q]).all(:limit => 100)
render :layout => false
end
def edit_membership
@group = Group.find(params[:id])
@membership = Member.edit_membership(params[:membership_id], params[:membership], @group)
@@ -160,7 +158,7 @@ class GroupsController < ApplicationController
end
end
end
def destroy_membership
@group = Group.find(params[:id])
Member.find(params[:membership_id]).destroy if request.post?

View File

@@ -1,16 +1,16 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
@@ -18,92 +18,58 @@
class IssueCategoriesController < ApplicationController
menu_item :settings
model_object IssueCategory
before_filter :find_model_object, :except => [:index, :new, :create]
before_filter :find_project_from_association, :except => [:index, :new, :create]
before_filter :find_project_by_project_id, :only => [:index, :new, :create]
before_filter :find_model_object, :except => :new
before_filter :find_project_from_association, :except => :new
before_filter :find_project, :only => :new
before_filter :authorize
accept_api_auth :index, :show, :create, :update, :destroy
def index
respond_to do |format|
format.html { redirect_to :controller => 'projects', :action => 'settings', :tab => 'categories', :id => @project }
format.api { @categories = @project.issue_categories.all }
end
end
def show
respond_to do |format|
format.html { redirect_to :controller => 'projects', :action => 'settings', :tab => 'categories', :id => @project }
format.api
end
end
verify :method => :post, :only => :destroy
def new
@category = @project.issue_categories.build
@category.safe_attributes = params[:issue_category]
end
def create
@category = @project.issue_categories.build
@category.safe_attributes = params[:issue_category]
if @category.save
respond_to do |format|
format.html do
flash[:notice] = l(:notice_successful_create)
redirect_to :controller => 'projects', :action => 'settings', :tab => 'categories', :id => @project
@category = @project.issue_categories.build(params[:category])
if request.post?
if @category.save
respond_to do |format|
format.html do
flash[:notice] = l(:notice_successful_create)
redirect_to :controller => 'projects', :action => 'settings', :tab => 'categories', :id => @project
end
format.js do
# IE doesn't support the replace_html rjs method for select box options
render(:update) {|page| page.replace "issue_category_id",
content_tag('select', '<option></option>' + options_from_collection_for_select(@project.issue_categories, 'id', 'name', @category.id), :id => 'issue_category_id', :name => 'issue[category_id]')
}
end
end
format.js do
# IE doesn't support the replace_html rjs method for select box options
render(:update) {|page| page.replace "issue_category_id",
content_tag('select', content_tag('option') + options_from_collection_for_select(@project.issue_categories, 'id', 'name', @category.id), :id => 'issue_category_id', :name => 'issue[category_id]')
}
else
respond_to do |format|
format.html
format.js do
render(:update) {|page| page.alert(@category.errors.full_messages.join('\n')) }
end
end
format.api { render :action => 'show', :status => :created, :location => issue_category_path(@category) }
end
else
respond_to do |format|
format.html { render :action => 'new'}
format.js do
render(:update) {|page| page.alert(@category.errors.full_messages.join('\n')) }
end
format.api { render_validation_errors(@category) }
end
end
end
def edit
end
def update
@category.safe_attributes = params[:issue_category]
if @category.save
respond_to do |format|
format.html {
flash[:notice] = l(:notice_successful_update)
redirect_to :controller => 'projects', :action => 'settings', :tab => 'categories', :id => @project
}
format.api { head :ok }
end
else
respond_to do |format|
format.html { render :action => 'edit' }
format.api { render_validation_errors(@category) }
end
if request.post? and @category.update_attributes(params[:category])
flash[:notice] = l(:notice_successful_update)
redirect_to :controller => 'projects', :action => 'settings', :tab => 'categories', :id => @project
end
end
def destroy
@issue_count = @category.issues.size
if @issue_count == 0 || params[:todo] || api_request?
reassign_to = nil
if params[:reassign_to_id] && (params[:todo] == 'reassign' || params[:todo].blank?)
reassign_to = @project.issue_categories.find_by_id(params[:reassign_to_id])
end
if @issue_count == 0
# No issue assigned to this category
@category.destroy
redirect_to :controller => 'projects', :action => 'settings', :id => @project, :tab => 'categories'
return
elsif params[:todo]
reassign_to = @project.issue_categories.find_by_id(params[:reassign_to_id]) if params[:todo] == 'reassign'
@category.destroy(reassign_to)
respond_to do |format|
format.html { redirect_to :controller => 'projects', :action => 'settings', :id => @project, :tab => 'categories' }
format.api { head :ok }
end
redirect_to :controller => 'projects', :action => 'settings', :id => @project, :tab => 'categories'
return
end
@categories = @project.issue_categories - [@category]
@@ -115,5 +81,11 @@ private
def find_model_object
super
@category = @object
end
def find_project
@project = Project.find(params[:project_id])
rescue ActiveRecord::RecordNotFound
render_404
end
end

View File

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

View File

@@ -1,52 +1,30 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# Copyright (C) 2006-2007 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class IssueRelationsController < ApplicationController
before_filter :find_issue, :find_project_from_association, :authorize, :only => [:index, :create]
before_filter :find_relation, :except => [:index, :create]
accept_api_auth :index, :show, :create, :destroy
def index
@relations = @issue.relations
respond_to do |format|
format.html { render :nothing => true }
format.api
end
end
def show
raise Unauthorized unless @relation.visible?
respond_to do |format|
format.html { render :nothing => true }
format.api
end
end
def create
before_filter :find_issue, :find_project_from_association, :authorize
def new
@relation = IssueRelation.new(params[:relation])
@relation.issue_from = @issue
if params[:relation] && m = params[:relation][:issue_to_id].to_s.strip.match(/^#?(\d+)$/)
if params[:relation] && m = params[:relation][:issue_to_id].to_s.match(/^#?(\d+)$/)
@relation.issue_to = Issue.visible.find_by_id(m[1].to_i)
end
saved = @relation.save
@relation.save if request.post?
respond_to do |format|
format.html { redirect_to :controller => 'issues', :action => 'show', :id => @issue }
format.js do
@@ -59,37 +37,28 @@ class IssueRelationsController < ApplicationController
end
end
end
format.api {
if saved
render :action => 'show', :status => :created, :location => relation_url(@relation)
else
render_validation_errors(@relation)
end
end
end
def destroy
relation = IssueRelation.find(params[:id])
if request.post? && @issue.relations.include?(relation)
relation.destroy
@issue.reload
end
respond_to do |format|
format.html { redirect_to :controller => 'issues', :action => 'show', :id => @issue }
format.js {
@relations = @issue.relations.select {|r| r.other_issue(@issue) && r.other_issue(@issue).visible? }
render(:update) {|page| page.replace_html "relations", :partial => 'issues/relations'}
}
end
end
def destroy
raise Unauthorized unless @relation.deletable?
@relation.destroy
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 }
end
end
private
def find_issue
@issue = @object = Issue.find(params[:issue_id])
rescue ActiveRecord::RecordNotFound
render_404
end
def find_relation
@relation = IssueRelation.find(params[:id])
rescue ActiveRecord::RecordNotFound
render_404
end
end

View File

@@ -1,37 +1,31 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class IssueStatusesController < ApplicationController
layout 'admin'
before_filter :require_admin
before_filter :require_admin, :except => :index
before_filter :require_admin_or_api_request, :only => :index
accept_api_auth :index
verify :method => :post, :only => [ :destroy, :create, :update, :move, :update_issue_done_ratio ],
:redirect_to => { :action => :index }
def index
respond_to do |format|
format.html {
@issue_status_pages, @issue_statuses = paginate :issue_statuses, :per_page => 25, :order => "position"
render :action => "index", :layout => false if request.xhr?
}
format.api {
@issue_statuses = IssueStatus.all(:order => 'position')
}
end
@issue_status_pages, @issue_statuses = paginate :issue_statuses, :per_page => 25, :order => "position"
render :action => "index", :layout => false if request.xhr?
end
def new
@@ -40,7 +34,7 @@ class IssueStatusesController < ApplicationController
def create
@issue_status = IssueStatus.new(params[:issue_status])
if request.post? && @issue_status.save
if @issue_status.save
flash[:notice] = l(:notice_successful_create)
redirect_to :action => 'index'
else
@@ -54,7 +48,7 @@ class IssueStatusesController < ApplicationController
def update
@issue_status = IssueStatus.find(params[:id])
if request.put? && @issue_status.update_attributes(params[:issue_status])
if @issue_status.update_attributes(params[:issue_status])
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'index'
else
@@ -69,9 +63,9 @@ class IssueStatusesController < ApplicationController
flash[:error] = l(:error_unable_delete_issue_status)
redirect_to :action => 'index'
end
def update_issue_done_ratio
if request.post? && IssueStatus.update_issue_done_ratios
if IssueStatus.update_issue_done_ratios
flash[:notice] = l(:notice_issue_done_ratios_updated)
else
flash[:error] = l(:error_issue_done_ratios_not_updated)

View File

@@ -20,7 +20,8 @@ class IssuesController < ApplicationController
default_search_scope :issues
before_filter :find_issue, :only => [:show, :edit, :update]
before_filter :find_issues, :only => [:bulk_edit, :bulk_update, :destroy]
before_filter :find_issues, :only => [:bulk_edit, :bulk_update, :move, :perform_move, :destroy]
before_filter :check_project_uniqueness, :only => [:move, :perform_move]
before_filter :find_project, :only => [:new, :create]
before_filter :authorize, :except => [:index]
before_filter :find_optional_project, :only => [:index]
@@ -53,6 +54,14 @@ class IssuesController < ApplicationController
helper :gantt
include Redmine::Export::PDF
verify :method => [:post, :delete],
:only => :destroy,
:render => { :nothing => true, :status => :method_not_allowed }
verify :method => :post, :only => :create, :render => {:nothing => true, :status => :method_not_allowed }
verify :method => :post, :only => :bulk_update, :render => {:nothing => true, :status => :method_not_allowed }
verify :method => :put, :only => :update, :render => {:nothing => true, :status => :method_not_allowed }
def index
retrieve_query
sort_init(@query.sort_criteria.empty? ? [['id', 'desc']] : @query.sort_criteria)
@@ -80,20 +89,15 @@ class IssuesController < ApplicationController
@issue_count_by_group = @query.issue_count_by_group
respond_to do |format|
format.html { render :template => 'issues/index', :layout => !request.xhr? }
format.api {
Issue.load_relations(@issues) if include_in_api_response?('relations')
}
format.html { render :template => 'issues/index.rhtml', :layout => !request.xhr? }
format.api
format.atom { render_feed(@issues, :title => "#{@project || Setting.app_title}: #{l(:label_issue_plural)}") }
format.csv { send_data(issues_to_csv(@issues, @project, @query, params), :type => 'text/csv; header=present', :filename => 'export.csv') }
format.csv { send_data(issues_to_csv(@issues, @project), :type => 'text/csv; header=present', :filename => 'export.csv') }
format.pdf { send_data(issues_to_pdf(@issues, @project, @query), :type => 'application/pdf', :filename => 'export.pdf') }
end
else
respond_to do |format|
format.html { render(:template => 'issues/index', :layout => !request.xhr?) }
format.any(:atom, :csv, :pdf) { render(:nothing => true) }
format.api { render_validation_errors(@query) }
end
# Send html if the query is not valid
render(:template => 'issues/index.rhtml', :layout => !request.xhr?)
end
rescue ActiveRecord::RecordNotFound
render_404
@@ -104,19 +108,18 @@ class IssuesController < ApplicationController
@journals.each_with_index {|j,i| j.indice = i+1}
@journals.reverse! if User.current.wants_comments_in_reverse_order?
@changesets = @issue.changesets.visible.all
@changesets.reverse! if User.current.wants_comments_in_reverse_order?
if User.current.allowed_to?(:view_changesets, @project)
@changesets = @issue.changesets.visible.all
@changesets.reverse! if User.current.wants_comments_in_reverse_order?
end
@relations = @issue.relations.select {|r| r.other_issue(@issue) && r.other_issue(@issue).visible? }
@allowed_statuses = @issue.new_statuses_allowed_to(User.current)
@edit_allowed = User.current.allowed_to?(:edit_issues, @project)
@priorities = IssuePriority.active
@priorities = IssuePriority.all
@time_entry = TimeEntry.new(:issue => @issue, :project => @issue.project)
respond_to do |format|
format.html {
retrieve_previous_and_next_issue_ids
render :template => 'issues/show'
}
format.html { render :template => 'issues/show.rhtml' }
format.api
format.atom { render :template => 'journals/index', :layout => false, :content_type => 'application/atom+xml' }
format.pdf { send_data(issue_to_pdf(@issue), :type => 'application/pdf', :filename => "#{@project.identifier}-#{@issue.id}.pdf") }
@@ -128,30 +131,20 @@ 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 => 'attributes' }
end
end
def create
call_hook(:controller_issues_new_before_save, { :params => params, :issue => @issue })
@issue.save_attachments(params[:attachments] || (params[:issue] && params[:issue][:uploads]))
if @issue.save
attachments = Attachment.attach_files(@issue, params[:attachments])
render_attachment_warning_if_needed(@issue)
flash[:notice] = l(:notice_successful_create)
call_hook(:controller_issues_new_after_save, { :params => params, :issue => @issue})
respond_to do |format|
format.html {
render_attachment_warning_if_needed(@issue)
flash[:notice] = l(:notice_issue_successful_create, :id => "<a href='#{issue_path(@issue)}'>##{@issue.id}</a>")
redirect_to(params[:continue] ? { :action => 'new', :project_id => @issue.project, :issue => {:tracker_id => @issue.tracker, :parent_issue_id => @issue.parent_issue_id}.reject {|k,v| v.nil?} } :
redirect_to(params[:continue] ? { :action => 'new', :project_id => @project, :issue => {:tracker_id => @issue.tracker, :parent_issue_id => @issue.parent_issue_id}.reject {|k,v| v.nil?} } :
{ :action => 'show', :id => @issue })
}
format.api { render :action => 'show', :status => :created, :location => issue_url(@issue) }
@@ -166,7 +159,9 @@ class IssuesController < ApplicationController
end
def edit
return unless update_issue_from_params
update_issue_from_params
@journal = @issue.current_journal
respond_to do |format|
format.html { }
@@ -175,24 +170,9 @@ class IssuesController < ApplicationController
end
def update
return unless update_issue_from_params
@issue.save_attachments(params[:attachments] || (params[:issue] && params[:issue][:uploads]))
saved = false
begin
saved = @issue.save_issue_with_child_records(params, @time_entry)
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
end
end
update_issue_from_params
if saved
if @issue.save_issue_with_child_records(params, @time_entry)
render_attachment_warning_if_needed(@issue)
flash[:notice] = l(:notice_successful_update) unless @issue.current_journal.new_record?
@@ -201,6 +181,10 @@ class IssuesController < ApplicationController
format.api { head :ok }
end
else
render_attachment_warning_if_needed(@issue)
flash[:notice] = l(:notice_successful_update) unless @issue.current_journal.new_record?
@journal = @issue.current_journal
respond_to do |format|
format.html { render :action => 'edit' }
format.api { render_validation_errors(@issue) }
@@ -208,74 +192,32 @@ class IssuesController < ApplicationController
end
end
# Bulk edit/copy a set of issues
# Bulk edit a set of issues
def bulk_edit
@issues.sort!
@copy = params[:copy].present?
@notes = params[:notes]
if User.current.allowed_to?(:move_issues, @projects)
@allowed_projects = Issue.allowed_target_projects_on_move
if params[:issue]
@target_project = @allowed_projects.detect {|p| p.id.to_s == params[:issue][:project_id].to_s}
if @target_project
target_projects = [@target_project]
end
end
end
target_projects ||= @projects
if @copy
@available_statuses = [IssueStatus.default]
else
@available_statuses = @issues.map(&:new_statuses_allowed_to).reduce(:&)
end
@custom_fields = target_projects.map{|p|p.all_issue_custom_fields}.reduce(:&)
@assignables = target_projects.map(&:assignable_users).reduce(:&)
@trackers = target_projects.map(&:trackers).reduce(:&)
@versions = target_projects.map {|p| p.shared_versions.open}.reduce(:&)
@categories = target_projects.map {|p| p.issue_categories}.reduce(:&)
if @copy
@attachments_present = @issues.detect {|i| i.attachments.any?}.present?
end
@safe_attributes = @issues.map(&:safe_attribute_names).reduce(:&)
render :layout => false if request.xhr?
@available_statuses = @projects.map{|p|Workflow.available_statuses(p)}.inject{|memo,w|memo & w}
@custom_fields = @projects.map{|p|p.all_issue_custom_fields}.inject{|memo,c|memo & c}
@assignables = @projects.map(&:assignable_users).inject{|memo,a| memo & a}
@trackers = @projects.map(&:trackers).inject{|memo,t| memo & t}
end
def bulk_update
@issues.sort!
@copy = params[:copy].present?
attributes = parse_params_for_bulk_issue_attributes(params)
unsaved_issue_ids = []
moved_issues = []
@issues.each do |issue|
issue.reload
if @copy
issue = issue.copy({}, :attachments => params[:copy_attachments].present?)
end
journal = issue.init_journal(User.current, params[:notes])
issue.safe_attributes = attributes
call_hook(:controller_issues_bulk_edit_before_save, { :params => params, :issue => issue })
if issue.save
moved_issues << issue
else
unless issue.save
# Keep unsaved issue ids to display them in flash error
unsaved_issue_ids << issue.id
end
end
set_flash_from_bulk_issue_save(@issues, unsaved_issue_ids)
if params[:follow]
if @issues.size == 1 && moved_issues.size == 1
redirect_to :controller => 'issues', :action => 'show', :id => moved_issues.first
elsif moved_issues.map(&:project).uniq.size == 1
redirect_to :controller => 'issues', :action => 'index', :project_id => moved_issues.map(&:project).first
end
else
redirect_back_or_default({:controller => 'issues', :action => 'index', :project_id => @project})
end
redirect_back_or_default({:controller => 'issues', :action => 'index', :project_id => @project})
end
def destroy
@@ -327,58 +269,25 @@ private
end
def find_project
project_id = params[:project_id] || (params[:issue] && params[:issue][:project_id])
project_id = (params[:issue] && params[:issue][:project_id]) || params[:project_id]
@project = Project.find(project_id)
rescue ActiveRecord::RecordNotFound
render_404
end
def retrieve_previous_and_next_issue_ids
retrieve_query_from_session
if @query
sort_init(@query.sort_criteria.empty? ? [['id', 'desc']] : @query.sort_criteria)
sort_update(@query.sortable_columns, 'issues_index_sort')
limit = 500
issue_ids = @query.issue_ids(:order => sort_clause, :limit => (limit + 1), :include => [:assigned_to, :tracker, :priority, :category, :fixed_version])
if (idx = issue_ids.index(@issue.id)) && idx < limit
if issue_ids.size < 500
@issue_position = idx + 1
@issue_count = issue_ids.size
end
@prev_issue_id = issue_ids[idx - 1] if idx > 0
@next_issue_id = issue_ids[idx + 1] if idx < (issue_ids.size - 1)
end
end
end
# Used by #edit and #update to set some common instance variables
# from the params
# TODO: Refactor, not everything in here is needed by #edit
def update_issue_from_params
@allowed_statuses = @issue.new_statuses_allowed_to(User.current)
@priorities = IssuePriority.all
@edit_allowed = User.current.allowed_to?(:edit_issues, @project)
@time_entry = TimeEntry.new(:issue => @issue, :project => @issue.project)
@time_entry.attributes = params[:time_entry]
@notes = params[:notes] || (params[:issue].present? ? params[:issue][:notes] : nil)
@issue.init_journal(User.current, @notes)
issue_attributes = params[:issue]
if issue_attributes && params[:conflict_resolution]
case params[:conflict_resolution]
when 'overwrite'
issue_attributes = issue_attributes.dup
issue_attributes.delete(:lock_version)
when 'add_notes'
issue_attributes = {}
when 'cancel'
redirect_to issue_path(@issue)
return false
end
end
@issue.safe_attributes = issue_attributes
@priorities = IssuePriority.active
@allowed_statuses = @issue.new_statuses_allowed_to(User.current)
true
@issue.safe_attributes = params[:issue]
end
# TODO: Refactor, lots of extra code in here
@@ -386,16 +295,7 @@ private
def build_new_issue_from_params
if params[:id].blank?
@issue = Issue.new
if params[:copy_from]
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)
rescue ActiveRecord::RecordNotFound
render_404
return
end
end
@issue.copy_from(params[:copy_from]) if params[:copy_from]
@issue.project = @project
else
@issue = @project.issues.visible.find(params[:id])
@@ -409,12 +309,15 @@ private
render_error l(:error_no_tracker_in_project)
return false
end
@issue.start_date ||= Date.today if Setting.default_issue_start_date_to_creation_date?
@issue.safe_attributes = params[:issue]
@priorities = IssuePriority.active
@issue.start_date ||= Date.today
if params[:issue].is_a?(Hash)
@issue.safe_attributes = params[:issue]
if User.current.allowed_to?(:add_issue_watchers, @project) && @issue.new_record?
@issue.watcher_user_ids = params[:issue]['watcher_user_ids']
end
end
@priorities = IssuePriority.all
@allowed_statuses = @issue.new_statuses_allowed_to(User.current, true)
@available_watchers = (@issue.project.users.sort + @issue.watcher_users).uniq
end
def check_for_default_issue_status
@@ -427,16 +330,7 @@ private
def parse_params_for_bulk_issue_attributes(params)
attributes = (params[:issue] || {}).reject {|k,v| v.blank?}
attributes.keys.each {|k| attributes[k] = '' if attributes[k] == 'none'}
if custom = attributes[:custom_field_values]
custom.reject! {|k,v| v.blank?}
custom.keys.each do |k|
if custom[k].is_a?(Array)
custom[k] << '' if custom[k].delete('__none__')
else
custom[k] = '' if custom[k] == '__none__'
end
end
end
attributes[:custom_field_values].reject! {|k,v| v.blank?} if attributes[:custom_field_values]
attributes
end
end

View File

@@ -5,12 +5,12 @@
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
@@ -22,7 +22,7 @@ class JournalsController < ApplicationController
before_filter :authorize, :only => [:new, :edit, :diff]
accept_rss_auth :index
menu_item :issues
helper :issues
helper :custom_fields
helper :queries
@@ -34,9 +34,9 @@ class JournalsController < ApplicationController
retrieve_query
sort_init 'id', 'desc'
sort_update(@query.sortable_columns)
if @query.valid?
@journals = @query.journals(:order => "#{Journal.table_name}.created_on DESC",
@journals = @query.journals(:order => "#{Journal.table_name}.created_on DESC",
:limit => 25)
end
@title = (@project ? @project.name : Setting.app_title) + ": " + (@query.new_record? ? l(:label_changes_details) : @query.name)
@@ -44,7 +44,7 @@ class JournalsController < ApplicationController
rescue ActiveRecord::RecordNotFound
render_404
end
def diff
@issue = @journal.issue
if params[:detail_id].present?
@@ -55,7 +55,7 @@ class JournalsController < ApplicationController
(render_404; return false) unless @issue && @detail
@diff = Redmine::Helpers::Diff.new(@detail.value, @detail.old_value)
end
def new
journal = Journal.find(params[:journal_id]) if params[:journal_id]
if journal
@@ -69,7 +69,7 @@ class JournalsController < ApplicationController
text = text.to_s.strip.gsub(%r{<pre>((.|\s)*?)</pre>}m, '[...]')
content = "#{ll(Setting.default_language, :text_user_wrote, user)}\n> "
content << text.gsub(/(\r?\n|\r\n?)/, "\n> ") + "\n\n"
render(:update) { |page|
page.<< "$('notes').value = \"#{escape_javascript content}\";"
page.show 'update'
@@ -78,7 +78,7 @@ class JournalsController < ApplicationController
page << "$('notes').scrollTop = $('notes').scrollHeight - $('notes').clientHeight;"
}
end
def edit
(render_403; return false) unless @journal.editable_by?(User.current)
if request.post?
@@ -93,15 +93,15 @@ class JournalsController < ApplicationController
respond_to do |format|
format.html {
# TODO: implement non-JS journal update
render :nothing => true
render :nothing => true
}
format.js
end
end
end
private
def find_journal
@journal = Journal.find(params[:id])
@project = @journal.journalized.project

View File

@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -15,12 +15,11 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
desc 'Removes uploaded files left unattached after one day.'
class LdapAuthSourcesController < AuthSourcesController
namespace :redmine do
namespace :attachments do
task :prune => :environment do
Attachment.prune
end
protected
def auth_source_class
AuthSourceLdap
end
end

View File

@@ -18,6 +18,10 @@
class MailHandlerController < ActionController::Base
before_filter :check_credential
verify :method => :post,
:only => :index,
:render => { :nothing => true, :status => 405 }
# Submits an incoming email to MailHandler
def index
options = params.dup

View File

@@ -1,82 +1,54 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class MembersController < ApplicationController
model_object Member
before_filter :find_model_object, :except => [:index, :create, :autocomplete]
before_filter :find_project_from_association, :except => [:index, :create, :autocomplete]
before_filter :find_project_by_project_id, :only => [:index, :create, :autocomplete]
before_filter :find_model_object, :except => [:new, :autocomplete_for_member]
before_filter :find_project_from_association, :except => [:new, :autocomplete_for_member]
before_filter :find_project, :only => [:new, :autocomplete_for_member]
before_filter :authorize
accept_api_auth :index, :show, :create, :update, :destroy
def index
@offset, @limit = api_offset_and_limit
@member_count = @project.member_principals.count
@member_pages = Paginator.new self, @member_count, @limit, params['page']
@offset ||= @member_pages.current.offset
@members = @project.member_principals.all(
:order => "#{Member.table_name}.id",
:limit => @limit,
:offset => @offset
)
respond_to do |format|
format.html { head 406 }
format.api
end
end
def show
respond_to do |format|
format.html { head 406 }
format.api
end
end
def create
def new
members = []
if params[:membership]
if params[:membership][:user_ids]
attrs = params[:membership].dup
user_ids = attrs.delete(:user_ids)
if params[:member] && request.post?
attrs = params[:member].dup
if (user_ids = attrs.delete(:user_ids))
user_ids.each do |user_id|
members << Member.new(:role_ids => params[:membership][:role_ids], :user_id => user_id)
members << Member.new(attrs.merge(:user_id => user_id))
end
else
members << Member.new(:role_ids => params[:membership][:role_ids], :user_id => params[:membership][:user_id])
members << Member.new(attrs)
end
@project.members << members
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|
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
render :action => 'show', :status => :created, :location => membership_url(@member)
}
else
format.js {
render(:update) {|page|
errors = members.collect {|m|
@@ -86,37 +58,28 @@ class MembersController < ApplicationController
page.alert(l(:notice_failed_to_save_members, :errors => errors.join(', ')))
}
}
format.api { render_validation_errors(members.first) }
end
end
end
def edit
if request.post? and @member.update_attributes(params[:member])
respond_to do |format|
format.html { redirect_to :controller => 'projects', :action => 'settings', :tab => 'members', :id => @project }
format.js {
render(:update) {|page|
page.replace_html "tab-content-members", :partial => 'projects/settings/members'
page << 'hideOnLoad()'
page.visual_effect(:highlight, "member-#{@member.id}")
}
}
end
end
end
def update
if params[:membership]
@member.role_ids = params[:membership][:role_ids]
end
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.api {
if saved
head :ok
else
render_validation_errors(@member)
end
}
end
end
def destroy
if request.delete? && @member.deletable?
if request.post? && @member.deletable?
@member.destroy
end
respond_to do |format|
@@ -126,18 +89,11 @@ class MembersController < ApplicationController
page << 'hideOnLoad()'
}
}
format.api {
if @member.destroyed?
head :ok
else
head :unprocessable_entity
end
}
end
end
def autocomplete
@principals = Principal.active.not_member_of(@project).like(params[:q]).all(:limit => 100)
def autocomplete_for_member
@principals = Principal.active.like(params[:q]).find(:all, :limit => 100) - @project.principals
render :layout => false
end

View File

@@ -1,16 +1,16 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
@@ -22,12 +22,15 @@ class MessagesController < ApplicationController
before_filter :find_message, :except => [:new, :preview]
before_filter :authorize, :except => [:preview, :edit, :destroy]
verify :method => :post, :only => [ :reply, :destroy ], :redirect_to => { :action => :show }
verify :xhr => true, :only => :quote
helper :watchers
helper :attachments
include AttachmentsHelper
include AttachmentsHelper
REPLIES_PER_PAGE = 25 unless const_defined?(:REPLIES_PER_PAGE)
# Show a topic and its replies
def show
page = params[:page]
@@ -36,40 +39,40 @@ class MessagesController < ApplicationController
offset = @topic.children.count(:conditions => ["#{Message.table_name}.id < ?", params[:r].to_i])
page = 1 + offset / REPLIES_PER_PAGE
end
@reply_count = @topic.children.count
@reply_pages = Paginator.new self, @reply_count, REPLIES_PER_PAGE, page
@replies = @topic.children.find(:all, :include => [:author, :attachments, {:board => :project}],
:order => "#{Message.table_name}.created_on ASC",
:limit => @reply_pages.items_per_page,
:offset => @reply_pages.current.offset)
@reply = Message.new(:subject => "RE: #{@message.subject}")
render :action => "show", :layout => false if request.xhr?
end
# Create a new topic
def new
@message = Message.new
@message = Message.new(params[:message])
@message.author = User.current
@message.board = @board
@message.safe_attributes = params[:message]
if request.post?
@message.save_attachments(params[:attachments])
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
end
if params[:message] && User.current.allowed_to?(:edit_messages, @project)
@message.locked = params[:message]['locked']
@message.sticky = params[:message]['sticky']
end
if request.post? && @message.save
call_hook(:controller_messages_new_after_save, { :params => params, :message => @message})
attachments = Attachment.attach_files(@message, params[:attachments])
render_attachment_warning_if_needed(@message)
redirect_to :action => 'show', :id => @message
end
end
# Reply to a topic
def reply
@reply = Message.new
@reply = Message.new(params[:reply])
@reply.author = User.current
@reply.board = @board
@reply.safe_attributes = params[:reply]
@topic.children << @reply
if !@reply.new_record?
call_hook(:controller_messages_reply_after_save, { :params => params, :message => @reply})
@@ -82,8 +85,11 @@ class MessagesController < ApplicationController
# Edit a message
def edit
(render_403; return false) unless @message.editable_by?(User.current)
@message.safe_attributes = params[:message]
if request.post? && @message.save
if params[:message]
@message.locked = params[:message]['locked']
@message.sticky = params[:message]['sticky']
end
if request.post? && @message.update_attributes(params[:message])
attachments = Attachment.attach_files(@message, params[:attachments])
render_attachment_warning_if_needed(@message)
flash[:notice] = l(:notice_successful_update)
@@ -91,7 +97,7 @@ class MessagesController < ApplicationController
redirect_to :action => 'show', :board_id => @message.board, :id => @message.root, :r => (@message.parent_id && @message.id)
end
end
# Delete a messages
def destroy
(render_403; return false) unless @message.destroyable_by?(User.current)
@@ -100,7 +106,7 @@ class MessagesController < ApplicationController
{ :controller => 'boards', :action => 'show', :project_id => @project, :id => @board } :
{ :action => 'show', :id => @message.parent, :r => @message }
end
def quote
user = @message.author
text = @message.content
@@ -109,7 +115,7 @@ class MessagesController < ApplicationController
content = "#{ll(Setting.default_language, :text_user_wrote, user)}\\n> "
content << text.to_s.strip.gsub(%r{<pre>((.|\s)*?)</pre>}m, '[...]').gsub('"', '\"').gsub(/(\r?\n|\r\n?)/, "\\n> ") + "\\n\\n"
render(:update) { |page|
page << "$('message_subject').value = \"#{subject}\";"
page << "$('reply_subject').value = \"#{subject}\";"
page.<< "$('message_content').value = \"#{content}\";"
page.show 'reply'
page << "Form.Element.focus('message_content');"
@@ -117,14 +123,14 @@ class MessagesController < ApplicationController
page << "$('message_content').scrollTop = $('message_content').scrollHeight - $('message_content').clientHeight;"
}
end
def preview
message = @board.messages.find_by_id(params[:id])
@attachements = message.attachments if message
@text = (params[:message] || params[:reply])[:content]
render :partial => 'common/preview'
end
private
def find_message
find_board
@@ -133,7 +139,7 @@ private
rescue ActiveRecord::RecordNotFound
render_404
end
def find_board
@board = Board.find(params[:board_id], :include => :project)
@project = @board.project

View File

@@ -1,16 +1,16 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# Copyright (C) 2006-2009 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
@@ -31,10 +31,13 @@ class MyController < ApplicationController
'timelog' => :label_spent_time
}.merge(Redmine::Views::MyPage::Block.additional_blocks).freeze
DEFAULT_LAYOUT = { 'left' => ['issuesassignedtome'],
'right' => ['issuesreportedbyme']
DEFAULT_LAYOUT = { 'left' => ['issuesassignedtome'],
'right' => ['issuesreportedbyme']
}.freeze
verify :xhr => true,
:only => [:add_block, :remove_block, :order_blocks]
def index
page
render :action => 'page'
@@ -65,24 +68,6 @@ class MyController < ApplicationController
end
end
# Destroys user's account
def destroy
@user = User.current
unless @user.own_account_deletable?
redirect_to :action => 'account'
return
end
if request.post? && params[:confirm]
@user.destroy
if @user.destroyed?
logout_user
flash[:notice] = l(:notice_account_deleted)
end
redirect_to home_path
end
end
# Manage user's password
def password
@user = User.current
@@ -103,7 +88,7 @@ class MyController < ApplicationController
end
end
end
# Create a new feeds key
def reset_rss_key
if request.post?
@@ -137,7 +122,7 @@ class MyController < ApplicationController
@block_options = []
BLOCKS.each {|k, v| @block_options << [l("my.blocks.#{v}", :default => [v, v.to_s.humanize]), k.dasherize]}
end
# Add a block to user's page
# The block is added on top of the page
# params[:block] : id of the block to add
@@ -151,10 +136,10 @@ class MyController < ApplicationController
# add it on top
layout['top'].unshift block
@user.pref[:my_page_layout] = layout
@user.pref.save
@user.pref.save
render :partial => "block", :locals => {:user => @user, :block_name => block}
end
# Remove a block to user's page
# params[:block] : id of the block to remove
def remove_block
@@ -164,7 +149,7 @@ class MyController < ApplicationController
layout = @user.pref[:my_page_layout] || {}
%w(top left right).each {|f| (layout[f] ||= []).delete block }
@user.pref[:my_page_layout] = layout
@user.pref.save
@user.pref.save
render :nothing => true
end
@@ -184,7 +169,7 @@ class MyController < ApplicationController
}
layout[group] = group_items
@user.pref[:my_page_layout] = layout
@user.pref.save
@user.pref.save
end
end
render :nothing => true

View File

@@ -5,12 +5,12 @@
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
@@ -20,15 +20,14 @@ class NewsController < ApplicationController
model_object News
before_filter :find_model_object, :except => [:new, :create, :index]
before_filter :find_project_from_association, :except => [:new, :create, :index]
before_filter :find_project_by_project_id, :only => [:new, :create]
before_filter :find_project, :only => [:new, :create]
before_filter :authorize, :except => [:index]
before_filter :find_optional_project, :only => :index
accept_rss_auth :index
accept_api_auth :index
helper :watchers
helper :attachments
def index
case params[:format]
when 'xml', 'json'
@@ -36,9 +35,9 @@ class NewsController < ApplicationController
else
@limit = 10
end
scope = @project ? @project.news.visible : News.visible
@news_count = scope.count
@news_pages = Paginator.new self, @news_count, @limit, params['page']
@offset ||= @news_pages.current.offset
@@ -46,17 +45,14 @@ class NewsController < ApplicationController
:order => "#{News.table_name}.created_on DESC",
:offset => @offset,
:limit => @limit)
respond_to do |format|
format.html {
@news = News.new # for adding news inline
render :layout => false if request.xhr?
}
format.html { render :layout => false if request.xhr? }
format.api
format.atom { render_feed(@newss, :title => (@project ? @project.name : Setting.app_title) + ": #{l(:label_news_plural)}") }
end
end
def show
@comments = @news.comments
@comments.reverse! if User.current.wants_comments_in_reverse_order?
@@ -68,25 +64,22 @@ class NewsController < ApplicationController
def create
@news = News.new(:project => @project, :author => User.current)
@news.safe_attributes = params[:news]
@news.save_attachments(params[:attachments])
if @news.save
render_attachment_warning_if_needed(@news)
flash[:notice] = l(:notice_successful_create)
redirect_to :controller => 'news', :action => 'index', :project_id => @project
else
render :action => 'new'
if request.post?
@news.attributes = params[:news]
if @news.save
flash[:notice] = l(:notice_successful_create)
redirect_to :controller => 'news', :action => 'index', :project_id => @project
else
render :action => 'new'
end
end
end
def edit
end
def update
@news.safe_attributes = params[:news]
@news.save_attachments(params[:attachments])
if @news.save
render_attachment_warning_if_needed(@news)
if request.put? and @news.update_attributes(params[:news])
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'show', :id => @news
else
@@ -98,9 +91,14 @@ class NewsController < ApplicationController
@news.destroy
redirect_to :action => 'index', :project_id => @project
end
private
private
def find_project
@project = Project.find(params[:project_id])
rescue ActiveRecord::RecordNotFound
render_404
end
def find_optional_project
return true unless params[:project_id]
@project = Project.find(params[:project_id])

View File

@@ -5,12 +5,12 @@
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
@@ -39,12 +39,12 @@ class PreviewsController < ApplicationController
end
private
def find_project
project_id = (params[:issue] && params[:issue][:project_id]) || params[:project_id]
@project = Project.find(project_id)
rescue ActiveRecord::RecordNotFound
render_404
end
end

View File

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

View File

@@ -5,12 +5,12 @@
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
@@ -19,7 +19,7 @@ class ProjectsController < ApplicationController
menu_item :overview
menu_item :roadmap, :only => :roadmap
menu_item :settings, :only => :settings
before_filter :find_project, :except => [ :index, :list, :new, :create, :copy ]
before_filter :authorize, :except => [ :index, :list, :new, :create, :copy, :archive, :unarchive, :destroy]
before_filter :authorize_global, :only => [:new, :create]
@@ -29,26 +29,26 @@ class ProjectsController < ApplicationController
after_filter :only => [:create, :edit, :update, :archive, :unarchive, :destroy] do |controller|
if controller.request.post?
controller.send :expire_action, :controller => 'welcome', :action => 'robots'
controller.send :expire_action, :controller => 'welcome', :action => 'robots.txt'
end
end
helper :sort
include SortHelper
helper :custom_fields
include CustomFieldsHelper
include CustomFieldsHelper
helper :issues
helper :queries
include QueriesHelper
helper :repositories
include RepositoriesHelper
include ProjectsHelper
# Lists visible projects
def index
respond_to do |format|
format.html {
@projects = Project.visible.find(:all, :order => 'lft')
format.html {
@projects = Project.visible.find(:all, :order => 'lft')
}
format.api {
@offset, @limit = api_offset_and_limit
@@ -62,14 +62,14 @@ class ProjectsController < ApplicationController
}
end
end
def new
@issue_custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position")
@trackers = Tracker.all
@project = Project.new
@project.safe_attributes = params[:project]
@project = Project.new(params[:project])
end
verify :method => :post, :only => :create, :render => {:nothing => true, :status => :method_not_allowed }
def create
@issue_custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position")
@trackers = Tracker.all
@@ -85,12 +85,9 @@ class ProjectsController < ApplicationController
@project.members << m
end
respond_to do |format|
format.html {
format.html {
flash[:notice] = l(:notice_successful_create)
redirect_to(params[:continue] ?
{:controller => 'projects', :action => 'new', :project => {:parent_id => @project.parent_id}.reject {|k,v| v.nil?}} :
{:controller => 'projects', :action => 'settings', :id => @project}
)
redirect_to :controller => 'projects', :action => 'settings', :id => @project
}
format.api { render :action => 'show', :status => :created, :location => url_for(:controller => 'projects', :action => 'show', :id => @project.id) }
end
@@ -100,9 +97,9 @@ class ProjectsController < ApplicationController
format.api { render_validation_errors(@project) }
end
end
end
def copy
@issue_custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position")
@trackers = Tracker.all
@@ -116,7 +113,7 @@ class ProjectsController < ApplicationController
@project.identifier = Project.next_identifier if Setting.sequential_project_identifiers?
else
redirect_to :controller => 'admin', :action => 'projects'
end
end
else
Mailer.with_deliveries(params[:notifications] == '1') do
@project = Project.new
@@ -144,27 +141,27 @@ class ProjectsController < ApplicationController
# try to redirect to the requested menu item
redirect_to_project_menu_item(@project, params[:jump]) && return
end
@users_by_role = @project.users_by_role
@subprojects = @project.children.visible.all
@news = @project.news.find(:all, :limit => 5, :include => [ :author, :project ], :order => "#{News.table_name}.created_on DESC")
@trackers = @project.rolled_up_trackers
cond = @project.project_condition(Setting.display_subprojects_issues?)
@open_issues_by_tracker = Issue.visible.count(:group => :tracker,
:include => [:project, :status, :tracker],
:conditions => ["(#{cond}) AND #{IssueStatus.table_name}.is_closed=?", false])
@total_issues_by_tracker = Issue.visible.count(:group => :tracker,
:include => [:project, :status, :tracker],
:conditions => cond)
if User.current.allowed_to?(:view_time_entries, @project)
@total_hours = TimeEntry.visible.sum(:hours, :include => :project, :conditions => cond).to_f
end
@key = User.current.rss_key
respond_to do |format|
format.html
format.api
@@ -176,18 +173,21 @@ class ProjectsController < ApplicationController
@issue_category ||= IssueCategory.new
@member ||= @project.members.new
@trackers = Tracker.all
@repository ||= @project.repository
@wiki ||= @project.wiki
end
def edit
end
# TODO: convert to PUT only
verify :method => [:post, :put], :only => :update, :render => {:nothing => true, :status => :method_not_allowed }
def update
@project.safe_attributes = params[:project]
if validate_parent_id && @project.save
@project.set_allowed_parent!(params[:project]['parent_id']) if params[:project].has_key?('parent_id')
respond_to do |format|
format.html {
format.html {
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'settings', :id => @project
}
@@ -195,7 +195,7 @@ class ProjectsController < ApplicationController
end
else
respond_to do |format|
format.html {
format.html {
settings
render :action => 'settings'
}
@@ -204,6 +204,7 @@ class ProjectsController < ApplicationController
end
end
verify :method => :post, :only => :modules, :render => {:nothing => true, :status => :method_not_allowed }
def modules
@project.enabled_module_names = params[:enabled_module_names]
flash[:notice] = l(:notice_successful_update)
@@ -218,27 +219,38 @@ class ProjectsController < ApplicationController
end
redirect_to(url_for(:controller => 'admin', :action => 'projects', :status => params[:status]))
end
def unarchive
@project.unarchive if request.post? && !@project.active?
redirect_to(url_for(:controller => 'admin', :action => 'projects', :status => params[:status]))
end
# Delete @project
def destroy
@project_to_destroy = @project
if api_request? || params[:confirm]
@project_to_destroy.destroy
respond_to do |format|
format.html { redirect_to :controller => 'admin', :action => 'projects' }
format.api { head :ok }
if request.get?
# display confirmation view
else
if api_request? || params[:confirm]
@project_to_destroy.destroy
respond_to do |format|
format.html { redirect_to :controller => 'admin', :action => 'projects' }
format.api { head :ok }
end
end
end
# hide project in layout
@project = nil
end
private
private
def find_optional_project
return true unless params[:id]
@project = Project.find(params[:id])
authorize
rescue ActiveRecord::RecordNotFound
render_404
end
# Validates parent_id param according to user's permissions
# TODO: move it to Project model in a validation that depends on User.current

View File

@@ -1,94 +1,67 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class QueriesController < ApplicationController
menu_item :issues
before_filter :find_query, :except => [:new, :create, :index]
before_filter :find_optional_project, :only => [:new, :create]
accept_api_auth :index
include QueriesHelper
def index
case params[:format]
when 'xml', 'json'
@offset, @limit = api_offset_and_limit
else
@limit = per_page_option
end
@query_count = Query.visible.count
@query_pages = Paginator.new self, @query_count, @limit, params['page']
@queries = Query.visible.all(:limit => @limit, :offset => @offset, :order => "#{Query.table_name}.name")
respond_to do |format|
format.html { render :nothing => true }
format.api
end
end
before_filter :find_query, :except => :new
before_filter :find_optional_project, :only => :new
def new
@query = Query.new
@query.user = User.current
@query.project = @project
@query.is_public = false unless User.current.allowed_to?(:manage_public_queries, @project) || User.current.admin?
build_query_from_params
end
def create
@query = Query.new(params[:query])
@query.user = User.current
@query.project = params[:query_is_for_all] ? nil : @project
@query.user = User.current
@query.is_public = false unless User.current.allowed_to?(:manage_public_queries, @project) || User.current.admin?
build_query_from_params
@query.add_filters(params[:fields] || params[:f], params[:operators] || params[:op], params[:values] || params[:v]) if params[:fields] || params[:f]
@query.group_by ||= params[:group_by]
@query.column_names = params[:c] if params[:c]
@query.column_names = nil if params[:default_columns]
if @query.save
if request.post? && params[:confirm] && @query.save
flash[:notice] = l(:notice_successful_create)
redirect_to :controller => 'issues', :action => 'index', :project_id => @project, :query_id => @query
else
render :action => 'new', :layout => !request.xhr?
return
end
render :layout => false if request.xhr?
end
def edit
end
def update
@query.attributes = params[:query]
@query.project = nil if params[:query_is_for_all]
@query.is_public = false unless User.current.allowed_to?(:manage_public_queries, @project) || User.current.admin?
build_query_from_params
@query.column_names = nil if params[:default_columns]
if @query.save
flash[:notice] = l(:notice_successful_update)
redirect_to :controller => 'issues', :action => 'index', :project_id => @project, :query_id => @query
else
render :action => 'edit'
if request.post?
@query.filters = {}
@query.add_filters(params[:fields] || params[:f], params[:operators] || params[:op], params[:values] || params[:v]) if params[:fields] || params[:f]
@query.attributes = params[:query]
@query.project = nil if params[:query_is_for_all]
@query.is_public = false unless User.current.allowed_to?(:manage_public_queries, @project) || User.current.admin?
@query.group_by ||= params[:group_by]
@query.column_names = params[:c] if params[:c]
@query.column_names = nil if params[:default_columns]
if @query.save
flash[:notice] = l(:notice_successful_update)
redirect_to :controller => 'issues', :action => 'index', :project_id => @project, :query_id => @query
end
end
end
def destroy
@query.destroy
@query.destroy if request.post?
redirect_to :controller => 'issues', :action => 'index', :project_id => @project, :set_filter => 1
end
private
def find_query
@query = Query.find(params[:id])
@@ -97,7 +70,7 @@ private
rescue ActiveRecord::RecordNotFound
render_404
end
def find_optional_project
@project = Project.find(params[:project_id]) if params[:project_id]
render_403 unless User.current.allowed_to?(:save_queries, @project, :global => true)

View File

@@ -1,16 +1,16 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
@@ -24,8 +24,8 @@ class ReportsController < ApplicationController
@versions = @project.shared_versions.sort
@priorities = IssuePriority.all
@categories = @project.issue_categories
@assignees = (Setting.issue_group_assignment? ? @project.principals : @project.users).sort
@authors = @project.users.sort
@assignees = @project.members.collect { |m| m.user }.sort
@authors = @project.members.collect { |m| m.user }.sort
@subprojects = @project.descendants.visible
@issues_by_tracker = Issue.by_tracker(@project)
@@ -37,7 +37,7 @@ class ReportsController < ApplicationController
@issues_by_subproject = Issue.by_subproject(@project) || []
render :template => "reports/issue_report"
end
end
def issue_report_details
case params[:detail]
@@ -63,12 +63,12 @@ class ReportsController < ApplicationController
@report_title = l(:field_category)
when "assigned_to"
@field = "assigned_to_id"
@rows = (Setting.issue_group_assignment? ? @project.principals : @project.users).sort
@rows = @project.members.collect { |m| m.user }.sort
@data = Issue.by_assigned_to(@project)
@report_title = l(:field_assigned_to)
when "author"
@field = "author_id"
@rows = @project.users.sort
@rows = @project.members.collect { |m| m.user }.sort
@data = Issue.by_author(@project)
@report_title = l(:field_author)
when "subproject"

View File

@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -24,46 +24,44 @@ class InvalidRevisionParam < Exception; end
class RepositoriesController < ApplicationController
menu_item :repository
menu_item :settings, :only => [:new, :create, :edit, :update, :destroy, :committers]
menu_item :settings, :only => :edit
default_search_scope :changesets
before_filter :find_project_by_project_id, :only => [:new, :create]
before_filter :find_repository, :only => [:edit, :update, :destroy, :committers]
before_filter :find_project_repository, :except => [:new, :create, :edit, :update, :destroy, :committers]
before_filter :find_changeset, :only => [:revision, :add_related_issue, :remove_related_issue]
before_filter :find_repository, :except => :edit
before_filter :find_project, :only => :edit
before_filter :authorize
accept_rss_auth :revisions
rescue_from Redmine::Scm::Adapters::CommandFailed, :with => :show_error_command_failed
def new
scm = params[:repository_scm] || (Redmine::Scm::Base.all & Setting.enabled_scm).first
@repository = Repository.factory(scm)
@repository.is_default = @project.repository.nil?
@repository.project = @project
render :layout => !request.xhr?
end
def create
@repository = Repository.factory(params[:repository_scm], params[:repository])
@repository.project = @project
if request.post? && @repository.save
redirect_to settings_project_path(@project, :tab => 'repositories')
else
render :action => 'new'
end
end
def edit
end
def update
@repository.attributes = params[:repository]
@repository.project = @project
if request.put? && @repository.save
redirect_to settings_project_path(@project, :tab => 'repositories')
else
render :action => 'edit'
@repository = @project.repository
if !@repository && !params[:repository_scm].blank?
@repository = Repository.factory(params[:repository_scm])
@repository.project = @project if @repository
end
if request.post? && @repository
p1 = params[:repository]
p = {}
p_extra = {}
p1.each do |k, v|
if k =~ /^extra_/
p_extra[k] = v
else
p[k] = v
end
end
@repository.attributes = p
@repository.merge_extra_info(p_extra)
@repository.save
end
render(:update) do |page|
page.replace_html "tab-content-repository",
:partial => 'projects/settings/repository'
if @repository && !@project.repository
@project.reload # needed to reload association
page.replace_html "main-menu", render_main_menu(@project)
end
end
end
@@ -78,13 +76,16 @@ class RepositoriesController < ApplicationController
# Build a hash with repository usernames as keys and corresponding user ids as values
@repository.committer_ids = params[:committers].values.inject({}) {|h, c| h[c.first] = c.last; h}
flash[:notice] = l(:notice_successful_update)
redirect_to settings_project_path(@project, :tab => 'repositories')
redirect_to :action => 'committers', :id => @project
end
end
def destroy
@repository.destroy if request.delete?
redirect_to settings_project_path(@project, :tab => 'repositories')
@repository.destroy
redirect_to :controller => 'projects',
:action => 'settings',
:id => @project,
:tab => 'repository'
end
def show
@@ -98,7 +99,6 @@ class RepositoriesController < ApplicationController
(show_error_not_found; return) unless @entries
@changesets = @repository.latest_changesets(@path, @rev)
@properties = @repository.properties(@path, @rev)
@repositories = @project.repositories
render :action => 'show'
end
end
@@ -121,7 +121,7 @@ class RepositoriesController < ApplicationController
@changesets = @repository.changesets.find(:all,
:limit => @changeset_pages.items_per_page,
:offset => @changeset_pages.current.offset,
:include => [:user, :repository, :parents])
:include => [:user, :repository])
respond_to do |format|
format.html { render :layout => false if request.xhr? }
@@ -172,70 +172,21 @@ class RepositoriesController < ApplicationController
(show_error_not_found; return) unless @entry
@annotate = @repository.scm.annotate(@path, @rev)
if @annotate.nil? || @annotate.empty?
(render_error l(:error_scm_annotate); return)
end
ann_buf_size = 0
@annotate.lines.each do |buf|
ann_buf_size += buf.size
end
if ann_buf_size > Setting.file_max_size_displayed.to_i.kilobyte
(render_error l(:error_scm_annotate_big_text_file); return)
end
(render_error l(:error_scm_annotate); return) if @annotate.nil? || @annotate.empty?
@changeset = @repository.find_changeset_by_name(@rev)
end
def revision
raise ChangesetNotFound if @rev.blank?
@changeset = @repository.find_changeset_by_name(@rev)
raise ChangesetNotFound unless @changeset
respond_to do |format|
format.html
format.js {render :layout => false}
end
end
# Adds a related issue to a changeset
# POST /projects/:project_id/repository/(:repository_id/)revisions/:rev/issues
def add_related_issue
@issue = @changeset.find_referenced_issue_by_id(params[:issue_id])
if @issue && (!@issue.visible? || @changeset.issues.include?(@issue))
@issue = nil
end
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
# Removes a related issue from a changeset
# DELETE /projects/:project_id/repository/(:repository_id/)revisions/:rev/issues/:issue_id
def remove_related_issue
@issue = Issue.visible.find_by_id(params[:issue_id])
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
rescue ChangesetNotFound
show_error_not_found
end
def diff
@@ -256,7 +207,7 @@ class RepositoriesController < ApplicationController
User.current.pref[:diff_type] = @diff_type
User.current.preference.save
end
@cache_key = "repositories/diff/#{@repository.id}/" +
@cache_key = "repositories/diff/#{@repository.id}/" +
Digest::MD5.hexdigest("#{@path}-#{@rev}-#{@rev_to}-#{@diff_type}-#{current_language}")
unless read_fragment(@cache_key)
@diff = @repository.diff(@path, @rev, @rev_to)
@@ -290,26 +241,15 @@ class RepositoriesController < ApplicationController
private
def find_repository
@repository = Repository.find(params[:id])
@project = @repository.project
rescue ActiveRecord::RecordNotFound
render_404
end
REV_PARAM_RE = %r{\A[a-f0-9]*\Z}i
def find_project_repository
def find_repository
@project = Project.find(params[:id])
if params[:repository_id].present?
@repository = @project.repositories.find_by_identifier_param(params[:repository_id])
else
@repository = @project.repository
end
@repository = @project.repository
(render_404; return false) unless @repository
@path = params[:path].join('/') unless params[:path].nil?
@path ||= ''
@rev = params[:rev].blank? ? @repository.default_branch : params[:rev].to_s.strip
@rev = params[:rev].blank? ? @repository.default_branch : params[:rev].strip
@rev_to = params[:rev_to]
unless @rev.to_s.match(REV_PARAM_RE) && @rev_to.to_s.match(REV_PARAM_RE)
@@ -323,13 +263,6 @@ class RepositoriesController < ApplicationController
show_error_not_found
end
def find_changeset
if @rev.present?
@changeset = @repository.find_changeset_by_name(@rev)
end
show_error_not_found unless @changeset
end
def show_error_not_found
render_error :message => l(:error_scm_not_found), :status => 404
end
@@ -424,3 +357,18 @@ class RepositoriesController < ApplicationController
end
end
class Date
def months_ago(date = Date.today)
(date.year - self.year)*12 + (date.month - self.month)
end
def weeks_ago(date = Date.today)
(date.year - self.year)*52 + (date.cweek - self.cweek)
end
end
class String
def with_leading_slash
starts_with?('/') ? self : "/#{self}"
end
end

View File

@@ -1,48 +1,36 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class RolesController < ApplicationController
layout 'admin'
before_filter :require_admin
before_filter :require_admin, :except => :index
before_filter :require_admin_or_api_request, :only => :index
before_filter :find_role, :only => [:edit, :update, :destroy]
accept_api_auth :index
verify :method => :post, :only => [ :destroy, :move ],
:redirect_to => { :action => :index }
def index
respond_to do |format|
format.html {
@role_pages, @roles = paginate :roles, :per_page => 25, :order => 'builtin, position'
render :action => "index", :layout => false if request.xhr?
}
format.api {
@roles = Role.givable.all
}
end
@role_pages, @roles = paginate :roles, :per_page => 25, :order => 'builtin, position'
render :action => "index", :layout => false if request.xhr?
end
def new
# Prefills the form with 'Non member' role permissions
@role = Role.new(params[:role] || {:permissions => Role.non_member.permissions})
@roles = Role.sorted.all
end
def create
@role = Role.new(params[:role])
if request.post? && @role.save
# workflow copy
if !params[:copy_workflow_from].blank? && (copy_from = Role.find_by_id(params[:copy_workflow_from]))
@@ -51,33 +39,32 @@ class RolesController < ApplicationController
flash[:notice] = l(:notice_successful_create)
redirect_to :action => 'index'
else
@roles = Role.sorted.all
render :action => 'new'
@permissions = @role.setable_permissions
@roles = Role.find :all, :order => 'builtin, position'
end
end
def edit
end
def update
if request.put? and @role.update_attributes(params[:role])
@role = Role.find(params[:id])
if request.post? and @role.update_attributes(params[:role])
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'index'
else
render :action => 'edit'
@permissions = @role.setable_permissions
end
end
def destroy
@role = Role.find(params[:id])
@role.destroy
redirect_to :action => 'index'
rescue
flash[:error] = l(:error_can_not_remove_role)
redirect_to :action => 'index'
end
def permissions
@roles = Role.sorted.all
def report
@roles = Role.find(:all, :order => 'builtin, position')
@permissions = Redmine::AccessControl.permissions.select { |p| !p.public? }
if request.post?
@roles.each do |role|
@@ -88,12 +75,4 @@ class RolesController < ApplicationController
redirect_to :action => 'index'
end
end
private
def find_role
@role = Role.find(params[:id])
rescue ActiveRecord::RecordNotFound
render_404
end
end

View File

@@ -5,12 +5,12 @@
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
@@ -26,7 +26,7 @@ class SearchController < ApplicationController
@question.strip!
@all_words = params[:all_words] ? params[:all_words].present? : true
@titles_only = params[:titles_only] ? params[:titles_only].present? : false
projects_to_search =
case params[:scope]
when 'all'
@@ -34,20 +34,20 @@ class SearchController < ApplicationController
when 'my_projects'
User.current.memberships.collect(&:project)
when 'subprojects'
@project ? (@project.self_and_descendants.active.all) : nil
@project ? (@project.self_and_descendants.active) : nil
else
@project
end
offset = nil
begin; offset = params[:offset].to_time if params[:offset]; rescue; end
# quick jump to an issue
if @question.match(/^#?(\d+)$/) && Issue.visible.find_by_id($1.to_i)
redirect_to :controller => "issues", :action => "show", :id => $1
return
end
@object_types = Redmine::Search.available_search_types.dup
if projects_to_search.is_a? Project
# don't search projects
@@ -55,23 +55,23 @@ class SearchController < ApplicationController
# only show what the user is allowed to view
@object_types = @object_types.select {|o| User.current.allowed_to?("view_#{o}".to_sym, projects_to_search)}
end
@scope = @object_types.select {|t| params[t]}
@scope = @object_types if @scope.empty?
# extract tokens from the question
# eg. hello "bye bye" => ["hello", "bye bye"]
@tokens = @question.scan(%r{((\s|^)"[\s\w]+"(\s|$)|\S+)}).collect {|m| m.first.gsub(%r{(^\s*"\s*|\s*"\s*$)}, '')}
# tokens must be at least 2 characters long
@tokens = @tokens.uniq.select {|w| w.length > 1 }
if !@tokens.empty?
# no more than 5 tokens to search for
@tokens.slice! 5..-1 if @tokens.size > 5
@tokens.slice! 5..-1 if @tokens.size > 5
@results = []
@results_by_type = Hash.new {|h,k| h[k] = 0}
limit = 10
@scope.each do |s|
r, c = s.singularize.camelcase.constantize.search(@tokens, projects_to_search,
@@ -87,13 +87,13 @@ class SearchController < ApplicationController
if params[:previous].nil?
@pagination_previous_date = @results[0].event_datetime if offset && @results[0]
if @results.size > limit
@pagination_next_date = @results[limit-1].event_datetime
@pagination_next_date = @results[limit-1].event_datetime
@results = @results[0, limit]
end
else
@pagination_next_date = @results[-1].event_datetime if offset && @results[-1]
if @results.size > limit
@pagination_previous_date = @results[-(limit)].event_datetime
@pagination_previous_date = @results[-(limit)].event_datetime
@results = @results[-(limit), limit]
end
end
@@ -103,7 +103,7 @@ class SearchController < ApplicationController
render :layout => false if request.xhr?
end
private
private
def find_optional_project
return true unless params[:id]
@project = Project.find(params[:id])

View File

@@ -17,7 +17,6 @@
class SettingsController < ApplicationController
layout 'admin'
menu_item :plugins, :only => :plugin
before_filter :require_admin
@@ -52,12 +51,12 @@ class SettingsController < ApplicationController
def plugin
@plugin = Redmine::Plugin.find(params[:id])
if request.post?
Setting.send "plugin_#{@plugin.id}=", params[:settings]
Setting["plugin_#{@plugin.id}"] = params[:settings]
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'plugin', :id => @plugin.id
else
@partial = @plugin.settings[:partial]
@settings = Setting.send "plugin_#{@plugin.id}"
@settings = Setting["plugin_#{@plugin.id}"]
end
rescue Redmine::PluginNotFound
render_404

View File

@@ -1,70 +1,53 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# Copyright (C) 2006-2009 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class SysController < ActionController::Base
before_filter :check_enabled
def projects
p = Project.active.has_module(:repository).find(
:all,
:include => :repository,
:order => "#{Project.table_name}.identifier"
)
# extra_info attribute from repository breaks activeresource client
render :xml => p.to_xml(
:only => [:id, :identifier, :name, :is_public, :status],
:include => {:repository => {:only => [:id, :url]}}
)
p = Project.active.has_module(:repository).find(:all, :include => :repository, :order => 'identifier')
render :xml => p.to_xml(:include => :repository)
end
def create_project_repository
project = Project.find(params[:id])
if project.repository
render :nothing => true, :status => 409
else
logger.info "Repository for #{project.name} was reported to be created by #{request.remote_ip}."
repository = Repository.factory(params[:vendor], params[:repository])
repository.project = project
if repository.save
render :xml => {repository.class.name.underscore.gsub('/', '-') => {:id => repository.id, :url => repository.url}}, :status => 201
project.repository = Repository.factory(params[:vendor], params[:repository])
if project.repository && project.repository.save
render :xml => project.repository, :status => 201
else
render :nothing => true, :status => 422
end
end
end
def fetch_changesets
projects = []
scope = Project.active.has_module(:repository)
if params[:id]
project = nil
if params[:id].to_s =~ /^\d*$/
project = scope.find(params[:id])
else
project = scope.find_by_identifier(params[:id])
end
raise ActiveRecord::RecordNotFound unless project
projects << project
projects << Project.active.has_module(:repository).find(params[:id])
else
projects = scope.all
projects = Project.active.has_module(:repository).find(:all, :include => :repository)
end
projects.each do |project|
project.repositories.each do |repository|
repository.fetch_changesets
if project.repository
project.repository.fetch_changesets
end
end
render :nothing => true, :status => 200

View File

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

View File

@@ -5,110 +5,96 @@
# 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 TimelogController < ApplicationController
menu_item :issues
before_filter :find_project_for_new_time_entry, :only => [:create]
before_filter :find_project, :only => [:new, :create]
before_filter :find_time_entry, :only => [:show, :edit, :update]
before_filter :find_time_entries, :only => [:bulk_edit, :bulk_update, :destroy]
before_filter :authorize, :except => [:new, :index, :report]
before_filter :find_optional_project, :only => [:index, :report]
before_filter :find_optional_project_for_new_time_entry, :only => [:new]
before_filter :authorize_global, :only => [:new, :index, :report]
before_filter :authorize, :except => [:index]
before_filter :find_optional_project, :only => [:index]
accept_rss_auth :index
accept_api_auth :index, :show, :create, :update, :destroy
helper :sort
include SortHelper
helper :issues
include TimelogHelper
helper :custom_fields
include CustomFieldsHelper
def index
sort_init 'spent_on', 'desc'
sort_update 'spent_on' => ['spent_on', "#{TimeEntry.table_name}.created_on"],
sort_update 'spent_on' => 'spent_on',
'user' => 'user_id',
'activity' => 'activity_id',
'project' => "#{Project.table_name}.name",
'issue' => 'issue_id',
'hours' => 'hours'
retrieve_date_range
scope = TimeEntry.visible.spent_between(@from, @to)
cond = ARCondition.new
if @issue
scope = scope.on_issue(@issue)
cond << "#{Issue.table_name}.root_id = #{@issue.root_id} AND #{Issue.table_name}.lft >= #{@issue.lft} AND #{Issue.table_name}.rgt <= #{@issue.rgt}"
elsif @project
scope = scope.on_project(@project, Setting.display_subprojects_issues?)
cond << @project.project_condition(Setting.display_subprojects_issues?)
end
retrieve_date_range
cond << ['spent_on BETWEEN ? AND ?', @from, @to]
respond_to do |format|
format.html {
# Paginate results
@entry_count = scope.count
@entry_count = TimeEntry.visible.count(:include => [:project, :issue], :conditions => cond.conditions)
@entry_pages = Paginator.new self, @entry_count, per_page_option, params['page']
@entries = scope.all(
:include => [:project, :activity, :user, {:issue => :tracker}],
:order => sort_clause,
:limit => @entry_pages.items_per_page,
:offset => @entry_pages.current.offset
)
@total_hours = scope.sum(:hours).to_f
@entries = TimeEntry.visible.find(:all,
:include => [:project, :activity, :user, {:issue => :tracker}],
:conditions => cond.conditions,
:order => sort_clause,
:limit => @entry_pages.items_per_page,
:offset => @entry_pages.current.offset)
@total_hours = TimeEntry.visible.sum(:hours, :include => [:project, :issue], :conditions => cond.conditions).to_f
render :layout => !request.xhr?
}
format.api {
@entry_count = scope.count
@entry_count = TimeEntry.visible.count(:include => [:project, :issue], :conditions => cond.conditions)
@offset, @limit = api_offset_and_limit
@entries = scope.all(
:include => [:project, :activity, :user, {:issue => :tracker}],
:order => sort_clause,
:limit => @limit,
:offset => @offset
)
@entries = TimeEntry.visible.find(:all,
:include => [:project, :activity, :user, {:issue => :tracker}],
:conditions => cond.conditions,
:order => sort_clause,
:limit => @limit,
:offset => @offset)
}
format.atom {
entries = scope.all(
:include => [:project, :activity, :user, {:issue => :tracker}],
:order => "#{TimeEntry.table_name}.created_on DESC",
:limit => Setting.feeds_limit.to_i
)
entries = TimeEntry.visible.find(:all,
:include => [:project, :activity, :user, {:issue => :tracker}],
:conditions => cond.conditions,
:order => "#{TimeEntry.table_name}.created_on DESC",
:limit => Setting.feeds_limit.to_i)
render_feed(entries, :title => l(:label_spent_time))
}
format.csv {
# Export all entries
@entries = scope.all(
:include => [:project, :activity, :user, {:issue => [:tracker, :assigned_to, :priority]}],
:order => sort_clause
)
@entries = TimeEntry.visible.find(:all,
:include => [:project, :activity, :user, {:issue => [:tracker, :assigned_to, :priority]}],
:conditions => cond.conditions,
:order => sort_clause)
send_data(entries_to_csv(@entries), :type => 'text/csv; header=present', :filename => 'timelog.csv')
}
end
end
def report
retrieve_date_range
@report = Redmine::Helpers::TimeReport.new(@project, @issue, params[:criteria], params[:columns], @from, @to)
respond_to do |format|
format.html { render :layout => !request.xhr? }
format.csv { send_data(report_to_csv(@report), :type => 'text/csv; header=present', :filename => 'timelog.csv') }
end
end
def show
respond_to do |format|
# TODO: Implement html response
@@ -119,52 +105,47 @@ class TimelogController < ApplicationController
def new
@time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => User.current.today)
@time_entry.safe_attributes = params[:time_entry]
@time_entry.attributes = params[:time_entry]
call_hook(:controller_timelog_edit_before_save, { :params => params, :time_entry => @time_entry })
render :action => 'edit'
end
verify :method => :post, :only => :create, :render => {:nothing => true, :status => :method_not_allowed }
def create
@time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => User.current.today)
@time_entry.safe_attributes = params[:time_entry]
@time_entry.attributes = params[:time_entry]
call_hook(:controller_timelog_edit_before_save, { :params => params, :time_entry => @time_entry })
if @time_entry.save
respond_to do |format|
format.html {
flash[:notice] = l(:notice_successful_create)
if params[:continue]
if params[:project_id]
redirect_to :action => 'new', :project_id => @time_entry.project, :issue_id => @time_entry.issue,
:time_entry => {:issue_id => @time_entry.issue_id, :activity_id => @time_entry.activity_id},
:back_url => params[:back_url]
else
redirect_to :action => 'new',
:time_entry => {:project_id => @time_entry.project_id, :issue_id => @time_entry.issue_id, :activity_id => @time_entry.activity_id},
:back_url => params[:back_url]
end
else
redirect_back_or_default :action => 'index', :project_id => @time_entry.project
end
flash[:notice] = l(:notice_successful_update)
redirect_back_or_default :action => 'index', :project_id => @time_entry.project
}
format.api { render :action => 'show', :status => :created, :location => time_entry_url(@time_entry) }
end
else
respond_to do |format|
format.html { render :action => 'new' }
format.html { render :action => 'edit' }
format.api { render_validation_errors(@time_entry) }
end
end
end
end
def edit
@time_entry.safe_attributes = params[:time_entry]
@time_entry.attributes = params[:time_entry]
call_hook(:controller_timelog_edit_before_save, { :params => params, :time_entry => @time_entry })
end
verify :method => :put, :only => :update, :render => {:nothing => true, :status => :method_not_allowed }
def update
@time_entry.safe_attributes = params[:time_entry]
@time_entry.attributes = params[:time_entry]
call_hook(:controller_timelog_edit_before_save, { :params => params, :time_entry => @time_entry })
if @time_entry.save
respond_to do |format|
format.html {
@@ -178,7 +159,7 @@ class TimelogController < ApplicationController
format.html { render :action => 'edit' }
format.api { render_validation_errors(@time_entry) }
end
end
end
end
def bulk_edit
@@ -192,7 +173,7 @@ class TimelogController < ApplicationController
unsaved_time_entry_ids = []
@time_entries.each do |time_entry|
time_entry.reload
time_entry.safe_attributes = attributes
time_entry.attributes = attributes
call_hook(:controller_time_entries_bulk_edit_before_save, { :params => params, :time_entry => time_entry })
unless time_entry.save
# Keep unsaved time_entry ids to display them in flash error
@@ -203,31 +184,32 @@ class TimelogController < ApplicationController
redirect_back_or_default({:controller => 'timelog', :action => 'index', :project_id => @projects.first})
end
verify :method => :delete, :only => :destroy, :render => {:nothing => true, :status => :method_not_allowed }
def destroy
destroyed = TimeEntry.transaction do
@time_entries.each do |t|
@time_entries.each do |t|
begin
unless t.destroy && t.destroyed?
raise ActiveRecord::Rollback
respond_to do |format|
format.html {
flash[:error] = l(:notice_unable_delete_time_entry)
redirect_to :back
}
format.api { render_validation_errors(t) }
end
return
end
rescue ::ActionController::RedirectBackError
redirect_to :action => 'index', :project_id => @projects.first
return
end
end
respond_to do |format|
format.html {
if destroyed
flash[:notice] = l(:notice_successful_delete)
else
flash[:error] = l(:notice_unable_delete_time_entry)
end
flash[:notice] = l(:notice_successful_delete)
redirect_back_or_default(:action => 'index', :project_id => @projects.first)
}
format.api {
if destroyed
head :ok
else
render_validation_errors(@time_entries)
end
}
format.api { head :ok }
end
end
@@ -263,25 +245,20 @@ private
end
end
def find_optional_project_for_new_time_entry
if (project_id = (params[:project_id] || params[:time_entry] && params[:time_entry][:project_id])).present?
@project = Project.find(project_id)
end
def find_project
if (issue_id = (params[:issue_id] || params[:time_entry] && params[:time_entry][:issue_id])).present?
@issue = Issue.find(issue_id)
@project ||= @issue.project
@project = @issue.project
elsif (project_id = (params[:project_id] || params[:time_entry] && params[:time_entry][:project_id])).present?
@project = Project.find(project_id)
else
render_404
return false
end
rescue ActiveRecord::RecordNotFound
render_404
end
def find_project_for_new_time_entry
find_optional_project_for_new_time_entry
if @project.nil?
render_404
end
end
def find_optional_project
if !params[:issue_id].blank?
@issue = Issue.find(params[:issue_id])
@@ -289,8 +266,9 @@ private
elsif !params[:project_id].blank?
@project = Project.find(params[:project_id])
end
deny_access unless User.current.allowed_to?(:view_time_entries, @project, :global => true)
end
# Retrieves the date range based on predefined ranges or specific from/to param dates
def retrieve_date_range
@free_period = false
@@ -331,8 +309,10 @@ private
else
# default
end
@from, @to = @to, @from if @from && @to && @from > @to
@from ||= (TimeEntry.earilest_date_for_project(@project) || Date.today)
@to ||= (TimeEntry.latest_date_for_project(@project) || Date.today)
end
def parse_params_for_bulk_time_entry_attributes(params)

View File

@@ -1,46 +1,33 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# Copyright (C) 2006-2009 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class TrackersController < ApplicationController
layout 'admin'
before_filter :require_admin
before_filter :require_admin, :except => :index
before_filter :require_admin_or_api_request, :only => :index
accept_api_auth :index
verify :method => :post, :only => :destroy, :redirect_to => { :action => :index }
def index
respond_to do |format|
format.html {
@tracker_pages, @trackers = paginate :trackers, :per_page => 10, :order => 'position'
render :action => "index", :layout => false if request.xhr?
}
format.api {
@trackers = Tracker.all
}
end
@tracker_pages, @trackers = paginate :trackers, :per_page => 10, :order => 'position'
render :action => "index", :layout => false if request.xhr?
end
def new
@tracker ||= Tracker.new(params[:tracker])
@trackers = Tracker.find :all, :order => 'position'
@projects = Project.find(:all)
end
def create
@tracker = Tracker.new(params[:tracker])
if request.post? and @tracker.save
# workflow copy
@@ -51,26 +38,20 @@ class TrackersController < ApplicationController
redirect_to :action => 'index'
return
end
new
render :action => 'new'
@trackers = Tracker.find :all, :order => 'position'
@projects = Project.find(:all)
end
def edit
@tracker ||= Tracker.find(params[:id])
@projects = Project.find(:all)
end
def update
@tracker = Tracker.find(params[:id])
if request.put? and @tracker.update_attributes(params[:tracker])
if request.post? and @tracker.update_attributes(params[:tracker])
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'index'
return
end
edit
render :action => 'edit'
@projects = Project.find(:all)
end
def destroy
@tracker = Tracker.find(params[:id])
unless @tracker.issues.empty?
@@ -79,5 +60,5 @@ class TrackersController < ApplicationController
@tracker.destroy
end
redirect_to :action => 'index'
end
end
end

View File

@@ -5,19 +5,19 @@
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class UsersController < ApplicationController
layout 'admin'
before_filter :require_admin, :except => :show
before_filter :find_user, :only => [:show, :edit, :update, :destroy, :edit_membership, :destroy_membership]
accept_api_auth :index, :show, :create, :update, :destroy
@@ -25,30 +25,36 @@ class UsersController < ApplicationController
helper :sort
include SortHelper
helper :custom_fields
include CustomFieldsHelper
include CustomFieldsHelper
def index
sort_init 'login', 'asc'
sort_update %w(login firstname lastname mail admin created_on last_login_on)
case params[:format]
when 'xml', 'json'
@offset, @limit = api_offset_and_limit
else
@limit = per_page_option
end
scope = User
scope = scope.in_group(params[:group_id].to_i) if params[:group_id].present?
@status = params[:status] ? params[:status].to_i : 1
c = ARCondition.new(@status == 0 ? "status <> 0" : ["status = ?", @status])
@status = params[:status] || 1
scope = User.logged.status(@status)
scope = scope.like(params[:name]) if params[:name].present?
scope = scope.in_group(params[:group_id]) if params[:group_id].present?
@user_count = scope.count
unless params[:name].blank?
name = "%#{params[:name].strip.downcase}%"
c << ["LOWER(login) LIKE ? OR LOWER(firstname) LIKE ? OR LOWER(lastname) LIKE ? OR LOWER(mail) LIKE ?", name, name, name, name]
end
@user_count = scope.count(:conditions => c.conditions)
@user_pages = Paginator.new self, @user_count, @limit, params['page']
@offset ||= @user_pages.current.offset
@users = scope.find :all,
:order => sort_clause,
:conditions => c.conditions,
:limit => @limit,
:offset => @offset
@@ -60,21 +66,21 @@ class UsersController < ApplicationController
format.api
end
end
def show
# show projects based on current user visibility
@memberships = @user.memberships.all(:conditions => Project.visible_condition(User.current))
events = Redmine::Activity::Fetcher.new(User.current, :author => @user).events(nil, nil, :limit => 10)
@events_by_day = events.group_by(&:event_date)
unless User.current.admin?
if !@user.active? || (@user != User.current && @memberships.empty? && events.empty?)
render_404
return
end
end
respond_to do |format|
format.html { render :layout => 'base' }
format.api
@@ -85,7 +91,8 @@ class UsersController < ApplicationController
@user = User.new(:language => Setting.default_language, :mail_notification => Setting.default_notification_option)
@auth_sources = AuthSource.find(:all)
end
verify :method => :post, :only => :create, :render => {:nothing => true, :status => :method_not_allowed }
def create
@user = User.new(:language => Setting.default_language, :mail_notification => Setting.default_notification_option)
@user.safe_attributes = params[:user]
@@ -93,19 +100,21 @@ class UsersController < ApplicationController
@user.login = params[:user][:login]
@user.password, @user.password_confirmation = params[:user][:password], params[:user][:password_confirmation] unless @user.auth_source_id
# TODO: Similar to My#account
@user.pref.attributes = params[:pref]
@user.pref[:no_self_notified] = (params[:no_self_notified] == '1')
if @user.save
@user.pref.attributes = params[:pref]
@user.pref[:no_self_notified] = (params[:no_self_notified] == '1')
@user.pref.save
@user.notified_project_ids = (@user.mail_notification == 'selected' ? params[:notified_project_ids] : [])
Mailer.deliver_account_information(@user, params[:user][:password]) if params[:send_information]
respond_to do |format|
format.html {
flash[:notice] = l(:notice_successful_create)
redirect_to(params[:continue] ?
{:controller => 'users', :action => 'new'} :
redirect_to(params[:continue] ?
{:controller => 'users', :action => 'new'} :
{:controller => 'users', :action => 'edit', :id => @user}
)
}
@@ -127,7 +136,8 @@ class UsersController < ApplicationController
@auth_sources = AuthSource.find(:all)
@membership ||= Member.new
end
verify :method => :put, :only => :update, :render => {:nothing => true, :status => :method_not_allowed }
def update
@user.admin = params[:user][:admin] if params[:user][:admin]
@user.login = params[:user][:login] if params[:user][:login]
@@ -150,7 +160,7 @@ class UsersController < ApplicationController
elsif @user.active? && params[:send_information] && !params[:user][:password].blank? && @user.auth_source_id.nil?
Mailer.deliver_account_information(@user, params[:user][:password])
end
respond_to do |format|
format.html {
flash[:notice] = l(:notice_successful_update)
@@ -173,19 +183,18 @@ class UsersController < ApplicationController
redirect_to :controller => 'users', :action => 'edit', :id => @user
end
verify :method => :delete, :only => :destroy, :render => {:nothing => true, :status => :method_not_allowed }
def destroy
@user.destroy
respond_to do |format|
format.html { redirect_to :back }
format.html { redirect_to(users_url) }
format.api { head :ok }
end
rescue ::ActionController::RedirectBackError
redirect_to(users_url)
end
def edit_membership
@membership = Member.edit_membership(params[:membership_id], params[:membership], @user)
@membership.save
@membership.save if request.post?
respond_to do |format|
if @membership.valid?
format.html { redirect_to :controller => 'users', :action => 'edit', :id => @user, :tab => 'memberships' }
@@ -204,10 +213,10 @@ class UsersController < ApplicationController
end
end
end
def destroy_membership
@membership = Member.find(params[:membership_id])
if @membership.deletable?
if request.post? && @membership.deletable?
@membership.destroy
end
respond_to do |format|
@@ -215,9 +224,9 @@ class UsersController < ApplicationController
format.js { render(:update) {|page| page.replace_html "tab-content-memberships", :partial => 'users/memberships'} }
end
end
private
def find_user
if params[:id] == 'current'
require_login || return

View File

@@ -1,16 +1,16 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
@@ -23,77 +23,55 @@ class VersionsController < ApplicationController
before_filter :find_project, :only => [:index, :new, :create, :close_completed]
before_filter :authorize
accept_api_auth :index, :show, :create, :update, :destroy
helper :custom_fields
helper :projects
def index
respond_to do |format|
format.html {
@trackers = @project.trackers.find(:all, :order => 'position')
retrieve_selected_tracker_ids(@trackers, @trackers.select {|t| t.is_in_roadmap?})
@with_subprojects = params[:with_subprojects].nil? ? Setting.display_subprojects_issues? : (params[:with_subprojects] == '1')
project_ids = @with_subprojects ? @project.self_and_descendants.collect(&:id) : [@project.id]
@versions = @project.shared_versions || []
@versions += @project.rolled_up_versions.visible if @with_subprojects
@versions = @versions.uniq.sort
unless params[:completed]
@completed_versions = @versions.select {|version| version.closed? || version.completed? }
@versions -= @completed_versions
end
@issues_by_version = {}
if @selected_tracker_ids.any? && @versions.any?
issues = Issue.visible.all(
:include => [:project, :status, :tracker, :priority, :fixed_version],
:conditions => {:tracker_id => @selected_tracker_ids, :project_id => project_ids, :fixed_version_id => @versions.map(&:id)},
:order => "#{Project.table_name}.lft, #{Tracker.table_name}.position, #{Issue.table_name}.id"
)
@issues_by_version = issues.group_by(&:fixed_version)
end
@versions.reject! {|version| !project_ids.include?(version.project_id) && @issues_by_version[version].blank?}
}
format.api {
@versions = @project.shared_versions.all
}
end
end
def show
respond_to do |format|
format.html {
@issues = @version.fixed_issues.visible.find(:all,
:include => [:status, :tracker, :priority],
:order => "#{Tracker.table_name}.position, #{Issue.table_name}.id")
}
format.api
end
end
def new
@version = @project.versions.build
@version.safe_attributes = params[:version]
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
@trackers = @project.trackers.find(:all, :order => 'position')
retrieve_selected_tracker_ids(@trackers, @trackers.select {|t| t.is_in_roadmap?})
@with_subprojects = params[:with_subprojects].nil? ? Setting.display_subprojects_issues? : (params[:with_subprojects] == '1')
project_ids = @with_subprojects ? @project.self_and_descendants.collect(&:id) : [@project.id]
@versions = @project.shared_versions || []
@versions += @project.rolled_up_versions.visible if @with_subprojects
@versions = @versions.uniq.sort
@versions.reject! {|version| version.closed? || version.completed? } unless params[:completed]
@issues_by_version = {}
unless @selected_tracker_ids.empty?
@versions.each do |version|
issues = version.fixed_issues.visible.find(:all,
:include => [:project, :status, :tracker, :priority],
:conditions => {:tracker_id => @selected_tracker_ids, :project_id => project_ids},
:order => "#{Project.table_name}.lft, #{Tracker.table_name}.position, #{Issue.table_name}.id")
@issues_by_version[version] = issues
end
end
@versions.reject! {|version| !project_ids.include?(version.project_id) && @issues_by_version[version].blank?}
end
def create
def show
@issues = @version.fixed_issues.visible.find(:all,
:include => [:status, :tracker, :priority],
:order => "#{Tracker.table_name}.position, #{Issue.table_name}.id")
end
def new
@version = @project.versions.build
if params[:version]
attributes = params[:version].dup
attributes.delete('sharing') unless attributes.nil? || @version.allowed_sharings.include?(attributes['sharing'])
@version.safe_attributes = attributes
@version.attributes = attributes
end
end
def create
# TODO: refactor with code above in #new
@version = @project.versions.build
if params[:version]
attributes = params[:version].dup
attributes.delete('sharing') unless attributes.nil? || @version.allowed_sharings.include?(attributes['sharing'])
@version.attributes = attributes
end
if request.post?
@@ -101,30 +79,21 @@ class VersionsController < ApplicationController
respond_to do |format|
format.html do
flash[:notice] = l(:notice_successful_create)
redirect_back_or_default :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
redirect_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
end
format.js do
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]')
# IE doesn't support the replace_html rjs method for select box options
render(:update) {|page| page.replace "issue_fixed_version_id",
content_tag('select', '<option></option>' + version_options_for_select(@project.shared_versions.open, @version), :id => 'issue_fixed_version_id', :name => 'issue[fixed_version_id]')
}
end
format.api do
render :action => 'show', :status => :created, :location => version_url(@version)
end
end
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
render(:update) {|page| page.alert(@version.errors.full_messages.join('\n')) }
end
format.api { render_validation_errors(@version) }
end
end
end
@@ -132,29 +101,22 @@ class VersionsController < ApplicationController
def edit
end
def update
if request.put? && params[:version]
attributes = params[:version].dup
attributes.delete('sharing') unless @version.allowed_sharings.include?(attributes['sharing'])
@version.safe_attributes = attributes
if @version.save
respond_to do |format|
format.html {
flash[:notice] = l(:notice_successful_update)
redirect_back_or_default :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
}
format.api { head :ok }
end
if @version.update_attributes(attributes)
flash[:notice] = l(:notice_successful_update)
redirect_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
else
respond_to do |format|
format.html { render :action => 'edit' }
format.api { render_validation_errors(@version) }
end
end
end
end
def close_completed
if request.put?
@project.close_completed_versions
@@ -165,21 +127,13 @@ class VersionsController < ApplicationController
def destroy
if @version.fixed_issues.empty?
@version.destroy
respond_to do |format|
format.html { redirect_back_or_default :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project }
format.api { head :ok }
end
redirect_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
else
respond_to do |format|
format.html {
flash[:error] = l(:notice_unable_delete_version)
redirect_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
}
format.api { head :unprocessable_entity }
end
flash[:error] = l(:notice_unable_delete_version)
redirect_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
end
end
def status_by
respond_to do |format|
format.html { render :action => 'show' }

View File

@@ -5,12 +5,12 @@
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
@@ -19,7 +19,11 @@ class WatchersController < ApplicationController
before_filter :find_project
before_filter :require_login, :check_project_privacy, :only => [:watch, :unwatch]
before_filter :authorize, :only => [:new, :destroy]
verify :method => :post,
:only => [ :watch, :unwatch ],
:render => { :nothing => true, :status => :method_not_allowed }
def watch
if @watched.respond_to?(:visible?) && !@watched.visible?(User.current)
render_403
@@ -27,35 +31,19 @@ class WatchersController < ApplicationController
set_watcher(User.current, true)
end
end
def unwatch
set_watcher(User.current, false)
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
if params[:watcher].is_a?(Hash) && request.post?
user_ids = params[:watcher][:user_ids] || [params[:watcher][:user_id]]
user_ids.each do |user_id|
Watcher.create(:watchable => @watched, :user_id => user_id)
end
end
@watcher = Watcher.new(params[:watcher])
@watcher.watchable = @watched
@watcher.save if request.post?
respond_to do |format|
format.html { redirect_to :back }
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
@@ -63,26 +51,7 @@ class WatchersController < ApplicationController
rescue ::ActionController::RedirectBackError
render :text => 'Watcher added.', :layout => true
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.select("#issue_watcher_user_ids_#{user.id}").each do |item|
page.remove item
end
end
page.insert_html :bottom, 'watchers_inputs', :text => watchers_checkboxes(nil, users, true)
end
end
end
end
end
def destroy
@watched.set_watcher(User.find(params[:user_id]), false) if request.post?
respond_to do |format|
@@ -94,29 +63,17 @@ class WatchersController < ApplicationController
end
end
end
def autocomplete_for_user
@users = User.active.like(params[:q]).find(:all, :limit => 100)
if @watched
@users -= @watched.watcher_users
end
render :layout => false
end
private
def find_project
if params[:object_type] && params[:object_id]
klass = Object.const_get(params[:object_type].camelcase)
return false unless klass.respond_to?('watched_by')
@watched = klass.find(params[:object_id])
@project = @watched.project
elsif params[:project_id]
@project = Project.visible.find(params[:project_id])
end
klass = Object.const_get(params[:object_type].camelcase)
return false unless klass.respond_to?('watched_by')
@watched = klass.find(params[:object_id])
@project = @watched.project
rescue
render_404
end
def set_watcher(user, watching)
@watched.set_watcher(user, watching)
respond_to do |format|

View File

@@ -1,16 +1,16 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
@@ -22,7 +22,7 @@ class WelcomeController < ApplicationController
@news = News.latest User.current
@projects = Project.latest User.current
end
def robots
@projects = Project.all_public.active
render :layout => false, :content_type => 'text/plain'

View File

@@ -40,7 +40,6 @@ class WikiController < ApplicationController
helper :attachments
include AttachmentsHelper
helper :watchers
include Redmine::Export::PDF
# List of pages, sorted alphabetically and by parent (hierarchy)
def index
@@ -72,10 +71,7 @@ class WikiController < ApplicationController
end
@content = @page.content_for_version(params[:version])
if User.current.allowed_to?(:export_wiki_pages, @project)
if params[:format] == 'pdf'
send_data(wiki_page_to_pdf(@page, @project), :type => 'application/pdf', :filename => "#{@page.title}.pdf")
return
elsif params[:format] == 'html'
if params[:format] == 'html'
export = render_to_string :action => 'export', :layout => false
send_data(export, :type => 'text/html', :filename => "#{@page.title}.html")
return
@@ -85,22 +81,13 @@ class WikiController < ApplicationController
end
end
@editable = editable?
@sections_editable = @editable && User.current.allowed_to?(:edit_wiki_pages, @page.project) &&
@content.current_version? &&
Redmine::WikiFormatting.supports_section_edit?
render :action => 'show'
end
# edit an existing page or a new one
def edit
return render_403 unless editable?
if @page.new_record?
@page.content = WikiContent.new(:page => @page)
if params[:parent].present?
@page.parent = @page.wiki.find_page(params[:parent].to_s)
end
end
@page.content = WikiContent.new(:page => @page) if @page.new_record?
@content = @page.content_for_version(params[:version])
@content.text = initial_page_content(@page) if @content.text.blank?
@@ -109,20 +96,13 @@ class WikiController < ApplicationController
# To prevent StaleObjectError exception when reverting to a previous version
@content.version = @page.content.version
@text = @content.text
if params[:section].present? && Redmine::WikiFormatting.supports_section_edit?
@section = params[:section].to_i
@text, @section_hash = Redmine::WikiFormatting.formatter.new(@text).get_section(@section)
render_404 if @text.blank?
end
end
verify :method => :put, :only => :update, :render => {:nothing => true, :status => :method_not_allowed }
# Creates a new page or updates an existing one
def update
return render_403 unless editable?
@page.content = WikiContent.new(:page => @page) if @page.new_record?
@page.safe_attributes = params[:wiki_page]
@content = @page.content_for_version(params[:version])
@content.text = initial_page_content(@page) if @content.text.blank?
@@ -132,25 +112,14 @@ class WikiController < ApplicationController
if !@page.new_record? && params[:content].present? && @content.text == params[:content][:text]
attachments = Attachment.attach_files(@page, params[:attachments])
render_attachment_warning_if_needed(@page)
# don't save content if text wasn't changed
@page.save
# don't save if text wasn't changed
redirect_to :action => 'show', :project_id => @project, :id => @page.title
return
end
@content.comments = params[:content][:comments]
@text = params[:content][:text]
if params[:section].present? && Redmine::WikiFormatting.supports_section_edit?
@section = params[:section].to_i
@section_hash = params[:section_hash]
@content.text = Redmine::WikiFormatting.formatter.new(@content.text).update_section(params[:section].to_i, @text, @section_hash)
else
@content.version = params[:content][:version]
@content.text = @text
end
@content.attributes = params[:content]
@content.author = User.current
@page.content = @content
if @page.save
# if page is new @page.save will also save content, but not if page isn't a new record
if (@page.new_record? ? @page.save : @content.save)
attachments = Attachment.attach_files(@page, params[:attachments])
render_attachment_warning_if_needed(@page)
call_hook(:controller_wiki_edit_after_save, { :params => params, :page => @page})
@@ -159,7 +128,7 @@ class WikiController < ApplicationController
render :action => 'edit'
end
rescue ActiveRecord::StaleObjectError, Redmine::WikiFormatting::StaleSectionError
rescue ActiveRecord::StaleObjectError
# Optimistic locking exception
flash.now[:error] = l(:notice_locking_conflict)
render :action => 'edit'
@@ -177,6 +146,7 @@ class WikiController < ApplicationController
end
end
verify :method => :post, :only => :protect, :redirect_to => { :action => :show }
def protect
@page.update_attribute :protected, params[:protected]
redirect_to :action => 'show', :project_id => @project, :id => @page.title
@@ -206,6 +176,7 @@ class WikiController < ApplicationController
render_404 unless @annotate
end
verify :method => :delete, :only => [:destroy], :redirect_to => { :action => :show }
# Removes a wiki page and its history
# Children can be either set as root pages, removed or reassigned to another parent page
def destroy
@@ -235,17 +206,14 @@ class WikiController < ApplicationController
redirect_to :action => 'index', :project_id => @project
end
# Export wiki to a single pdf or html file
# Export wiki to a single html file
def export
@pages = @wiki.pages.all(:order => 'title', :include => [:content, :attachments], :limit => 75)
respond_to do |format|
format.html {
export = render_to_string :action => 'export_multiple', :layout => false
send_data(export, :type => 'text/html', :filename => "wiki.html")
}
format.pdf {
send_data(wiki_pages_to_pdf(@pages, @project), :type => 'application/pdf', :filename => "#{@project.identifier}.pdf")
}
if User.current.allowed_to?(:export_wiki_pages, @project)
@pages = @wiki.pages.find :all, :order => 'title'
export = render_to_string :action => 'export_multiple', :layout => false
send_data(export, :type => 'text/html', :filename => "wiki.html")
else
redirect_to :action => 'show', :project_id => @project, :id => nil
end
end

View File

@@ -1,16 +1,16 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
@@ -18,11 +18,11 @@
class WikisController < ApplicationController
menu_item :settings
before_filter :find_project, :authorize
# Create or update a project's wiki
def edit
@wiki = @project.wiki || Wiki.new(:project => @project)
@wiki.safe_attributes = params[:wiki]
@wiki.attributes = params[:wiki]
@wiki.save if request.post?
render(:update) {|page| page.replace_html "tab-content-wiki", :partial => 'projects/settings/wiki'}
end
@@ -32,6 +32,6 @@ class WikisController < ApplicationController
if request.post? && params[:confirm] && @project.wiki
@project.wiki.destroy
redirect_to :controller => 'projects', :action => 'settings', :id => @project, :tab => 'wiki'
end
end
end
end

View File

@@ -1,42 +1,42 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# Copyright (C) 2006-2008 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class WorkflowsController < ApplicationController
layout 'admin'
before_filter :require_admin
before_filter :find_roles
before_filter :find_trackers
def index
@workflow_counts = Workflow.count_by_tracker_and_role
end
def edit
@role = Role.find_by_id(params[:role_id])
@tracker = Tracker.find_by_id(params[:tracker_id])
@tracker = Tracker.find_by_id(params[:tracker_id])
if request.post?
Workflow.destroy_all( ["role_id=? and tracker_id=?", @role.id, @tracker.id])
(params[:issue_status] || []).each { |status_id, transitions|
transitions.each { |new_status_id, options|
author = options.is_a?(Array) && options.include?('author') && !options.include?('always')
assignee = options.is_a?(Array) && options.include?('assignee') && !options.include?('always')
@role.workflows.build(:tracker_id => @tracker.id, :old_status_id => status_id, :new_status_id => new_status_id, :author => author, :assignee => assignee)
@role.workflows.build(:tracker_id => @tracker.id, :old_status_id => status_id, :new_status_id => new_status_id, :author => author, :assignee => assignee)
}
}
if @role.save
@@ -45,13 +45,13 @@ class WorkflowsController < ApplicationController
return
end
end
@used_statuses_only = (params[:used_statuses_only] == '0' ? false : true)
if @tracker && @used_statuses_only && @tracker.issue_statuses.any?
@statuses = @tracker.issue_statuses
end
@statuses ||= IssueStatus.find(:all, :order => 'position')
if @tracker && @role && @statuses.any?
workflows = Workflow.all(:conditions => {:role_id => @role.id, :tracker_id => @tracker.id})
@workflows = {}
@@ -60,9 +60,9 @@ class WorkflowsController < ApplicationController
@workflows['assignee'] = workflows.select {|w| w.assignee}
end
end
def copy
if params[:source_tracker_id].blank? || params[:source_tracker_id] == 'any'
@source_tracker = nil
else
@@ -73,10 +73,10 @@ class WorkflowsController < ApplicationController
else
@source_role = Role.find_by_id(params[:source_role_id].to_i)
end
@target_trackers = params[:target_tracker_ids].blank? ? nil : Tracker.find_all_by_id(params[:target_tracker_ids])
@target_roles = params[:target_role_ids].blank? ? nil : Role.find_all_by_id(params[:target_role_ids])
if request.post?
if params[:source_tracker_id].blank? || params[:source_role_id].blank? || (@source_tracker.nil? && @source_role.nil?)
flash.now[:error] = l(:error_workflow_copy_source)
@@ -95,7 +95,7 @@ class WorkflowsController < ApplicationController
def find_roles
@roles = Role.find(:all, :order => 'builtin, position')
end
def find_trackers
@trackers = Tracker.find(:all, :order => 'position')
end

View File

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

View File

@@ -1,25 +1,23 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
module AdminHelper
def project_status_options_for_select(selected)
options_for_select([[l(:label_all), ''],
[l(:status_active), '1']], selected.to_s)
options_for_select([[l(:label_all), ''],
[l(:status_active), 1]], selected)
end
end

View File

@@ -1,5 +1,3 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
#
@@ -82,11 +80,11 @@ module ApplicationHelper
subject = truncate(subject, :length => options[:truncate])
end
end
s = link_to "#{h(issue.tracker)} ##{issue.id}", {:controller => "issues", :action => "show", :id => issue},
s = link_to "#{issue.tracker} ##{issue.id}", {:controller => "issues", :action => "show", :id => issue},
:class => issue.css_classes,
:title => title
s << h(": #{subject}") if subject
s = h("#{issue.project} - ") + s if options[:project]
s << ": #{h subject}" if subject
s = "#{h issue.project} - " + s if options[:project]
s
end
@@ -97,29 +95,19 @@ module ApplicationHelper
def link_to_attachment(attachment, options={})
text = options.delete(:text) || attachment.filename
action = options.delete(:download) ? 'download' : 'show'
opt_only_path = {}
opt_only_path[:only_path] = (options[:only_path] == false ? false : true)
options.delete(:only_path)
link_to(h(text),
{:controller => 'attachments', :action => action,
:id => attachment, :filename => attachment.filename}.merge(opt_only_path),
options)
link_to(h(text), {:controller => 'attachments', :action => action, :id => attachment, :filename => attachment.filename }, options)
end
# Generates a link to a SCM revision
# Options:
# * :text - Link text (default to the formatted revision)
def link_to_revision(revision, repository, options={})
if repository.is_a?(Project)
repository = repository.repository
end
def link_to_revision(revision, project, options={})
text = options.delete(:text) || format_revision(revision)
rev = revision.respond_to?(:identifier) ? revision.identifier : revision
link_to(
h(text),
{:controller => 'repositories', :action => 'revision', :id => repository.project, :repository_id => repository.identifier_param, :rev => rev},
:title => l(:label_revision_id, format_revision(revision))
)
link_to(text, {:controller => 'repositories', :action => 'revision', :id => project, :rev => rev},
:title => l(:label_revision_id, format_revision(revision)))
end
# Generates a link to a message
@@ -182,8 +170,7 @@ module ApplicationHelper
end
def format_activity_description(text)
h(truncate(text.to_s, :length => 120).gsub(%r{[\r\n]*<(pre|code)>.*$}m, '...')
).gsub(/[\r\n]+/, "<br />").html_safe
h(truncate(text.to_s, :length => 120).gsub(%r{[\r\n]*<(pre|code)>.*$}m, '...')).gsub(/[\r\n]+/, "<br />")
end
def format_version_name(version)
@@ -213,16 +200,16 @@ module ApplicationHelper
end
content << "</ul>\n"
end
content.html_safe
content
end
# Renders flash messages
def render_flash_messages
s = ''
flash.each do |k,v|
s << (content_tag('div', v.html_safe, :class => "flash #{k}"))
s << content_tag('div', v, :class => "flash #{k}")
end
s.html_safe
s
end
# Renders tabs and their content
@@ -246,14 +233,14 @@ module ApplicationHelper
{ :value => url_for(:controller => 'projects', :action => 'show', :id => p, :jump => current_menu_item) }
end
s << '</select>'
s.html_safe
s
end
end
def project_tree_options_for_select(projects, options = {})
s = ''
project_tree(projects) do |project, level|
name_prefix = (level > 0 ? ('&nbsp;' * 2 * level + '&#187; ').html_safe : '')
name_prefix = (level > 0 ? ('&nbsp;' * 2 * level + '&#187; ') : '')
tag_options = {:value => project.id}
if project == options[:selected] || (options[:selected].respond_to?(:include?) && options[:selected].include?(project))
tag_options[:selected] = 'selected'
@@ -263,7 +250,7 @@ module ApplicationHelper
tag_options.merge!(yield(project)) if block_given?
s << content_tag('option', name_prefix + h(project), tag_options)
end
s.html_safe
s
end
# Yields the given block for each project with its level in the tree
@@ -294,7 +281,7 @@ module ApplicationHelper
end
s << ("</li></ul>\n" * ancestors.size)
end
s.html_safe
s
end
def principals_check_box_tags(name, principals)
@@ -302,23 +289,6 @@ module ApplicationHelper
principals.sort.each do |principal|
s << "<label>#{ check_box_tag name, principal.id, false } #{h principal}</label>\n"
end
s.html_safe
end
# Returns a string for users/groups option tags
def principals_options_for_select(collection, selected=nil)
s = ''
if collection.include?(User.current)
s << content_tag('option', "<< #{l(:label_me)} >>".html_safe, :value => User.current.id)
end
groups = ''
collection.sort.each do |element|
selected_attribute = ' selected="selected"' if option_value_selected?(element, selected)
(element.is_a?(Group) ? groups : s) << %(<option value="#{element.id}"#{selected_attribute}>#{h element.name}</option>)
end
unless groups.empty?
s << %(<optgroup label="#{h(l(:label_group_plural))}">#{groups}</optgroup>)
end
s
end
@@ -337,16 +307,12 @@ module ApplicationHelper
end
end
def anchor(text)
text.to_s.gsub(' ', '_')
end
def html_hours(text)
text.gsub(%r{(\d+)\.(\d+)}, '<span class="hours hours-int">\1</span><span class="hours hours-dec">.\2</span>').html_safe
text.gsub(%r{(\d+)\.(\d+)}, '<span class="hours hours-int">\1</span><span class="hours hours-dec">.\2</span>')
end
def authoring(created, author, options={})
l(options[:label] || :label_added_time_by, :author => link_to_user(author), :age => time_tag(created)).html_safe
l(options[:label] || :label_added_time_by, :author => link_to_user(author), :age => time_tag(created))
end
def time_tag(time)
@@ -358,12 +324,6 @@ module ApplicationHelper
end
end
def syntax_highlight_lines(name, content)
lines = []
syntax_highlight(name, content).each_line { |line| lines << line }
lines
end
def syntax_highlight(name, content)
Redmine::SyntaxHighlighting.highlight_by_filename(content, name)
end
@@ -379,10 +339,7 @@ module ApplicationHelper
html = ''
if paginator.current.previous
# \xc2\xab(utf-8) = &#171;
html << link_to_content_update(
"\xc2\xab " + l(:label_previous),
url_param.merge(page_param => paginator.current.previous)) + ' '
html << link_to_content_update('&#171; ' + l(:label_previous), url_param.merge(page_param => paginator.current.previous)) + ' '
end
html << (pagination_links_each(paginator, options) do |n|
@@ -390,10 +347,7 @@ module ApplicationHelper
end || '')
if paginator.current.next
# \xc2\xbb(utf-8) = &#187;
html << ' ' + link_to_content_update(
(l(:label_next) + " \xc2\xbb"),
url_param.merge(page_param => paginator.current.next))
html << ' ' + link_to_content_update((l(:label_next) + ' &#187;'), url_param.merge(page_param => paginator.current.next))
end
unless count.nil?
@@ -403,7 +357,7 @@ module ApplicationHelper
end
end
html.html_safe
html
end
def per_page_links(selected=nil)
@@ -413,30 +367,22 @@ module ApplicationHelper
links.size > 1 ? l(:label_display_per_page, links.join(', ')) : nil
end
def reorder_links(name, url, method = :post)
link_to(image_tag('2uparrow.png', :alt => l(:label_sort_highest)),
url.merge({"#{name}[move_to]" => 'highest'}),
:method => method, :title => l(:label_sort_highest)) +
link_to(image_tag('1uparrow.png', :alt => l(:label_sort_higher)),
url.merge({"#{name}[move_to]" => 'higher'}),
:method => method, :title => l(:label_sort_higher)) +
link_to(image_tag('1downarrow.png', :alt => l(:label_sort_lower)),
url.merge({"#{name}[move_to]" => 'lower'}),
:method => method, :title => l(:label_sort_lower)) +
link_to(image_tag('2downarrow.png', :alt => l(:label_sort_lowest)),
url.merge({"#{name}[move_to]" => 'lowest'}),
:method => method, :title => l(:label_sort_lowest))
def reorder_links(name, url)
link_to(image_tag('2uparrow.png', :alt => l(:label_sort_highest)), url.merge({"#{name}[move_to]" => 'highest'}), :method => :post, :title => l(:label_sort_highest)) +
link_to(image_tag('1uparrow.png', :alt => l(:label_sort_higher)), url.merge({"#{name}[move_to]" => 'higher'}), :method => :post, :title => l(:label_sort_higher)) +
link_to(image_tag('1downarrow.png', :alt => l(:label_sort_lower)), url.merge({"#{name}[move_to]" => 'lower'}), :method => :post, :title => l(:label_sort_lower)) +
link_to(image_tag('2downarrow.png', :alt => l(:label_sort_lowest)), url.merge({"#{name}[move_to]" => 'lowest'}), :method => :post, :title => l(:label_sort_lowest))
end
def breadcrumb(*args)
elements = args.flatten
elements.any? ? content_tag('p', (args.join(" \xc2\xbb ") + " \xc2\xbb ").html_safe, :class => 'breadcrumb') : nil
elements.any? ? content_tag('p', args.join(' &#187; ') + ' &#187; ', :class => 'breadcrumb') : nil
end
def other_formats_links(&block)
concat('<p class="other-formats">'.html_safe + l(:label_export_to))
concat('<p class="other-formats">' + l(:label_export_to))
yield Redmine::Views::OtherFormatsBuilder.new(self)
concat('</p>'.html_safe)
concat('</p>')
end
def page_header_title
@@ -449,21 +395,22 @@ module ApplicationHelper
root = ancestors.shift
b << link_to_project(root, {:jump => current_menu_item}, :class => 'root')
if ancestors.size > 2
b << "\xe2\x80\xa6"
b << '&#8230;'
ancestors = ancestors[-2, 2]
end
b += ancestors.collect {|p| link_to_project(p, {:jump => current_menu_item}, :class => 'ancestor') }
end
b << h(@project)
b.join(" \xc2\xbb ").html_safe
b.join(' &#187; ')
end
end
def html_title(*args)
if args.empty?
title = @html_title || []
title = []
title << @project.name if @project
title << Setting.app_title unless Setting.app_title == title.last
title += @html_title if @html_title
title << Setting.app_title
title.select {|t| !t.blank? }.join(' - ')
else
@html_title ||= []
@@ -479,8 +426,8 @@ module ApplicationHelper
css << 'theme-' + theme.name
end
css << 'controller-' + controller_name
css << 'action-' + action_name
css << 'controller-' + params[:controller]
css << 'action-' + params[:action]
css.join(' ')
end
@@ -509,25 +456,20 @@ module ApplicationHelper
project = options[:project] || @project || (obj && obj.respond_to?(:project) ? obj.project : nil)
only_path = options.delete(:only_path) == false ? false : true
text = Redmine::WikiFormatting.to_html(Setting.text_formatting, text, :object => obj, :attribute => attr)
text = Redmine::WikiFormatting.to_html(Setting.text_formatting, text, :object => obj, :attribute => attr) { |macro, args| exec_macro(macro, obj, args) }
@parsed_headings = []
@heading_anchors = {}
@current_section = 0 if options[:edit_section_links]
parse_sections(text, project, obj, attr, only_path, options)
text = parse_non_pre_blocks(text) do |text|
[:parse_inline_attachments, :parse_wiki_links, :parse_redmine_links, :parse_macros].each do |method_name|
[:parse_inline_attachments, :parse_wiki_links, :parse_redmine_links, :parse_headings].each do |method_name|
send method_name, text, project, obj, attr, only_path, options
end
end
parse_headings(text, project, obj, attr, only_path, options)
if @parsed_headings.any?
replace_toc(text, @parsed_headings)
end
text.html_safe
text
end
def parse_non_pre_blocks(text)
@@ -562,13 +504,13 @@ module ApplicationHelper
def parse_inline_attachments(text, project, obj, attr, only_path, options)
# when using an image link, try to use an attachment, if possible
if options[:attachments] || (obj && obj.respond_to?(:attachments))
attachments = options[:attachments] || obj.attachments
text.gsub!(/src="([^\/"]+\.(bmp|gif|jpg|jpe|jpeg|png))"(\s+alt="([^"]*)")?/i) do |m|
attachments = nil
text.gsub!(/src="([^\/"]+\.(bmp|gif|jpg|jpeg|png))"(\s+alt="([^"]*)")?/i) do |m|
filename, ext, alt, alttext = $1.downcase, $2, $3, $4
attachments ||= (options[:attachments] || obj.attachments).sort_by(&:created_on).reverse
# search for the picture in attachments
if found = Attachment.latest_attach(attachments, filename)
image_url = url_for :only_path => only_path, :controller => 'attachments',
:action => 'download', :id => found
if found = attachments.detect { |att| att.filename.downcase == filename }
image_url = url_for :only_path => only_path, :controller => 'attachments', :action => 'download', :id => found
desc = found.description.to_s.gsub('"', '')
if !desc.blank? && alttext.blank?
alt = " title=\"#{desc}\" alt=\"#{desc}\""
@@ -616,15 +558,13 @@ module ApplicationHelper
else
case options[:wiki_links]
when :local; "#{page.present? ? Wiki.titleize(page) : ''}.html" + (anchor.present? ? "##{anchor}" : '')
when :anchor; "##{page.present? ? Wiki.titleize(page) : title}" + (anchor.present? ? "_#{anchor}" : '') # used for single-file wiki export
when :anchor; "##{title}" # used for single-file wiki export
else
wiki_page_id = page.present? ? Wiki.titleize(page) : nil
parent = wiki_page.nil? && obj.is_a?(WikiContent) && obj.page && project == link_project ? obj.page.title : nil
url_for(:only_path => only_path, :controller => 'wiki', :action => 'show', :project_id => link_project,
:id => wiki_page_id, :anchor => anchor, :parent => parent)
url_for(:only_path => only_path, :controller => 'wiki', :action => 'show', :project_id => link_project, :id => wiki_page_id, :anchor => anchor)
end
end
link_to(title.present? ? title.html_safe : h(page), url, :class => ('wiki-page' + (wiki_page ? '' : ' new')))
link_to((title || page), url, :class => ('wiki-page' + (wiki_page ? '' : ' new')))
else
# project or wiki doesn't exist
all
@@ -668,35 +608,26 @@ module ApplicationHelper
# identifier:version:1.0.0
# identifier:source:some/file
def parse_redmine_links(text, project, obj, attr, only_path, options)
text.gsub!(%r{([\s\(,\-\[\>]|^)(!)?(([a-z0-9\-_]+):)?(attachment|document|version|forum|news|message|project|commit|source|export)?(((#)|((([a-z0-9\-]+)\|)?(r)))((\d+)((#note)?-(\d+))?)|(:)([^"\s<>][^\s<>]*?|"[^"]+?"))(?=(?=[[:punct:]][^A-Za-z0-9_/])|,|\s|\]|<|$)}) do |m|
leading, esc, project_prefix, project_identifier, prefix, repo_prefix, repo_identifier, sep, identifier, comment_suffix, comment_id = $1, $2, $3, $4, $5, $10, $11, $8 || $12 || $18, $14 || $19, $15, $17
text.gsub!(%r{([\s\(,\-\[\>]|^)(!)?(([a-z0-9\-]+):)?(attachment|document|version|commit|source|export|message|project)?((#|r)(\d+)|(:)([^"\s<>][^\s<>]*?|"[^"]+?"))(?=(?=[[:punct:]]\W)|,|\s|\]|<|$)}) do |m|
leading, esc, project_prefix, project_identifier, prefix, sep, identifier = $1, $2, $3, $4, $5, $7 || $9, $8 || $10
link = nil
if project_identifier
project = Project.visible.find_by_identifier(project_identifier)
end
if esc.nil?
if prefix.nil? && sep == 'r'
if project
repository = nil
if repo_identifier
repository = project.repositories.detect {|repo| repo.identifier == repo_identifier}
else
repository = project.repository
end
# project.changesets.visible raises an SQL error because of a double join on repositories
if repository && (changeset = Changeset.visible.find_by_repository_id_and_revision(repository.id, identifier))
link = link_to(h("#{project_prefix}#{repo_prefix}r#{identifier}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :repository_id => repository.identifier_param, :rev => changeset.revision},
:class => 'changeset',
:title => truncate_single_line(changeset.comments, :length => 100))
end
# project.changesets.visible raises an SQL error because of a double join on repositories
if project && project.repository && (changeset = Changeset.visible.find_by_repository_id_and_revision(project.repository.id, identifier))
link = link_to("#{project_prefix}r#{identifier}", {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => changeset.revision},
:class => 'changeset',
:title => truncate_single_line(changeset.comments, :length => 100))
end
elsif sep == '#'
oid = identifier.to_i
case prefix
when nil
if 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},
link = link_to("##{oid}", {:only_path => only_path, :controller => 'issues', :action => 'show', :id => oid},
:class => issue.css_classes,
:title => "#{truncate(issue.subject, :length => 100)} (#{issue.status.name})")
end
@@ -714,16 +645,6 @@ module ApplicationHelper
if message = Message.visible.find_by_id(oid, :include => :parent)
link = link_to_message(message, {:only_path => only_path}, :class => 'message')
end
when 'forum'
if board = Board.visible.find_by_id(oid)
link = link_to h(board.name), {:only_path => only_path, :controller => 'boards', :action => 'show', :id => board, :project_id => board.project},
:class => 'board'
end
when 'news'
if news = News.visible.find_by_id(oid)
link = link_to h(news.title), {:only_path => only_path, :controller => 'news', :action => 'show', :id => news},
:class => 'news'
end
when 'project'
if p = Project.visible.find_by_id(oid)
link = link_to_project(p, {:only_path => only_path}, :class => 'project')
@@ -743,44 +664,22 @@ module ApplicationHelper
link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
:class => 'version'
end
when 'forum'
if project && board = project.boards.visible.find_by_name(name)
link = link_to h(board.name), {:only_path => only_path, :controller => 'boards', :action => 'show', :id => board, :project_id => board.project},
:class => 'board'
when 'commit'
if project && project.repository && (changeset = Changeset.visible.find(:first, :conditions => ["repository_id = ? AND scmid LIKE ?", project.repository.id, "#{name}%"]))
link = link_to h("#{project_prefix}#{name}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => changeset.identifier},
:class => 'changeset',
:title => truncate_single_line(changeset.comments, :length => 100)
end
when 'news'
if project && news = project.news.visible.find_by_title(name)
link = link_to h(news.title), {:only_path => only_path, :controller => 'news', :action => 'show', :id => news},
:class => 'news'
end
when 'commit', 'source', 'export'
if project
repository = nil
if name =~ %r{^(([a-z0-9\-]+)\|)(.+)$}
repo_prefix, repo_identifier, name = $1, $2, $3
repository = project.repositories.detect {|repo| repo.identifier == repo_identifier}
else
repository = project.repository
end
if prefix == 'commit'
if repository && (changeset = Changeset.visible.find(:first, :conditions => ["repository_id = ? AND scmid LIKE ?", repository.id, "#{name}%"]))
link = link_to h("#{project_prefix}#{repo_prefix}#{name}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :repository_id => repository.identifier_param, :rev => changeset.identifier},
:class => 'changeset',
:title => truncate_single_line(h(changeset.comments), :length => 100)
end
else
if repository && User.current.allowed_to?(:browse_repository, project)
name =~ %r{^[/\\]*(.*?)(@([0-9a-f]+))?(#(L\d+))?$}
path, rev, anchor = $1, $3, $5
link = link_to h("#{project_prefix}#{prefix}:#{repo_prefix}#{name}"), {:controller => 'repositories', :action => 'entry', :id => project, :repository_id => repository.identifier_param,
:path => to_path_param(path),
:rev => rev,
:anchor => anchor,
:format => (prefix == 'export' ? 'raw' : nil)},
:class => (prefix == 'export' ? 'source download' : 'source')
end
end
repo_prefix = nil
when 'source', 'export'
if project && project.repository && User.current.allowed_to?(:browse_repository, project)
name =~ %r{^[/\\]*(.*?)(@([0-9a-f]+))?(#(L\d+))?$}
path, rev, anchor = $1, $3, $5
link = link_to h("#{project_prefix}#{prefix}:#{name}"), {:controller => 'repositories', :action => 'entry', :id => project,
:path => to_path_param(path),
:rev => rev,
:anchor => anchor,
:format => (prefix == 'export' ? 'raw' : nil)},
:class => (prefix == 'export' ? 'source download' : 'source')
end
when 'attachment'
attachments = options[:attachments] || (obj && obj.respond_to?(:attachments) ? obj.attachments : nil)
@@ -795,27 +694,11 @@ module ApplicationHelper
end
end
end
(leading + (link || "#{project_prefix}#{prefix}#{repo_prefix}#{sep}#{identifier}#{comment_suffix}"))
leading + (link || "#{project_prefix}#{prefix}#{sep}#{identifier}")
end
end
HEADING_RE = /(<h(\d)( [^>]+)?>(.+?)<\/h(\d)>)/i unless const_defined?(:HEADING_RE)
def parse_sections(text, project, obj, attr, only_path, options)
return unless options[:edit_section_links]
text.gsub!(HEADING_RE) do
heading = $1
@current_section += 1
if @current_section > 1
content_tag('div',
link_to(image_tag('edit.png'), options[:edit_section_links].merge(:section => @current_section)),
:class => 'contextual',
:title => l(:button_edit_section)) + heading.html_safe
else
heading
end
end
end
HEADING_RE = /<h(1|2|3|4)( [^>]+)?>(.+?)<\/h(1|2|3|4)>/i unless const_defined?(:HEADING_RE)
# Headings and TOC
# Adds ids and links to headings unless options[:headings] is set to false
@@ -823,55 +706,19 @@ module ApplicationHelper
return if options[:headings] == false
text.gsub!(HEADING_RE) do
level, attrs, content = $2.to_i, $3, $4
level, attrs, content = $1.to_i, $2, $3
item = strip_tags(content).strip
anchor = sanitize_anchor_name(item)
# used for single-file wiki export
anchor = "#{obj.page.title}_#{anchor}" if options[:wiki_links] == :anchor && (obj.is_a?(WikiContent) || obj.is_a?(WikiContent::Version))
@heading_anchors[anchor] ||= 0
idx = (@heading_anchors[anchor] += 1)
if idx > 1
anchor = "#{anchor}-#{idx}"
end
@parsed_headings << [level, anchor, item]
"<a name=\"#{anchor}\"></a>\n<h#{level} #{attrs}>#{content}<a href=\"##{anchor}\" class=\"wiki-anchor\">&para;</a></h#{level}>"
end
end
MACROS_RE = /
(!)? # escaping
(
\{\{ # opening tag
([\w]+) # macro name
(\(([^\}]*)\))? # optional arguments
\}\} # closing tag
)
/x unless const_defined?(:MACROS_RE)
# Macros substitution
def parse_macros(text, project, obj, attr, only_path, options)
text.gsub!(MACROS_RE) do
esc, all, macro = $1, $2, $3.downcase
args = ($5 || '').split(',').each(&:strip)
if esc.nil?
begin
exec_macro(macro, obj, args)
rescue => e
"<div class=\"flash error\">Error executing the <strong>#{macro}</strong> macro (#{e})</div>"
end || all
else
all
end
end
end
TOC_RE = /<p>\{\{([<>]?)toc\}\}<\/p>/i unless const_defined?(:TOC_RE)
# Renders the TOC with given headings
def replace_toc(text, headings)
text.gsub!(TOC_RE) do
# Keep only the 4 first levels
headings = headings.select{|level, anchor, item| level <= 4}
if headings.empty?
''
else
@@ -905,8 +752,7 @@ module ApplicationHelper
text.to_s.
gsub(/\r\n?/, "\n"). # \r\n and \r -> \n
gsub(/\n\n+/, "<br /><br />"). # 2+ newline -> 2 br
gsub(/([^\n]\n)(?=[^\n])/, '\1<br />'). # 1 newline -> br
html_safe
gsub(/([^\n]\n)(?=[^\n])/, '\1<br />') # 1 newline -> br
end
def lang_options_for_select(blank=true)
@@ -919,60 +765,21 @@ 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
def labelled_tabular_form_for(name, object, options, &proc)
options[:html] ||= {}
options[:html][:class] = 'tabular' unless options[:html].has_key?(:class)
options.merge!({:builder => Redmine::Views::LabelledFormBuilder})
form_for(*args, &proc)
form_for(name, object, options.merge({ :builder => TabularFormBuilder, :lang => current_language}), &proc)
end
def labelled_form_for(*args, &proc)
args << {} unless args.last.is_a?(Hash)
options = args.last
options.merge!({:builder => Redmine::Views::LabelledFormBuilder})
form_for(*args, &proc)
end
def labelled_fields_for(*args, &proc)
args << {} unless args.last.is_a?(Hash)
options = args.last
options.merge!({:builder => Redmine::Views::LabelledFormBuilder})
fields_for(*args, &proc)
end
def labelled_remote_form_for(*args, &proc)
args << {} unless args.last.is_a?(Hash)
options = args.last
options.merge!({:builder => Redmine::Views::LabelledFormBuilder})
remote_form_for(*args, &proc)
end
def error_messages_for(*objects)
html = ""
objects = objects.map {|o| o.is_a?(String) ? instance_variable_get("@#{o}") : o}.compact
errors = objects.map {|o| o.errors.full_messages}.flatten
if errors.any?
html << "<div id='errorExplanation'><ul>\n"
errors.each do |error|
html << "<li>#{h error}</li>\n"
end
html << "</ul></div>\n"
end
html.html_safe
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?
hidden_field_tag('back_url', CGI.escape(back_url)) unless back_url.blank?
end
def check_all_links(form_name)
link_to_function(l(:button_check_all), "checkAll('#{form_name}', true)") +
" | ".html_safe +
" | " +
link_to_function(l(:button_uncheck_all), "checkAll('#{form_name}', false)")
end
@@ -985,11 +792,11 @@ module ApplicationHelper
legend = options[:legend] || ''
content_tag('table',
content_tag('tr',
(pcts[0] > 0 ? content_tag('td', '', :style => "width: #{pcts[0]}%;", :class => 'closed') : ''.html_safe) +
(pcts[1] > 0 ? content_tag('td', '', :style => "width: #{pcts[1]}%;", :class => 'done') : ''.html_safe) +
(pcts[2] > 0 ? content_tag('td', '', :style => "width: #{pcts[2]}%;", :class => 'todo') : ''.html_safe)
), :class => 'progress', :style => "width: #{width};").html_safe +
content_tag('p', legend, :class => 'pourcent').html_safe
(pcts[0] > 0 ? content_tag('td', '', :style => "width: #{pcts[0]}%;", :class => 'closed') : '') +
(pcts[1] > 0 ? content_tag('td', '', :style => "width: #{pcts[1]}%;", :class => 'done') : '') +
(pcts[2] > 0 ? content_tag('td', '', :style => "width: #{pcts[2]}%;", :class => 'todo') : '')
), :class => 'progress', :style => "width: #{width};") +
content_tag('p', legend, :class => 'pourcent')
end
def checked_image(checked=true)
@@ -1014,6 +821,22 @@ module ApplicationHelper
javascript_tag "new ContextMenu('#{ url_for(url) }')"
end
def context_menu_link(name, url, options={})
options[:class] ||= ''
if options.delete(:selected)
options[:class] << ' icon-checked disabled'
options[:disabled] = true
end
if options.delete(:disabled)
options.delete(:method)
options.delete(:confirm)
options.delete(:onclick)
options[:class] << ' disabled'
url = '#'
end
link_to name, url, options
end
def calendar_for(field_id)
include_calendar_headers_tags
image_tag("calendar.png", {:id => "#{field_id}_trigger",:class => "calendar-trigger"}) +
@@ -1054,10 +877,6 @@ module ApplicationHelper
(@has_content && @has_content[name]) || false
end
def email_delivery_enabled?
!!ActionMailer::Base.perform_deliveries
end
# Returns the avatar image tag for the given +user+ if avatars are enabled
# +user+ can be a User or a string that will be scanned for an email address (eg. 'joe <joe@foo.bar>')
def avatar(user, options = { })
@@ -1083,17 +902,17 @@ module ApplicationHelper
def javascript_heads
tags = javascript_include_tag(:defaults)
unless User.current.pref.warn_on_leaving_unsaved == '0'
tags << "\n".html_safe + javascript_tag("Event.observe(window, 'load', function(){ new WarnLeavingUnsaved('#{escape_javascript( l(:text_warn_on_leaving_unsaved) )}'); });")
tags << "\n" + javascript_tag("Event.observe(window, 'load', function(){ new WarnLeavingUnsaved('#{escape_javascript( l(:text_warn_on_leaving_unsaved) )}'); });")
end
tags
end
def favicon
"<link rel='shortcut icon' href='#{image_path('/favicon.ico')}' />".html_safe
"<link rel='shortcut icon' href='#{image_path('/favicon.ico')}' />"
end
def robot_exclusion_tag
'<meta name="robots" content="noindex,follow,noarchive" />'.html_safe
'<meta name="robots" content="noindex,follow,noarchive" />'
end
# Returns true if arg is expected in the API response

View File

@@ -1,5 +1,3 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
#
@@ -30,16 +28,19 @@ module AttachmentsHelper
end
end
def render_api_attachment(attachment, api)
api.attachment do
api.id attachment.id
api.filename attachment.filename
api.filesize attachment.filesize
api.content_type attachment.content_type
api.description attachment.description
api.content_url url_for(:controller => 'attachments', :action => 'download', :id => attachment, :filename => attachment.filename, :only_path => false)
api.author(:id => attachment.author.id, :name => attachment.author.name) if attachment.author
api.created_on attachment.created_on
def to_utf8(str)
if str.respond_to?(:force_encoding)
str.force_encoding('UTF-8')
return str if str.valid_encoding?
else
return str if /\A[\r\n\t\x20-\x7e]*\Z/n.match(str) # for us-ascii
end
begin
Iconv.conv('UTF-8//IGNORE', 'UTF-8', str + ' ')[0..-3]
rescue Iconv::InvalidEncoding
# "UTF-8//IGNORE" is not supported on some OS
str
end
end
end

View File

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

View File

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

View File

@@ -1,22 +1,3 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
module CalendarsHelper
def link_to_previous_month(year, month, options={})
target_year, target_month = if month == 1
@@ -24,15 +5,14 @@ module CalendarsHelper
else
[year, month - 1]
end
name = if target_month == 12
"#{month_name(target_month)} #{target_year}"
else
"#{month_name(target_month)}"
end
# \xc2\xab(utf-8) = &#171;
link_to_month(("\xc2\xab " + name), target_year, target_month, options)
link_to_month(('&#171; ' + name), target_year, target_month, options)
end
def link_to_next_month(year, month, options={})
@@ -48,11 +28,10 @@ module CalendarsHelper
"#{month_name(target_month)}"
end
# \xc2\xbb(utf-8) = &#187;
link_to_month((name + " \xc2\xbb"), target_year, target_month, options)
link_to_month((name + ' &#187;'), target_year, target_month, options)
end
def link_to_month(link_name, year, month, options={})
link_to_content_update(h(link_name), params.merge(:year => year, :month => month))
link_to_content_update(link_name, params.merge(:year => year, :month => month))
end
end

View File

@@ -1,43 +0,0 @@
# encoding: utf-8
#
# 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.
module ContextMenusHelper
def context_menu_link(name, url, options={})
options[:class] ||= ''
if options.delete(:selected)
options[:class] << ' icon-checked disabled'
options[:disabled] = true
end
if options.delete(:disabled)
options.delete(:method)
options.delete(:confirm)
options.delete(:onclick)
options[:class] << ' disabled'
url = '#'
end
link_to h(name), url, options
end
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},
:method => :post,
:selected => (@issue && @issue.custom_field_value(field) == value)
end
end

View File

@@ -1,18 +1,16 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
@@ -31,65 +29,52 @@ module CustomFieldsHelper
{:name => 'DocumentCategoryCustomField', :partial => 'custom_fields/index', :label => DocumentCategory::OptionName}
]
end
# Return custom field html tag corresponding to its format
def custom_field_tag(name, custom_value)
custom_field = custom_value.custom_field
field_name = "#{name}[custom_field_values][#{custom_field.id}]"
field_name << "[]" if custom_field.multiple?
field_id = "#{name}_custom_field_values_#{custom_field.id}"
field_format = Redmine::CustomFieldFormat.find_by_name(custom_field.field_format)
case field_format.try(:edit_as)
when "date"
text_field_tag(field_name, custom_value.value, :id => field_id, :size => 10) +
text_field_tag(field_name, custom_value.value, :id => field_id, :size => 10) +
calendar_for(field_id)
when "text"
text_area_tag(field_name, custom_value.value, :id => field_id, :rows => 3, :style => 'width:90%')
when "bool"
hidden_field_tag(field_name, '0') + check_box_tag(field_name, '1', custom_value.true?, :id => field_id)
when "list"
blank_option = ''
unless custom_field.multiple?
if custom_field.is_required?
unless custom_field.default_value.present?
blank_option = "<option value=\"\">--- #{l(:actionview_instancetag_blank_option)} ---</option>"
end
else
blank_option = '<option></option>'
end
end
s = select_tag(field_name, blank_option.html_safe + options_for_select(custom_field.possible_values_options(custom_value.customized), custom_value.value),
:id => field_id, :multiple => custom_field.multiple?)
if custom_field.multiple?
s << hidden_field_tag(field_name, '')
end
s
blank_option = custom_field.is_required? ?
(custom_field.default_value.blank? ? "<option value=\"\">--- #{l(:actionview_instancetag_blank_option)} ---</option>" : '') :
'<option></option>'
select_tag(field_name, blank_option + options_for_select(custom_field.possible_values_options(custom_value.customized), custom_value.value), :id => field_id)
else
text_field_tag(field_name, custom_value.value, :id => field_id)
end
end
# Return custom field label tag
def custom_field_label_tag(name, custom_value)
content_tag "label", h(custom_value.custom_field.name) +
(custom_value.custom_field.is_required? ? " <span class=\"required\">*</span>".html_safe : ""),
:for => "#{name}_custom_field_values_#{custom_value.custom_field.id}"
content_tag "label", custom_value.custom_field.name +
(custom_value.custom_field.is_required? ? " <span class=\"required\">*</span>" : ""),
:for => "#{name}_custom_field_values_#{custom_value.custom_field.id}",
:class => (custom_value.errors.empty? ? nil : "error" )
end
# Return custom field tag with its label tag
def custom_field_tag_with_label(name, custom_value)
custom_field_label_tag(name, custom_value) + custom_field_tag(name, custom_value)
end
def custom_field_tag_for_bulk_edit(name, custom_field, projects=nil)
field_name = "#{name}[custom_field_values][#{custom_field.id}]"
field_name << "[]" if custom_field.multiple?
field_id = "#{name}_custom_field_values_#{custom_field.id}"
field_format = Redmine::CustomFieldFormat.find_by_name(custom_field.field_format)
case field_format.try(:edit_as)
when "date"
text_field_tag(field_name, '', :id => field_id, :size => 10) +
text_field_tag(field_name, '', :id => field_id, :size => 10) +
calendar_for(field_id)
when "text"
text_area_tag(field_name, '', :id => field_id, :rows => 3, :style => 'width:90%')
@@ -98,12 +83,7 @@ module CustomFieldsHelper
[l(:general_text_yes), '1'],
[l(:general_text_no), '0']]), :id => field_id)
when "list"
options = []
options << [l(:label_no_change_option), ''] unless custom_field.multiple?
options << [l(:label_none), '__none__'] unless custom_field.is_required?
options += custom_field.possible_values_options(projects)
select_tag(field_name, options_for_select(options),
:id => field_id, :multiple => custom_field.multiple?)
select_tag(field_name, options_for_select([[l(:label_no_change_option), '']] + custom_field.possible_values_options(projects)), :id => field_id)
else
text_field_tag(field_name, '', :id => field_id)
end
@@ -114,37 +94,23 @@ module CustomFieldsHelper
return "" unless custom_value
format_value(custom_value.value, custom_value.custom_field.field_format)
end
# Return a string used to display a custom value
def format_value(value, field_format)
if value.is_a?(Array)
value.collect {|v| format_value(v, field_format)}.compact.sort.join(', ')
else
Redmine::CustomFieldFormat.format_value(value, field_format)
end
Redmine::CustomFieldFormat.format_value(value, field_format) # Proxy
end
# Return an array of custom field formats which can be used in select_tag
def custom_field_formats_for_select(custom_field)
Redmine::CustomFieldFormat.as_select(custom_field.class.customized_class.name)
end
# Renders the custom_values in api views
def render_api_custom_values(custom_values, api)
api.array :custom_fields do
custom_values.each do |custom_value|
attrs = {:id => custom_value.custom_field_id, :name => custom_value.custom_field.name}
attrs.merge!(:multiple => true) if custom_value.custom_field.multiple?
api.custom_field attrs do
if custom_value.value.is_a?(Array)
api.array :value do
custom_value.value.each do |value|
api.value value unless value.blank?
end
end
else
api.value custom_value.value
end
api.custom_field :id => custom_value.custom_field_id, :name => custom_value.custom_field.name do
api.value custom_value.value
end
end
end unless custom_values.empty?

View File

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

View File

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

View File

@@ -1,5 +1,3 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
#
@@ -7,12 +5,12 @@
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
@@ -27,16 +25,16 @@ module GanttHelper
params.merge(gantt.params.merge(:zoom => (gantt.zoom+1))),
:class => 'icon icon-zoom-in'
else
content_tag('span', l(:text_zoom_in), :class => 'icon icon-zoom-in').html_safe
content_tag('span', l(:text_zoom_in), :class => 'icon icon-zoom-in')
end
when :out
if gantt.zoom > 1
link_to_content_update l(:text_zoom_out),
params.merge(gantt.params.merge(:zoom => (gantt.zoom-1))),
:class => 'icon icon-zoom-out'
else
content_tag('span', l(:text_zoom_out), :class => 'icon icon-zoom-out').html_safe
content_tag('span', l(:text_zoom_out), :class => 'icon icon-zoom-out')
end
end
end

View File

@@ -1,18 +1,16 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# Copyright (C) 2006-2009 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
@@ -26,7 +24,7 @@ module GroupsHelper
end
options
end
def group_settings_tabs
tabs = [{:name => 'general', :partial => 'groups/general', :label => :label_general},
{:name => 'users', :partial => 'groups/users', :label => :label_user_plural},

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,5 +1,3 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
#
@@ -48,13 +46,13 @@ module IssuesHelper
@cached_label_priority ||= l(:field_priority)
@cached_label_project ||= l(:field_project)
link_to_issue(issue) + "<br /><br />".html_safe +
"<strong>#{@cached_label_project}</strong>: #{link_to_project(issue.project)}<br />".html_safe +
"<strong>#{@cached_label_status}</strong>: #{h(issue.status.name)}<br />".html_safe +
"<strong>#{@cached_label_start_date}</strong>: #{format_date(issue.start_date)}<br />".html_safe +
"<strong>#{@cached_label_due_date}</strong>: #{format_date(issue.due_date)}<br />".html_safe +
"<strong>#{@cached_label_assigned_to}</strong>: #{h(issue.assigned_to)}<br />".html_safe +
"<strong>#{@cached_label_priority}</strong>: #{h(issue.priority.name)}".html_safe
link_to_issue(issue) + "<br /><br />" +
"<strong>#{@cached_label_project}</strong>: #{link_to_project(issue.project)}<br />" +
"<strong>#{@cached_label_status}</strong>: #{issue.status.name}<br />" +
"<strong>#{@cached_label_start_date}</strong>: #{format_date(issue.start_date)}<br />" +
"<strong>#{@cached_label_due_date}</strong>: #{format_date(issue.due_date)}<br />" +
"<strong>#{@cached_label_assigned_to}</strong>: #{issue.assigned_to}<br />" +
"<strong>#{@cached_label_priority}</strong>: #{issue.priority.name}"
end
def issue_heading(issue)
@@ -74,7 +72,7 @@ module IssuesHelper
end
s << content_tag('h3', subject)
s << '</div>' * (ancestors.size + 1)
s.html_safe
s
end
def render_descendants_tree(issue)
@@ -88,8 +86,8 @@ module IssuesHelper
content_tag('td', progress_bar(child.done_ratio, :width => '80px')),
:class => "issue issue-#{child.id} hascontextmenu #{level > 0 ? "idnt idnt-#{level}" : nil}")
end
s << '</table></form>'
s.html_safe
s << '</form></table>'
s
end
def render_custom_fields_rows(issue)
@@ -108,7 +106,7 @@ module IssuesHelper
n += 1
end
s << "</tr>\n"
s.html_safe
s
end
def issues_destroy_confirmation_message(issues)
@@ -131,11 +129,14 @@ module IssuesHelper
def sidebar_queries
unless @sidebar_queries
@sidebar_queries = Query.visible.all(
:order => "#{Query.table_name}.name ASC",
# Project specific queries and global queries
:conditions => (@project.nil? ? ["project_id IS NULL"] : ["project_id IS NULL OR project_id = ?", @project.id])
)
# User can see public queries and his own queries
visible = ARCondition.new(["is_public = ? OR user_id = ?", true, (User.current.logged? ? User.current.id : 0)])
# Project specific queries and global queries
visible << (@project.nil? ? ["project_id IS NULL"] : ["project_id IS NULL OR project_id = ?", @project.id])
@sidebar_queries = Query.find(:all,
:select => 'id, name, is_public',
:order => "name ASC",
:conditions => visible.conditions)
end
@sidebar_queries
end
@@ -144,16 +145,14 @@ module IssuesHelper
# links to #index on issues/show
url_params = controller_name == 'issues' ? {:controller => 'issues', :action => 'index', :project_id => @project} : params
content_tag('h3', h(title)) +
content_tag('h3', title) +
queries.collect {|query|
css = 'query'
css << ' selected' if query == @query
link_to(h(query.name), url_params.merge(:query_id => query), :class => css)
}.join('<br />').html_safe
link_to(h(query.name), url_params.merge(:query_id => query))
}.join('<br />')
end
def render_sidebar_queries
out = ''.html_safe
out = ''
queries = sidebar_queries.select {|q| !q.is_public?}
out << query_links(l(:label_my_queries), queries) if queries.any?
queries = sidebar_queries.select {|q| q.is_public?}
@@ -161,76 +160,36 @@ module IssuesHelper
out
end
# Returns the textual representation of a journal details
# as an array of strings
def details_to_strings(details, no_html=false, options={})
options[:only_path] = (options[:only_path] == false ? false : true)
strings = []
values_by_field = {}
details.each do |detail|
if detail.property == 'cf'
field_id = detail.prop_key
field = CustomField.find_by_id(field_id)
if field && field.multiple?
values_by_field[field_id] ||= {:added => [], :deleted => []}
if detail.old_value
values_by_field[field_id][:deleted] << detail.old_value
end
if detail.value
values_by_field[field_id][:added] << detail.value
end
next
end
end
strings << show_detail(detail, no_html, options)
end
values_by_field.each do |field_id, changes|
detail = JournalDetail.new(:property => 'cf', :prop_key => field_id)
if changes[:added].any?
detail.value = changes[:added]
strings << show_detail(detail, no_html, options)
elsif changes[:deleted].any?
detail.old_value = changes[:deleted]
strings << show_detail(detail, no_html, options)
end
end
strings
end
# Returns the textual representation of a single journal detail
def show_detail(detail, no_html=false, options={})
multiple = false
def show_detail(detail, no_html=false)
case detail.property
when 'attr'
field = detail.prop_key.to_s.gsub(/\_id$/, "")
label = l(("field_" + field).to_sym)
case detail.prop_key
when 'due_date', 'start_date'
case
when ['due_date', 'start_date'].include?(detail.prop_key)
value = format_date(detail.value.to_date) if detail.value
old_value = format_date(detail.old_value.to_date) if detail.old_value
when 'project_id', 'status_id', 'tracker_id', 'assigned_to_id',
'priority_id', 'category_id', 'fixed_version_id'
when ['project_id', 'status_id', 'tracker_id', 'assigned_to_id', 'priority_id', 'category_id', 'fixed_version_id'].include?(detail.prop_key)
value = find_name_by_reflection(field, detail.value)
old_value = find_name_by_reflection(field, detail.old_value)
when 'estimated_hours'
when detail.prop_key == 'estimated_hours'
value = "%0.02f" % detail.value.to_f unless detail.value.blank?
old_value = "%0.02f" % detail.old_value.to_f unless detail.old_value.blank?
when 'parent_id'
when detail.prop_key == 'parent_id'
label = l(:field_parent_issue)
value = "##{detail.value}" unless detail.value.blank?
old_value = "##{detail.old_value}" unless detail.old_value.blank?
when 'is_private'
when detail.prop_key == 'is_private'
value = l(detail.value == "0" ? :general_text_No : :general_text_Yes) unless detail.value.blank?
old_value = l(detail.old_value == "0" ? :general_text_No : :general_text_Yes) unless detail.old_value.blank?
end
when 'cf'
custom_field = CustomField.find_by_id(detail.prop_key)
if custom_field
multiple = custom_field.multiple?
label = custom_field.name
value = format_value(detail.value, custom_field.field_format) if detail.value
old_value = format_value(detail.old_value, custom_field.field_format) if detail.old_value
@@ -238,8 +197,7 @@ module IssuesHelper
when 'attachment'
label = l(:label_attachment)
end
call_hook(:helper_issues_show_detail_after_setting,
{:detail => detail, :label => label, :value => value, :old_value => old_value })
call_hook(:helper_issues_show_detail_after_setting, {:detail => detail, :label => label, :value => value, :old_value => old_value })
label ||= detail.prop_key
value ||= detail.value
@@ -248,17 +206,10 @@ 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?
if detail.property == 'attachment' && !value.blank? && atta = Attachment.find_by_id(detail.prop_key)
old_value = content_tag("strike", old_value) if detail.old_value and (!detail.value or detail.value.empty?)
if detail.property == 'attachment' && !value.blank? && a = Attachment.find_by_id(detail.prop_key)
# Link to the attachment if it has not been removed
value = link_to_attachment(atta, :download => true, :only_path => options[:only_path])
if options[:only_path] != false && atta.is_text?
value += link_to(
image_tag('magnifier.png'),
:controller => 'attachments', :action => 'show',
:id => atta, :filename => atta.filename
)
end
value = link_to_attachment(a)
else
value = content_tag("i", h(value)) if value
end
@@ -268,27 +219,24 @@ module IssuesHelper
s = l(:text_journal_changed_no_detail, :label => label)
unless no_html
diff_link = link_to 'diff',
{:controller => 'journals', :action => 'diff', :id => detail.journal_id,
:detail_id => detail.id, :only_path => options[:only_path]},
{:controller => 'journals', :action => 'diff', :id => detail.journal_id, :detail_id => detail.id},
:title => l(:label_view_diff)
s << " (#{ diff_link })"
end
s.html_safe
elsif detail.value.present?
s
elsif !detail.value.blank?
case detail.property
when 'attr', 'cf'
if detail.old_value.present?
l(:text_journal_changed, :label => label, :old => old_value, :new => value).html_safe
elsif multiple
l(:text_journal_added, :label => label, :value => value).html_safe
if !detail.old_value.blank?
l(:text_journal_changed, :label => label, :old => old_value, :new => value)
else
l(:text_journal_set_to, :label => label, :value => value).html_safe
l(:text_journal_set_to, :label => label, :value => value)
end
when 'attachment'
l(:text_journal_added, :label => label, :value => value).html_safe
l(:text_journal_added, :label => label, :value => value)
end
else
l(:text_journal_deleted, :label => label, :old => old_value).html_safe
l(:text_journal_deleted, :label => label, :old => old_value)
end
end
@@ -315,38 +263,59 @@ module IssuesHelper
end
end
def issues_to_csv(issues, project, query, options={})
def issues_to_csv(issues, project = nil)
ic = Iconv.new(l(:general_csv_encoding), 'UTF-8')
decimal_separator = l(:general_csv_decimal_separator)
encoding = l(:general_csv_encoding)
columns = (options[:columns] == 'all' ? query.available_columns : query.columns)
export = FCSV.generate(:col_sep => l(:general_csv_separator)) do |csv|
# csv header fields
csv << [ "#" ] + columns.collect {|c| Redmine::CodesetUtil.from_utf8(c.caption.to_s, encoding) } +
(options[:description] ? [Redmine::CodesetUtil.from_utf8(l(:field_description), encoding)] : [])
headers = [ "#",
l(:field_status),
l(:field_project),
l(:field_tracker),
l(:field_priority),
l(:field_subject),
l(:field_assigned_to),
l(:field_category),
l(:field_fixed_version),
l(:field_author),
l(:field_start_date),
l(:field_due_date),
l(:field_done_ratio),
l(:field_estimated_hours),
l(:field_parent_issue),
l(:field_created_on),
l(:field_updated_on)
]
# Export project custom fields if project is given
# otherwise export custom fields marked as "For all projects"
custom_fields = project.nil? ? IssueCustomField.for_all : project.all_issue_custom_fields
custom_fields.each {|f| headers << f.name}
# Description in the last column
headers << l(:field_description)
csv << headers.collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end }
# csv lines
issues.each do |issue|
col_values = columns.collect do |column|
s = if column.is_a?(QueryCustomFieldColumn)
cv = issue.custom_field_values.detect {|v| v.custom_field_id == column.custom_field.id}
show_value(cv)
else
value = column.value(issue)
if value.is_a?(Date)
format_date(value)
elsif value.is_a?(Time)
format_time(value)
elsif value.is_a?(Float)
("%.2f" % value).gsub('.', decimal_separator)
else
value
end
end
s.to_s
end
csv << [ issue.id.to_s ] + col_values.collect {|c| Redmine::CodesetUtil.from_utf8(c.to_s, encoding) } +
(options[:description] ? [Redmine::CodesetUtil.from_utf8(issue.description, encoding)] : [])
fields = [issue.id,
issue.status.name,
issue.project.name,
issue.tracker.name,
issue.priority.name,
issue.subject,
issue.assigned_to,
issue.category,
issue.fixed_version,
issue.author.name,
format_date(issue.start_date),
format_date(issue.due_date),
issue.done_ratio,
issue.estimated_hours.to_s.gsub('.', decimal_separator),
issue.parent_id,
format_time(issue.created_on),
format_time(issue.updated_on)
]
custom_fields.each {|f| fields << show_value(issue.custom_value_for(f)) }
fields << issue.description
csv << fields.collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end }
end
end
export

View File

@@ -1,18 +1,16 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006-2008 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
@@ -26,17 +24,17 @@ module JournalsHelper
links << link_to_remote(image_tag('comment.png'),
{ :url => {:controller => 'journals', :action => 'new', :id => issue, :journal_id => journal} },
:title => l(:button_quote)) if options[:reply_links]
links << link_to_in_place_notes_editor(image_tag('edit.png'), "journal-#{journal.id}-notes",
links << link_to_in_place_notes_editor(image_tag('edit.png'), "journal-#{journal.id}-notes",
{ :controller => 'journals', :action => 'edit', :id => journal },
:title => l(:button_edit)) if editable
end
content << content_tag('div', links.join(' ').html_safe, :class => 'contextual') unless links.empty?
content << content_tag('div', links.join(' '), :class => 'contextual') unless links.empty?
content << textilizable(journal, :notes)
css_classes = "wiki"
css_classes << " editable" if editable
content_tag('div', content.html_safe, :id => "journal-#{journal.id}-notes", :class => css_classes)
content_tag('div', content, :id => "journal-#{journal.id}-notes", :class => css_classes)
end
def link_to_in_place_notes_editor(text, field_id, url, options={})
onclick = "new Ajax.Request('#{url_for(url)}', {asynchronous:true, evalScripts:true, method:'get'}); return false;"
link_to text, '#', options.merge(:onclick => onclick)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,5 +1,3 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
#
@@ -30,7 +28,7 @@ module ProjectsHelper
{:name => 'versions', :action => :manage_versions, :partial => 'projects/settings/versions', :label => :label_version_plural},
{:name => 'categories', :action => :manage_categories, :partial => 'projects/settings/issue_categories', :label => :label_issue_category_plural},
{:name => 'wiki', :action => :manage_wiki, :partial => 'projects/settings/wiki', :label => :label_wiki},
{:name => 'repositories', :action => :manage_repository, :partial => 'projects/settings/repositories', :label => :label_repository_plural},
{:name => 'repository', :action => :manage_repository, :partial => 'projects/settings/repository', :label => :label_repository},
{:name => 'boards', :action => :manage_boards, :partial => 'projects/settings/boards', :label => :label_board_plural},
{:name => 'activities', :action => :manage_project_activities, :partial => 'projects/settings/activities', :label => :enumeration_activities}
]
@@ -48,7 +46,7 @@ module ProjectsHelper
options = ''
options << "<option value=''></option>" if project.allowed_parents.include?(nil)
options << project_tree_options_for_select(project.allowed_parents.compact, :selected => selected)
content_tag('select', options.html_safe, :name => 'project[parent_id]', :id => 'project_parent_id')
content_tag('select', options, :name => 'project[parent_id]', :id => 'project_parent_id')
end
# Renders a tree of projects as a nested set of unordered lists
@@ -82,7 +80,7 @@ module ProjectsHelper
s << ("</li></ul>\n" * ancestors.size)
@project = original_project
end
s.html_safe
s
end
# Returns a set of options for a select field, grouped by project.

View File

@@ -1,5 +1,3 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
#
@@ -7,38 +5,31 @@
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
module QueriesHelper
def operators_for_select(filter_type)
Query.operators_by_filter_type[filter_type].collect {|o| [l(Query.operators[o]), o]}
end
def column_header(column)
column.sortable ? sort_header_tag(column.name.to_s, :caption => column.caption,
:default_order => column.default_order) :
content_tag('th', h(column.caption))
end
def column_content(column, issue)
value = column.value(issue)
if value.is_a?(Array)
value.collect {|v| column_value(column, issue, v)}.compact.sort.join(', ').html_safe
else
column_value(column, issue, value)
end
:default_order => column.default_order) :
content_tag('th', column.caption)
end
def column_value(column, issue, value)
def column_content(column, issue)
value = column.value(issue)
case value.class.name
when 'String'
if column.name == :subject
@@ -53,10 +44,8 @@ module QueriesHelper
when 'Fixnum', 'Float'
if column.name == :done_ratio
progress_bar(value, :width => '80px')
elsif column.name == :spent_hours
sprintf "%.2f", value
else
h(value.to_s)
value.to_s
end
when 'User'
link_to_user value
@@ -85,47 +74,27 @@ module QueriesHelper
@query.project = @project
session[:query] = {:id => @query.id, :project_id => @query.project_id}
sort_clear
elsif api_request? || params[:set_filter] || session[:query].nil? || session[:query][:project_id] != (@project ? @project.id : nil)
# Give it a name, required to be valid
@query = Query.new(:name => "_")
@query.project = @project
build_query_from_params
session[:query] = {:project_id => @query.project_id, :filters => @query.filters, :group_by => @query.group_by, :column_names => @query.column_names}
else
# retrieve from session
@query = Query.find_by_id(session[:query][:id]) if session[:query][:id]
@query ||= Query.new(:name => "_", :filters => session[:query][:filters], :group_by => session[:query][:group_by], :column_names => session[:query][:column_names])
@query.project = @project
end
end
def retrieve_query_from_session
if session[:query]
if session[:query][:id]
@query = Query.find_by_id(session[:query][:id])
return unless @query
else
@query = Query.new(:name => "_", :filters => session[:query][:filters], :group_by => session[:query][:group_by], :column_names => session[:query][:column_names])
end
if session[:query].has_key?(:project_id)
@query.project_id = session[:query][:project_id]
if api_request? || params[:set_filter] || session[:query].nil? || session[:query][:project_id] != (@project ? @project.id : nil)
# Give it a name, required to be valid
@query = Query.new(:name => "_")
@query.project = @project
if params[:fields] || params[:f]
@query.filters = {}
@query.add_filters(params[:fields] || params[:f], params[:operators] || params[:op], params[:values] || params[:v])
else
@query.available_filters.keys.each do |field|
@query.add_short_filter(field, params[field]) if params[field]
end
end
@query.group_by = params[:group_by]
@query.column_names = params[:c] || (params[:query] && params[:query][:column_names])
session[:query] = {:project_id => @query.project_id, :filters => @query.filters, :group_by => @query.group_by, :column_names => @query.column_names}
else
@query = Query.find_by_id(session[:query][:id]) if session[:query][:id]
@query ||= Query.new(:name => "_", :project => @project, :filters => session[:query][:filters], :group_by => session[:query][:group_by], :column_names => session[:query][:column_names])
@query.project = @project
end
@query
end
end
def build_query_from_params
if params[:fields] || params[:f]
@query.filters = {}
@query.add_filters(params[:fields] || params[:f], params[:operators] || params[:op], params[:values] || params[:v])
else
@query.available_filters.keys.each do |field|
@query.add_short_filter(field, params[field]) if params[field]
end
end
@query.group_by = params[:group_by] || (params[:query] && params[:query][:group_by])
@query.column_names = params[:c] || (params[:query] && params[:query][:column_names])
end
end

View File

@@ -1,5 +1,3 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
#
@@ -33,6 +31,6 @@ module ReportsHelper
def aggregate_link(data, criteria, *args)
a = aggregate data, criteria
a > 0 ? link_to(h(a), *args) : '-'
a > 0 ? link_to(a, *args) : '-'
end
end

View File

@@ -1,5 +1,3 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
#
@@ -39,9 +37,9 @@ module RepositoriesHelper
unless properties.nil? || properties.empty?
content = ''
properties.keys.sort.each do |property|
content << content_tag('li', "<b>#{h property}</b>: <span>#{h properties[property]}</span>".html_safe)
content << content_tag('li', "<b>#{h property}</b>: <span>#{h properties[property]}</span>")
end
content_tag('ul', content.html_safe, :class => 'properties')
content_tag('ul', content, :class => 'properties')
end
end
@@ -89,39 +87,66 @@ module RepositoriesHelper
if s = tree[file][:s]
style << ' folder'
path_param = to_path_param(@repository.relative_path(file))
text = link_to(h(text), :controller => 'repositories',
text = link_to(text, :controller => 'repositories',
:action => 'show',
:id => @project,
:repository_id => @repository.identifier_param,
:path => path_param,
:rev => @changeset.identifier)
output << "<li class='#{style}'>#{text}"
output << "<li class='#{style}'>#{text}</li>"
output << render_changes_tree(s)
output << "</li>"
elsif c = tree[file][:c]
style << " change-#{c.action}"
path_param = to_path_param(@repository.relative_path(c.path))
text = link_to(h(text), :controller => 'repositories',
text = link_to(text, :controller => 'repositories',
:action => 'entry',
:id => @project,
:repository_id => @repository.identifier_param,
:path => path_param,
:rev => @changeset.identifier) unless c.action == 'D'
text << " - #{h(c.revision)}" unless c.revision.blank?
text << ' ('.html_safe + link_to(l(:label_diff), :controller => 'repositories',
text << " - #{c.revision}" unless c.revision.blank?
text << ' (' + link_to('diff', :controller => 'repositories',
:action => 'diff',
:id => @project,
:repository_id => @repository.identifier_param,
:path => path_param,
:rev => @changeset.identifier) + ') '.html_safe if c.action == 'M'
text << ' '.html_safe + content_tag('span', h(c.from_path), :class => 'copied-from') unless c.from_path.blank?
:rev => @changeset.identifier) + ') ' if c.action == 'M'
text << ' ' + content_tag('span', c.from_path, :class => 'copied-from') unless c.from_path.blank?
output << "<li class='#{style}'>#{text}</li>"
end
end
output << '</ul>'
output.html_safe
output
end
def to_utf8(str)
return str if str.nil?
str = to_utf8_internal(str)
if str.respond_to?(:force_encoding)
str.force_encoding('UTF-8')
end
str
end
def to_utf8_internal(str)
return str if str.nil?
if str.respond_to?(:force_encoding)
str.force_encoding('ASCII-8BIT')
end
return str if str.empty?
return str if /\A[\r\n\t\x20-\x7e]*\Z/n.match(str) # for us-ascii
if str.respond_to?(:force_encoding)
str.force_encoding('UTF-8')
end
@encodings ||= Setting.repositories_encodings.split(',').collect(&:strip)
@encodings.each do |encoding|
begin
return Iconv.conv('UTF-8', encoding, str)
rescue Iconv::Failure
# do nothing here and try the next encoding
end
end
str = Redmine::CodesetUtil.replace_invalid_utf8(str)
end
private :to_utf8_internal
def repository_field_tags(form, repository)
method = repository.class.name.demodulize.underscore + "_field_tags"
if repository.is_a?(Repository) &&
@@ -142,10 +167,13 @@ module RepositoriesHelper
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)")
:url => {
:controller => 'repositories',
:action => 'edit',
:id => @project
},
:method => :get,
:with => "Form.serialize(this.form)")
)
end
@@ -160,8 +188,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?)) +
'<br />'.html_safe +
'(file:///, http://, https://, svn://, svn+[tunnelscheme]://)') +
'<br />(file:///, http://, https://, svn://, svn+[tunnelscheme]://)') +
content_tag('p', form.text_field(:login, :size => 30)) +
content_tag('p', form.password_field(
:password, :size => 30, :name => 'ignore',
@@ -186,12 +213,12 @@ module RepositoriesHelper
:size => 60, :required => true,
:disabled => (repository && !repository.root_url.blank?)
) +
'<br />'.html_safe + l(:text_mercurial_repository_note)) +
'<br />' + l(:text_mercurial_repository_note)) +
content_tag('p', form.select(
:path_encoding, [nil] + Setting::ENCODINGS,
:label => l(:field_scm_path_encoding)
) +
'<br />'.html_safe + l(:text_scm_path_encoding_note))
'<br />' + l(:text_scm_path_encoding_note))
end
def git_field_tags(form, repository)
@@ -200,13 +227,12 @@ module RepositoriesHelper
:size => 60, :required => true,
:disabled => (repository && !repository.root_url.blank?)
) +
'<br />'.html_safe +
l(:text_git_repository_note)) +
'<br />' + l(:text_git_repository_note)) +
content_tag('p', form.select(
:path_encoding, [nil] + Setting::ENCODINGS,
:label => l(:field_scm_path_encoding)
) +
'<br />'.html_safe + l(:text_scm_path_encoding_note)) +
'<br />' + l(:text_scm_path_encoding_note)) +
content_tag('p', form.check_box(
:extra_report_last_commit,
:label => l(:label_git_report_last_commit)
@@ -231,7 +257,7 @@ module RepositoriesHelper
:path_encoding, [nil] + Setting::ENCODINGS,
:label => l(:field_scm_path_encoding)
) +
'<br />'.html_safe + l(:text_scm_path_encoding_note))
'<br />' + l(:text_scm_path_encoding_note))
end
def bazaar_field_tags(form, repository)
@@ -253,66 +279,6 @@ module RepositoriesHelper
:path_encoding, [nil] + Setting::ENCODINGS,
:label => l(:field_scm_path_encoding)
) +
'<br />'.html_safe + l(:text_scm_path_encoding_note))
end
def index_commits(commits, heads)
return nil if commits.nil? or commits.first.parents.nil?
refs_map = {}
heads.each do |head|
refs_map[head.scmid] ||= []
refs_map[head.scmid] << head
end
commits_by_scmid = {}
commits.reverse.each_with_index do |commit, commit_index|
commits_by_scmid[commit.scmid] = {
:parent_scmids => commit.parents.collect { |parent| parent.scmid },
:rdmid => commit_index,
:refs => refs_map.include?(commit.scmid) ? refs_map[commit.scmid].join(" ") : nil,
:scmid => commit.scmid,
:href => block_given? ? yield(commit.scmid) : commit.scmid
}
end
heads.sort! { |head1, head2| head1.to_s <=> head2.to_s }
space = nil
heads.each do |head|
if commits_by_scmid.include? head.scmid
space = index_head((space || -1) + 1, head, commits_by_scmid)
end
end
# when no head matched anything use first commit
space ||= index_head(0, commits.first, commits_by_scmid)
return commits_by_scmid, space
end
def index_head(space, commit, commits_by_scmid)
stack = [[space, commits_by_scmid[commit.scmid]]]
max_space = space
until stack.empty?
space, commit = stack.pop
commit[:space] = space if commit[:space].nil?
space -= 1
commit[:parent_scmids].each_with_index do |parent_scmid, parent_index|
parent_commit = commits_by_scmid[parent_scmid]
if parent_commit and parent_commit[:space].nil?
stack.unshift [space += 1, parent_commit]
end
end
max_space = space if max_space < space
end
max_space
'<br />' + l(:text_scm_path_encoding_note))
end
end

View File

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

View File

@@ -1,5 +1,3 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
#
@@ -7,12 +5,12 @@
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
@@ -21,7 +19,7 @@ module SearchHelper
def highlight_tokens(text, tokens)
return text unless text && tokens && !tokens.empty?
re_tokens = tokens.collect {|t| Regexp.escape(t)}
regexp = Regexp.new "(#{re_tokens.join('|')})", Regexp::IGNORECASE
regexp = Regexp.new "(#{re_tokens.join('|')})", Regexp::IGNORECASE
result = ''
text.split(regexp).each_with_index do |words, i|
if result.length > 1200
@@ -37,22 +35,21 @@ module SearchHelper
result << content_tag('span', h(words), :class => "highlight token-#{t}")
end
end
result.html_safe
result
end
def type_label(t)
l("label_#{t.singularize}_plural", :default => t.to_s.humanize)
end
def project_select_tag
options = [[l(:label_project_all), 'all']]
options << [l(:label_my_projects), 'my_projects'] unless User.current.memberships.empty?
options << [l(:label_and_its_subprojects, @project.name), 'subprojects'] unless @project.nil? || @project.descendants.active.empty?
options << [@project.name, ''] unless @project.nil?
label_tag("scope", l(:description_project_scope), :class => "hidden-for-sighted") +
select_tag('scope', options_for_select(options, params[:scope].to_s)) if options.size > 1
end
def render_results_by_type(results_by_type)
links = []
# Sorts types by results count
@@ -60,11 +57,8 @@ module SearchHelper
c = results_by_type[t]
next if c == 0
text = "#{type_label(t)} (#{c})"
links << link_to(h(text), :q => params[:q], :titles_only => params[:titles_only],
:all_words => params[:all_words], :scope => params[:scope], t => 1)
links << link_to(text, :q => params[:q], :titles_only => params[:titles_only], :all_words => params[:all_words], :scope => params[:scope], t => 1)
end
('<ul>'.html_safe +
links.map {|link| content_tag('li', link)}.join(' ').html_safe +
'</ul>'.html_safe) unless links.empty?
('<ul>' + links.map {|link| content_tag('li', link)}.join(' ') + '</ul>') unless links.empty?
end
end

View File

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

View File

@@ -1,5 +1,3 @@
# encoding: utf-8
#
# Helpers to sort tables using clickable column headers.
#
# Author: Stuart Rackham <srackham@methods.co.nz>, March 2005.
@@ -17,18 +15,18 @@
#
# helper :sort
# include SortHelper
#
#
# def list
# sort_init 'last_name'
# sort_update %w(first_name last_name)
# @items = Contact.find_all nil, sort_clause
# end
#
#
# Controller (using Pagination module):
#
# helper :sort
# include SortHelper
#
#
# def list
# sort_init 'last_name'
# sort_update %w(first_name last_name)
@@ -36,9 +34,9 @@
# :order_by => sort_clause,
# :per_page => 10
# end
#
#
# View (table header in list.rhtml):
#
#
# <thead>
# <tr>
# <%= sort_header_tag('id', :title => 'Sort by contact ID') %>
@@ -54,32 +52,32 @@
module SortHelper
class SortCriteria
def initialize
@criteria = []
end
def available_criteria=(criteria)
unless criteria.is_a?(Hash)
criteria = criteria.inject({}) {|h,k| h[k] = k; h}
end
@available_criteria = criteria
end
def from_param(param)
@criteria = param.to_s.split(',').collect {|s| s.split(':')[0..1]}
normalize!
end
def criteria=(arg)
@criteria = arg
normalize!
end
def to_param
@criteria.collect {|k,o| k + (o ? '' : ':desc')}.join(',')
end
def to_sql
sql = @criteria.collect do |k,o|
if s = @available_criteria[k]
@@ -88,33 +86,33 @@ module SortHelper
end.compact.join(', ')
sql.blank? ? nil : sql
end
def add!(key, asc)
@criteria.delete_if {|k,o| k == key}
@criteria = [[key, asc]] + @criteria
normalize!
end
def add(*args)
r = self.class.new.from_param(to_param)
r.add!(*args)
r
end
def first_key
@criteria.first && @criteria.first.first
end
def first_asc?
@criteria.first && @criteria.first.last
end
def empty?
@criteria.empty?
end
private
def normalize!
@criteria ||= []
@criteria = @criteria.collect {|s| s = s.to_a; [s.first, (s.last == false || s.last == 'desc') ? false : true]}
@@ -122,7 +120,7 @@ module SortHelper
@criteria.slice!(3)
self
end
# Appends DESC to the sort criterion unless it has a fixed order
def append_desc(criterion)
if criterion =~ / (asc|desc)$/i
@@ -132,14 +130,14 @@ module SortHelper
end
end
end
def sort_name
controller_name + '_' + action_name + '_sort'
end
# Initializes the default sort.
# Examples:
#
#
# sort_init 'name'
# sort_init 'id', 'desc'
# sort_init ['name', ['id', 'desc']]
@@ -160,15 +158,14 @@ module SortHelper
# sort_clause.
# - criteria can be either an array or a hash of allowed keys
#
def sort_update(criteria, sort_name=nil)
sort_name ||= self.sort_name
def sort_update(criteria)
@sort_criteria = SortCriteria.new
@sort_criteria.available_criteria = criteria
@sort_criteria.from_param(params[:sort] || session[sort_name])
@sort_criteria.criteria = @sort_default if @sort_criteria.empty?
session[sort_name] = @sort_criteria.to_param
end
# Clears the sort criteria session data
#
def sort_clear
@@ -190,7 +187,7 @@ module SortHelper
#
def sort_link(column, caption, default_order)
css, order = nil, default_order
if column.to_s == @sort_criteria.first_key
if @sort_criteria.first_asc?
css = 'sort asc'
@@ -201,14 +198,14 @@ module SortHelper
end
end
caption = column.to_s.humanize unless caption
sort_options = { :sort => @sort_criteria.add(column.to_s, order).to_param }
url_options = params.merge(sort_options)
# Add project_id to url_options
url_options = url_options.merge(:project_id => params[:project_id]) if params.has_key?(:project_id)
link_to_content_update(h(caption), url_options, :class => css)
link_to_content_update(caption, url_options, :class => css)
end
# Returns a table header <th> tag with a sort link for the named column

View File

@@ -1,25 +1,23 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
module TimelogHelper
include ApplicationHelper
def render_timelog_breadcrumb
links = []
links << link_to(l(:label_project_all), {:project_id => nil, :issue_id => nil})
@@ -54,15 +52,15 @@ module TimelogHelper
activities.each { |a| collection << [a.name, a.id] }
collection
end
def select_hours(data, criteria, value)
if value.to_s.empty?
data.select {|row| row[criteria].blank? }
else
else
data.select {|row| row[criteria].to_s == value.to_s}
end
end
def sum_hours(data)
sum = 0
data.each do |row|
@@ -70,7 +68,7 @@ module TimelogHelper
end
sum
end
def options_for_period_select(value)
options_for_select([[l(:label_all_time), 'all'],
[l(:label_today), 'today'],
@@ -84,8 +82,9 @@ module TimelogHelper
[l(:label_this_year), 'current_year']],
value)
end
def entries_to_csv(entries)
ic = Iconv.new(l(:general_csv_encoding), 'UTF-8')
decimal_separator = l(:general_csv_decimal_separator)
custom_fields = TimeEntryCustomField.find(:all)
export = FCSV.generate(:col_sep => l(:general_csv_separator)) do |csv|
@@ -102,10 +101,8 @@ module TimelogHelper
]
# Export custom fields
headers += custom_fields.collect(&:name)
csv << headers.collect {|c| Redmine::CodesetUtil.from_utf8(
c.to_s,
l(:general_csv_encoding) ) }
csv << headers.collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end }
# csv lines
entries.each do |entry|
fields = [format_date(entry.spent_on),
@@ -118,20 +115,18 @@ module TimelogHelper
entry.hours.to_s.gsub('.', decimal_separator),
entry.comments
]
fields += custom_fields.collect {|f| show_value(entry.custom_field_values.detect {|v| v.custom_field_id == f.id}) }
csv << fields.collect {|c| Redmine::CodesetUtil.from_utf8(
c.to_s,
l(:general_csv_encoding) ) }
fields += custom_fields.collect {|f| show_value(entry.custom_value_for(f)) }
csv << fields.collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end }
end
end
export
end
def format_criteria_value(criteria_options, value)
def format_criteria_value(criteria, value)
if value.blank?
"[#{l(:label_none)}]"
elsif k = criteria_options[:klass]
l(:label_none)
elsif k = @available_criterias[criteria][:klass]
obj = k.find_by_id(value.to_i)
if obj.is_a?(Issue)
obj.visible? ? "#{obj.tracker} ##{obj.id}: #{obj.subject}" : "##{obj.id}"
@@ -139,58 +134,57 @@ module TimelogHelper
obj
end
else
format_value(value, criteria_options[:format])
format_value(value, @available_criterias[criteria][:format])
end
end
def report_to_csv(report)
decimal_separator = l(:general_csv_decimal_separator)
def report_to_csv(criterias, periods, hours)
export = FCSV.generate(:col_sep => l(:general_csv_separator)) do |csv|
# Column headers
headers = report.criteria.collect {|criteria| l(report.available_criteria[criteria][:label]) }
headers += report.periods
headers = criterias.collect {|criteria| l(@available_criterias[criteria][:label]) }
headers += periods
headers << l(:label_total)
csv << headers.collect {|c| Redmine::CodesetUtil.from_utf8(
c.to_s,
l(:general_csv_encoding) ) }
csv << headers.collect {|c| to_utf8(c) }
# Content
report_criteria_to_csv(csv, report.available_criteria, report.columns, report.criteria, report.periods, report.hours)
report_criteria_to_csv(csv, criterias, periods, hours)
# Total row
str_total = Redmine::CodesetUtil.from_utf8(l(:label_total), l(:general_csv_encoding))
row = [ str_total ] + [''] * (report.criteria.size - 1)
row = [ l(:label_total) ] + [''] * (criterias.size - 1)
total = 0
report.periods.each do |period|
sum = sum_hours(select_hours(report.hours, report.columns, period.to_s))
periods.each do |period|
sum = sum_hours(select_hours(hours, @columns, period.to_s))
total += sum
row << (sum > 0 ? ("%.2f" % sum).gsub('.',decimal_separator) : '')
row << (sum > 0 ? "%.2f" % sum : '')
end
row << ("%.2f" % total).gsub('.',decimal_separator)
row << "%.2f" %total
csv << row
end
export
end
def report_criteria_to_csv(csv, available_criteria, columns, criteria, periods, hours, level=0)
decimal_separator = l(:general_csv_decimal_separator)
hours.collect {|h| h[criteria[level]].to_s}.uniq.each do |value|
hours_for_value = select_hours(hours, criteria[level], value)
def report_criteria_to_csv(csv, criterias, periods, hours, level=0)
hours.collect {|h| h[criterias[level]].to_s}.uniq.each do |value|
hours_for_value = select_hours(hours, criterias[level], value)
next if hours_for_value.empty?
row = [''] * level
row << Redmine::CodesetUtil.from_utf8(
format_criteria_value(available_criteria[criteria[level]], value).to_s,
l(:general_csv_encoding) )
row += [''] * (criteria.length - level - 1)
row << to_utf8(format_criteria_value(criterias[level], value))
row += [''] * (criterias.length - level - 1)
total = 0
periods.each do |period|
sum = sum_hours(select_hours(hours_for_value, columns, period.to_s))
sum = sum_hours(select_hours(hours_for_value, @columns, period.to_s))
total += sum
row << (sum > 0 ? ("%.2f" % sum).gsub('.',decimal_separator) : '')
row << (sum > 0 ? "%.2f" % sum : '')
end
row << ("%.2f" % total).gsub('.',decimal_separator)
row << "%.2f" %total
csv << row
if criteria.length > level + 1
report_criteria_to_csv(csv, available_criteria, columns, criteria, periods, hours_for_value, level + 1)
if criterias.length > level + 1
report_criteria_to_csv(csv, criterias, periods, hours_for_value, level + 1)
end
end
end
def to_utf8(s)
@ic ||= Iconv.new(l(:general_csv_encoding), 'UTF-8')
begin; @ic.iconv(s.to_s); rescue; s.to_s; end
end
end

View File

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

View File

@@ -1,18 +1,16 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
@@ -20,12 +18,12 @@
module UsersHelper
def users_status_options_for_select(selected)
user_count_by_status = User.count(:group => 'status').to_hash
options_for_select([[l(:label_all), ''],
["#{l(:status_active)} (#{user_count_by_status[1].to_i})", '1'],
["#{l(:status_registered)} (#{user_count_by_status[2].to_i})", '2'],
["#{l(:status_locked)} (#{user_count_by_status[3].to_i})", '3']], selected.to_s)
options_for_select([[l(:label_all), ''],
["#{l(:status_active)} (#{user_count_by_status[1].to_i})", 1],
["#{l(:status_registered)} (#{user_count_by_status[2].to_i})", 2],
["#{l(:status_locked)} (#{user_count_by_status[3].to_i})", 3]], selected)
end
# Options for the new membership projects combo-box
def options_for_membership_project_select(user, projects)
options = content_tag('option', "--- #{l(:actionview_instancetag_blank_option)} ---")
@@ -34,14 +32,14 @@ module UsersHelper
end
options
end
def user_mail_notification_options(user)
user.valid_notification_options.collect {|o| [l(o.last), o.first]}
end
def change_status_link(user)
url = {:controller => 'users', :action => 'update', :id => user, :page => params[:page], :status => params[:status], :tab => nil}
if user.locked?
link_to l(:button_unlock), url.merge(:user => {:status => User::STATUS_ACTIVE}), :method => :put, :class => 'icon icon-unlock'
elsif user.registered?
@@ -50,7 +48,7 @@ module UsersHelper
link_to l(:button_lock), url.merge(:user => {:status => User::STATUS_LOCKED}), :method => :put, :class => 'icon icon-lock'
end
end
def user_settings_tabs
tabs = [{:name => 'general', :partial => 'users/general', :label => :label_general},
{:name => 'memberships', :partial => 'users/memberships', :label => :label_project_plural}

View File

@@ -1,5 +1,3 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
#
@@ -7,12 +5,12 @@
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
@@ -20,10 +18,10 @@
module VersionsHelper
STATUS_BY_CRITERIAS = %w(category tracker status priority author assigned_to)
def render_issue_status_by(version, criteria)
criteria = 'category' unless STATUS_BY_CRITERIAS.include?(criteria)
h = Hash.new {|k,v| k[v] = [0, 0]}
begin
# Total issue count
@@ -38,10 +36,10 @@ module VersionsHelper
end
counts = h.keys.compact.sort.collect {|k| {:group => k, :total => h[k][0], :open => h[k][1], :closed => (h[k][0] - h[k][1])}}
max = counts.collect {|c| c[:total]}.max
render :partial => 'issue_counts', :locals => {:version => version, :criteria => criteria, :counts => counts, :max => max}
end
def status_by_options_for_select(value)
options_for_select(STATUS_BY_CRITERIAS.collect {|criteria| [l("field_#{criteria}".to_sym), criteria]}, value)
end

View File

@@ -1,5 +1,3 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
#
@@ -7,22 +5,22 @@
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
module WatchersHelper
def watcher_tag(object, user, options={})
content_tag("span", watcher_link(object, user), :class => watcher_css(object))
end
def watcher_link(object, user)
return '' unless user && user.logged? && object.respond_to?('watched_by?')
watched = object.watched_by?(user)
@@ -34,14 +32,14 @@ module WatchersHelper
{:url => url},
:href => url_for(url),
:class => (watched ? 'icon icon-fav' : 'icon icon-fav-off'))
end
# Returns the css class used to identify watch links for a given +object+
def watcher_css(object)
"#{object.class.to_s.underscore}-#{object.id}-watcher"
end
# Returns a comma separated list of users watching the given object
def watchers_list(object)
remove_allowed = User.current.allowed_to?("delete_#{object.class.name.underscore}_watchers".to_sym, object.project)
@@ -59,16 +57,8 @@ module WatchersHelper
:style => "vertical-align: middle",
:class => "delete")
end
content_tag :li, s.html_safe
"<li>#{ s }</li>"
end
(lis.empty? ? "" : "<ul>#{ lis.join("\n") }</ul>").html_safe
end
def watchers_checkboxes(object, users, checked=nil)
users.map do |user|
c = checked.nil? ? object.watched_by?(user) : checked
tag = check_box_tag 'issue[watcher_user_ids][]', user.id, c, :id => nil
content_tag 'label', "#{tag} #{h(user)}", :id => "issue_watcher_user_ids_#{user.id}", :class => "floating"
end.join
lis.empty? ? "" : "<ul>#{ lis.join("\n") }</ul>"
end
end

View File

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

View File

@@ -1,5 +1,3 @@
# encoding: utf-8
#
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
#
@@ -7,18 +5,18 @@
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
module WikiHelper
def wiki_page_options_for_select(pages, selected = nil, parent = nil, level = 0)
pages = pages.group_by(&:parent) unless pages.is_a?(Hash)
s = ''
@@ -27,17 +25,11 @@ module WikiHelper
attrs = "value='#{page.id}'"
attrs << " selected='selected'" if selected == page
indent = (level > 0) ? ('&nbsp;' * level * 2 + '&#187; ') : nil
s << "<option #{attrs}>#{indent}#{h page.pretty_title}</option>\n" +
s << "<option #{attrs}>#{indent}#{h page.pretty_title}</option>\n" +
wiki_page_options_for_select(pages, selected, page, level + 1)
end
end
s
end
def wiki_page_breadcrumb(page)
breadcrumb(page.ancestors.reverse.collect {|parent|
link_to(h(parent.pretty_title), {:controller => 'wiki', :action => 'show', :id => parent.title, :project_id => parent.project})
})
end
end

View File

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

View File

@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2012 Jean-Philippe Lang
# Copyright (C) 2006-2011 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -21,11 +21,9 @@ class Attachment < ActiveRecord::Base
belongs_to :container, :polymorphic => true
belongs_to :author, :class_name => "User", :foreign_key => "author_id"
validates_presence_of :filename, :author
validates_presence_of :container, :filename, :author
validates_length_of :filename, :maximum => 255
validates_length_of :disk_filename, :maximum => 255
validates_length_of :description, :maximum => 255, :allow_blank => true
validate :validate_max_file_size
acts_as_event :title => :filename,
:url => Proc.new {|o| {:controller => 'attachments', :action => 'download', :id => o.id, :filename => o.filename}}
@@ -45,31 +43,11 @@ class Attachment < ActiveRecord::Base
"LEFT JOIN #{Project.table_name} ON #{Document.table_name}.project_id = #{Project.table_name}.id"}
cattr_accessor :storage_path
@@storage_path = Redmine::Configuration['attachments_storage_path'] || "#{Rails.root}/files"
@@storage_path = Redmine::Configuration['attachments_storage_path'] || "#{RAILS_ROOT}/files"
before_save :files_to_final_location
after_destroy :delete_from_disk
def container_with_blank_type_check
if container_type.blank?
nil
else
container_without_blank_type_check
end
end
alias_method_chain :container, :blank_type_check unless method_defined?(:container_without_blank_type_check)
# Returns an unsaved copy of the attachment
def copy(attributes=nil)
copy = self.class.new
copy.attributes = self.attributes.dup.except("id", "downloads")
copy.attributes = attributes if attributes
copy
end
def validate_max_file_size
if @temp_file && self.filesize > Setting.attachment_max_size.to_i.kilobytes
errors.add(:base, l(:error_attachment_too_big, :max_size => Setting.attachment_max_size.to_i.kilobytes))
def validate
if self.filesize > Setting.attachment_max_size.to_i.kilobytes
errors.add(:base, :too_long, :count => Setting.attachment_max_size.to_i.kilobytes)
end
end
@@ -77,65 +55,45 @@ class Attachment < ActiveRecord::Base
unless incoming_file.nil?
@temp_file = incoming_file
if @temp_file.size > 0
if @temp_file.respond_to?(:original_filename)
self.filename = @temp_file.original_filename
self.filename.force_encoding("UTF-8") if filename.respond_to?(:force_encoding)
end
if @temp_file.respond_to?(:content_type)
self.content_type = @temp_file.content_type.to_s.chomp
end
if content_type.blank? && filename.present?
self.filename = sanitize_filename(@temp_file.original_filename)
self.disk_filename = Attachment.disk_filename(filename)
self.content_type = @temp_file.content_type.to_s.chomp
if content_type.blank?
self.content_type = Redmine::MimeType.of(filename)
end
self.filesize = @temp_file.size
end
end
end
def file
nil
end
def filename=(arg)
write_attribute :filename, sanitize_filename(arg.to_s)
if new_record? && disk_filename.blank?
self.disk_filename = Attachment.disk_filename(filename)
end
filename
end
# Copies the temporary file to its final location
# and computes its MD5 hash
def files_to_final_location
def before_save
if @temp_file && (@temp_file.size > 0)
logger.info("Saving attachment '#{self.diskfile}' (#{@temp_file.size} bytes)")
logger.debug("saving '#{self.diskfile}'")
md5 = Digest::MD5.new
File.open(diskfile, "wb") do |f|
if @temp_file.respond_to?(:read)
buffer = ""
while (buffer = @temp_file.read(8192))
f.write(buffer)
md5.update(buffer)
end
else
f.write(@temp_file)
md5.update(@temp_file)
buffer = ""
while (buffer = @temp_file.read(8192))
f.write(buffer)
md5.update(buffer)
end
end
self.digest = md5.hexdigest
end
@temp_file = nil
# Don't save the content type if it's longer than the authorized length
if self.content_type && self.content_type.length > 255
self.content_type = nil
end
end
# 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?
delete_from_disk!
end
# Deletes file on the disk
def after_destroy
File.delete(diskfile) if !filename.blank? && File.exist?(diskfile)
end
# Returns file's location on disk
@@ -148,19 +106,19 @@ class Attachment < ActiveRecord::Base
end
def project
container.try(:project)
container.project
end
def visible?(user=User.current)
container && container.attachments_visible?(user)
container.attachments_visible?(user)
end
def deletable?(user=User.current)
container && container.attachments_deletable?(user)
container.attachments_deletable?(user)
end
def image?
self.filename =~ /\.(bmp|gif|jpg|jpe|jpeg|png)$/i
self.filename =~ /\.(jpe?g|gif|png)$/i
end
def is_text?
@@ -176,59 +134,42 @@ class Attachment < ActiveRecord::Base
File.readable?(diskfile)
end
# Returns the attachment token
def token
"#{id}.#{digest}"
end
# Finds an attachment that matches the given token and that has no container
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})
if attachment && attachment.container.nil?
attachment
end
end
end
# Bulk attaches a set of files to an object
#
# Returns a Hash of the results:
# :files => array of the attached files
# :unsaved => array of the files that could not be attached
def self.attach_files(obj, attachments)
result = obj.save_attachments(attachments, User.current)
obj.attach_saved_attachments
result
end
attached = []
if attachments && attachments.is_a?(Hash)
attachments.each_value do |attachment|
file = attachment['file']
next unless file && file.size > 0
a = Attachment.create(:container => obj,
:file => file,
:description => attachment['description'].to_s.strip,
:author => User.current)
def self.latest_attach(attachments, filename)
attachments.sort_by(&:created_on).reverse.detect {
|att| att.filename.downcase == filename.downcase
}
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)
end
private
# Physically deletes the file from the file system
def delete_from_disk!
if disk_filename.present? && File.exist?(diskfile)
File.delete(diskfile)
if a.new_record?
obj.unsaved_attachments ||= []
obj.unsaved_attachments << a
else
attached << a
end
end
end
{:files => attached, :unsaved => obj.unsaved_attachments}
end
private
def sanitize_filename(value)
# get only the filename, not the whole path
just_filename = value.gsub(/^.*(\\|\/)/, '')
# NOTE: File.basename doesn't work right with Windows paths on Unix
# INCORRECT: just_filename = File.basename(value.gsub('\\\\', '/'))
# Finally, replace invalid characters with underscore
@filename = just_filename.gsub(/[\/\?\%\*\:\|\"\'<>]+/, '_')
# Finally, replace all non alphanumeric, hyphens or periods with underscore
@filename = just_filename.gsub(/[^\w\.\-]/,'_')
end
# Returns an ASCII or hashed filename

View File

@@ -1,48 +1,43 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
# Generic exception for when the AuthSource can not be reached
# (eg. can not connect to the LDAP)
class AuthSourceException < Exception; end
class AuthSource < ActiveRecord::Base
include Redmine::SubclassFactory
include Redmine::Ciphering
has_many :users
validates_presence_of :name
validates_uniqueness_of :name
validates_length_of :name, :maximum => 60
def authenticate(login, password)
end
def test_connection
end
def auth_method_name
"Abstract"
end
def account_password
read_ciphered_attribute(:account_password)
end
def account_password=(arg)
write_ciphered_attribute(:account_password, arg)
end

View File

@@ -1,93 +1,68 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require 'iconv'
require 'net/ldap'
require 'net/ldap/dn'
require 'iconv'
class AuthSourceLdap < AuthSource
class AuthSourceLdap < AuthSource
validates_presence_of :host, :port, :attr_login
validates_length_of :name, :host, :maximum => 60, :allow_nil => true
validates_length_of :account, :account_password, :base_dn, :filter, :maximum => 255, :allow_blank => true
validates_length_of :account, :account_password, :base_dn, :maximum => 255, :allow_nil => true
validates_length_of :attr_login, :attr_firstname, :attr_lastname, :attr_mail, :maximum => 30, :allow_nil => true
validates_numericality_of :port, :only_integer => true
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
def after_initialize
self.port = 389 if self.port == 0
end
def authenticate(login, password)
return nil if login.blank? || password.blank?
attrs = get_user_dn(login, password)
attrs = get_user_dn(login)
if attrs && attrs[:dn] && authenticate_dn(attrs[:dn], password)
logger.debug "Authentication successful for '#{login}'" if logger && logger.debug?
return attrs.except(:dn)
end
rescue Net::LDAP::LdapError => e
raise AuthSourceException.new(e.message)
rescue Net::LDAP::LdapError => text
raise "LdapError: " + text
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
rescue Net::LDAP::LdapError => text
raise "LdapError: " + text
end
def auth_method_name
"LDAP"
end
private
def ldap_filter
if filter.present?
Net::LDAP::Filter.construct(filter)
end
rescue Net::LDAP::LdapError
nil
end
def validate_filter
if filter.present? && ldap_filter.nil?
errors.add(:filter, :invalid)
end
end
def strip_ldap_attributes
[:attr_login, :attr_firstname, :attr_lastname, :attr_mail].each do |attr|
write_attribute(attr, read_attribute(attr).strip) unless read_attribute(attr).nil?
end
end
def initialize_ldap_con(ldap_user, ldap_password)
options = { :host => self.host,
:port => self.port,
@@ -125,24 +100,14 @@ class AuthSourceLdap < AuthSource
end
# Get the user's dn and any attributes for them, given their login
def get_user_dn(login, password)
ldap_con = nil
if self.account && self.account.include?("$login")
ldap_con = initialize_ldap_con(self.account.sub("$login", Net::LDAP::DN.escape(login)), password)
else
ldap_con = initialize_ldap_con(self.account, self.account_password)
end
login_filter = Net::LDAP::Filter.eq( self.attr_login, login )
object_filter = Net::LDAP::Filter.eq( "objectClass", "*" )
def get_user_dn(login)
ldap_con = initialize_ldap_con(self.account, self.account_password)
login_filter = Net::LDAP::Filter.eq( self.attr_login, login )
object_filter = Net::LDAP::Filter.eq( "objectClass", "*" )
attrs = {}
search_filter = object_filter & login_filter
if f = ldap_filter
search_filter = search_filter & f
end
ldap_con.search( :base => self.base_dn,
:filter => search_filter,
ldap_con.search( :base => self.base_dn,
:filter => object_filter & login_filter,
:attributes=> search_attributes) do |entry|
if onthefly_register?
@@ -156,7 +121,7 @@ class AuthSourceLdap < AuthSource
attrs
end
def self.get_attr(entry, attr_name)
if !attr_name.blank?
entry[attr_name].is_a?(Array) ? entry[attr_name].first : entry[attr_name]

View File

@@ -1,50 +1,44 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class Board < ActiveRecord::Base
include Redmine::SafeAttributes
belongs_to :project
has_many :topics, :class_name => 'Message', :conditions => "#{Message.table_name}.parent_id IS NULL", :order => "#{Message.table_name}.created_on DESC"
has_many :messages, :dependent => :destroy, :order => "#{Message.table_name}.created_on DESC"
belongs_to :last_message, :class_name => 'Message', :foreign_key => :last_message_id
acts_as_list :scope => :project_id
acts_as_watchable
validates_presence_of :name, :description
validates_length_of :name, :maximum => 30
validates_length_of :description, :maximum => 255
named_scope :visible, lambda {|*args| { :include => :project,
:conditions => Project.allowed_to_condition(args.shift || User.current, :view_messages, *args) } }
safe_attributes 'name', 'description', 'move_to'
def visible?(user=User.current)
!user.nil? && user.allowed_to?(:view_messages, project)
end
def to_s
name
end
def reset_counters!
self.class.reset_counters!(id)
end
# Updates topics_count, messages_count and last_message_id attributes for +board_id+
def self.reset_counters!(board_id)
board_id = board_id.to_i

View File

@@ -20,13 +20,12 @@ class Change < ActiveRecord::Base
validates_presence_of :changeset_id, :action, :path
before_save :init_path
before_validation :replace_invalid_utf8_of_path
def relative_path
changeset.repository.relative_path(path)
end
def replace_invalid_utf8_of_path
def before_validation
self.path = Redmine::CodesetUtil.replace_invalid_utf8(self.path)
self.from_path = Redmine::CodesetUtil.replace_invalid_utf8(self.from_path)
end

View File

@@ -22,19 +22,11 @@ class Changeset < ActiveRecord::Base
belongs_to :user
has_many :changes, :dependent => :delete_all
has_and_belongs_to_many :issues
has_and_belongs_to_many :parents,
:class_name => "Changeset",
:join_table => "#{table_name_prefix}changeset_parents#{table_name_suffix}",
:association_foreign_key => 'parent_id', :foreign_key => 'changeset_id'
has_and_belongs_to_many :children,
:class_name => "Changeset",
:join_table => "#{table_name_prefix}changeset_parents#{table_name_suffix}",
:association_foreign_key => 'changeset_id', :foreign_key => 'parent_id'
acts_as_event :title => Proc.new {|o| o.title},
acts_as_event :title => Proc.new {|o| "#{l(:label_revision)} #{o.format_identifier}" + (o.short_comments.blank? ? '' : (': ' + o.short_comments))},
:description => :long_comments,
:datetime => :committed_on,
:url => Proc.new {|o| {:controller => 'repositories', :action => 'revision', :id => o.repository.project, :repository_id => o.repository.identifier_param, :rev => o.identifier}}
:url => Proc.new {|o| {:controller => 'repositories', :action => 'revision', :id => o.repository.project, :rev => o.identifier}}
acts_as_searchable :columns => 'comments',
:include => {:repository => :project},
@@ -52,9 +44,6 @@ class Changeset < ActiveRecord::Base
named_scope :visible, lambda {|*args| { :include => {:repository => :project},
:conditions => Project.allowed_to_condition(args.shift || User.current, :view_changesets, *args) } }
after_create :scan_for_issues
before_create :before_create_cs
def revision=(r)
write_attribute :revision, (r.nil? ? nil : r.to_s)
end
@@ -90,14 +79,14 @@ class Changeset < ActiveRecord::Base
user || committer.to_s.split('<').first
end
def before_create_cs
def before_create
self.committer = self.class.to_utf8(self.committer, repository.repo_log_encoding)
self.comments = self.class.normalize_comments(
self.comments, repository.repo_log_encoding)
self.user = repository.find_committer_user(self.committer)
end
def scan_for_issues
def after_create
scan_comment_for_issue_ids
end
@@ -151,26 +140,12 @@ class Changeset < ActiveRecord::Base
@long_comments || split_comments.last
end
def text_tag(ref_project=nil)
tag = if scmid?
def text_tag
if scmid?
"commit:#{scmid}"
else
"r#{revision}"
end
if repository && repository.identifier.present?
tag = "#{repository.identifier}|#{tag}"
end
if ref_project && project && ref_project != project
tag = "#{project.identifier}:#{tag}"
end
tag
end
# Returns the title used for the changeset in the activity/search results
def title
repo = (repository && repository.identifier.present?) ? " (#{repository.identifier})" : ''
comm = short_comments.blank? ? '' : (': ' + short_comments)
"#{l(:label_revision)} #{format_identifier}#{repo}#{comm}"
end
# Returns the previous changeset
@@ -198,14 +173,14 @@ class Changeset < ActiveRecord::Base
:from_revision => change[:from_revision])
end
private
# Finds an issue that can be referenced by the commit message
# i.e. an issue that belong to the repository project, a subproject or a parent project
def find_referenced_issue_by_id(id)
return nil if id.blank?
issue = Issue.find_by_id(id.to_i, :include => :project)
if Setting.commit_cross_project_ref?
# all issues can be referenced/fixed
elsif issue
# issue that belong to the repository project, a subproject or a parent project only
if issue
unless issue.project &&
(project == issue.project || project.is_ancestor_of?(issue.project) ||
project.is_descendant_of?(issue.project))
@@ -215,8 +190,6 @@ class Changeset < ActiveRecord::Base
issue
end
private
def fix_issue(issue)
status = IssueStatus.find_by_id(Setting.commit_fix_status_id.to_i)
if status.nil?
@@ -229,7 +202,7 @@ class Changeset < ActiveRecord::Base
# don't change the status is the issue is closed
return if issue.status && issue.status.is_closed?
journal = issue.init_journal(user || User.anonymous, ll(Setting.default_language, :text_status_changed_by_changeset, text_tag(issue.project)))
journal = issue.init_journal(user || User.anonymous, ll(Setting.default_language, :text_status_changed_by_changeset, text_tag))
issue.status = status
unless Setting.commit_fix_done_ratio.blank?
issue.done_ratio = Setting.commit_fix_done_ratio.to_i
@@ -248,7 +221,7 @@ class Changeset < ActiveRecord::Base
:hours => hours,
:issue => issue,
:spent_on => commit_date,
:comments => l(:text_time_logged_by_changeset, :value => text_tag(issue.project),
:comments => l(:text_time_logged_by_changeset, :value => text_tag,
:locale => Setting.default_language)
)
time_entry.activity = log_time_activity unless log_time_activity.nil?
@@ -280,6 +253,39 @@ class Changeset < ActiveRecord::Base
end
def self.to_utf8(str, encoding)
Redmine::CodesetUtil.to_utf8(str, encoding)
return str if str.nil?
str.force_encoding("ASCII-8BIT") if str.respond_to?(:force_encoding)
if str.empty?
str.force_encoding("UTF-8") if str.respond_to?(:force_encoding)
return str
end
enc = encoding.blank? ? "UTF-8" : encoding
if str.respond_to?(:force_encoding)
if enc.upcase != "UTF-8"
str.force_encoding(enc)
str = str.encode("UTF-8", :invalid => :replace,
:undef => :replace, :replace => '?')
else
str.force_encoding("UTF-8")
if ! str.valid_encoding?
str = str.encode("US-ASCII", :invalid => :replace,
:undef => :replace, :replace => '?').encode("UTF-8")
end
end
else
ic = Iconv.new('UTF-8', enc)
txtar = ""
begin
txtar += ic.iconv(str)
rescue Iconv::IllegalSequence
txtar += $!.success
str = '?' + $!.failed[1,$!.failed.length]
retry
rescue
txtar += $!.success
end
str = txtar
end
str
end
end

View File

@@ -1,26 +1,23 @@
# Redmine - project management software
# Copyright (C) 2006-2011 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class Comment < ActiveRecord::Base
include Redmine::SafeAttributes
belongs_to :commented, :polymorphic => true, :counter_cache => true
belongs_to :author, :class_name => 'User', :foreign_key => 'author_id'
validates_presence_of :commented, :author, :comments
safe_attributes 'comments'
end

View File

@@ -5,12 +5,12 @@
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

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