Compare commits

...
116 Commits
Author SHA1 Message Date
Jean-Philippe Lang 13ec81598b Docs updated for 0.6.4 release.
git-svn-id: http://redmine.rubyforge.org/svn/branches/0.6-stable@1233 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-03-12 18:06:43 +00:00
Jean-Philippe Lang a677817003 Merged r1231 from trunk.
git-svn-id: http://redmine.rubyforge.org/svn/branches/0.6-stable@1232 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-03-12 17:57:46 +00:00
Jean-Philippe Lang abaeecbaa9 Merged Mercurial browsing fix from trunk (r1228 and r1229).
git-svn-id: http://redmine.rubyforge.org/svn/branches/0.6-stable@1230 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-03-12 17:54:50 +00:00
Jean-Philippe Lang 75d652d9e5 Ported r1169 from trunk.
git-svn-id: http://redmine.rubyforge.org/svn/branches/0.6-stable@1170 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-02-22 17:28:17 +00:00
Jean-Philippe Lang 7e9dd78de8 Ported r1034 and r1140 from trunk.
git-svn-id: http://redmine.rubyforge.org/svn/branches/0.6-stable@1141 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-02-12 21:14:58 +00:00
Jean-Philippe Lang bd7542cf98 Merged r1015, r1019, r1022 to r1025, r1045 and r1047 from trunk.
git-svn-id: http://redmine.rubyforge.org/svn/branches/0.6-stable@1048 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-01-07 20:46:54 +00:00
Jean-Philippe Lang a2c18b2037 Ported r1036 from trunk.
git-svn-id: http://redmine.rubyforge.org/svn/branches/0.6-stable@1041 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-01-05 13:12:30 +00:00
Jean-Philippe Lang eb86e5e7e1 Changes for 0.6.3 release.
git-svn-id: http://redmine.rubyforge.org/svn/branches/0.6-stable@1010 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-12-18 18:12:18 +00:00
Jean-Philippe Lang fc26668cd9 Fixed: upload doesn't work in "Files" section.
git-svn-id: http://redmine.rubyforge.org/svn/branches/0.6-stable@1009 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-12-18 18:04:10 +00:00
Jean-Philippe Lang 2b42e3fc50 Added 0.6 stable branch.
git-svn-id: http://redmine.rubyforge.org/svn/branches/0.6-stable@1008 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-12-18 17:53:13 +00:00
Jean-Philippe Lang b4eafd9ea8 Changes for 0.6.2 release.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1004 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-12-16 14:03:26 +00:00
Jean-Philippe Lang 2bcd448dda Updated Japanese translation (Satoru Kurashiki).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1003 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-12-16 13:33:15 +00:00
Nicolas Chuche 1af9c47a27 bug when using apache authentication method
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1002 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-12-15 12:23:39 +00:00
Jean-Philippe Lang ea35fff5bf SCM adapters: moved Errno::ENOENT exception rescuing to the abstract adapter.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1001 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-12-15 12:14:40 +00:00
Jean-Philippe Lang 124ef65c1f Fixed warning: toplevel constant User referenced by WikiContent::User
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1000 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-12-15 11:57:48 +00:00
Jean-Philippe Lang 361c50c1f4 Added some functional tests (wiki).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@999 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-12-15 11:57:06 +00:00
Jean-Philippe Lang 27d6334afa Fixed: Unable to create a wiki (Rails 2.0 compatibility).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@998 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-12-14 22:00:03 +00:00
Jean-Philippe Lang 963b1283c2 Fixed Bazaar test repository path.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@997 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-12-14 21:49:23 +00:00
Jean-Philippe Lang 777edc13c1 Fixed: can not save numeric, date and boolean custom fields (broken by r994).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@996 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-12-14 21:47:57 +00:00
Jean-Philippe Lang d1a3fbea40 Fixed Trac importer error with Rails 2.0: readonly? is defined by ActiveRecord.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@995 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-12-14 21:31:30 +00:00
Jean-Philippe Lang 58610ec52a Search engine: issue custom fields can now be searched.
Each issue custom field (excepting numeric, date and boolean fields) can be marked as "Searchable" (default to false).

git-svn-id: http://redmine.rubyforge.org/svn/trunk@994 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-12-14 18:54:55 +00:00
Jean-Philippe Lang 38b185f1dc Fixed: empty lines when displaying repository files with Windows style eol.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@993 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-12-14 17:48:11 +00:00
Jean-Philippe Lang e69631f26c Fixed: missing body closing tag in repository annotate and entry views.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@992 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-12-14 17:47:30 +00:00
Jean-Philippe Lang 47f399104b Added a Mercurial test repository with unit and functional tests.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@991 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-12-14 17:46:45 +00:00
Jean-Philippe Lang 86319feef2 Added ApplicationController#attach_files as a common method to attach files in all actions.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@990 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-12-14 17:33:05 +00:00
Jean-Philippe Lang eb7cbd481e Added some tests for projects controller and helper.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@989 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-12-14 17:31:24 +00:00
Jean-Philippe Lang 17c7886791 Removed unused UsersController#destroy.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@988 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-12-13 18:53:34 +00:00
Jean-Philippe Lang 48949f979a Added some functional tests and a CVS test repository.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@987 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-12-13 18:52:09 +00:00
Jean-Philippe Lang 86d756d22d News comments are now textilized.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@986 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-12-12 22:57:20 +00:00
Jean-Philippe Lang ab4ff48abc Added /coverage and /vendor/rails to svn-ignore.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@985 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-12-12 22:23:34 +00:00
Jean-Philippe Lang 63ef594033 Added some functional tests (issues).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@984 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-12-12 22:22:33 +00:00
Jean-Philippe Lang bd8eded670 Fixed: 500 error when validation fails on issue edition with no custom fields.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@983 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-12-12 21:10:04 +00:00
Jean-Philippe Lang d5cc40c9b6 Fixed: calendar and gantt broken with Rails 2.0
git-svn-id: http://redmine.rubyforge.org/svn/trunk@982 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-12-12 21:03:36 +00:00
Jean-Philippe Lang 76ed8cc200 Added some functional tests (projects and repositories).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@981 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-12-12 20:56:22 +00:00
Jean-Philippe Lang 3539bef96b Updated Chinese translation (Shortie Lo).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@980 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-12-12 20:49:02 +00:00
Jean-Philippe Lang e29539df9c Fixed: 'assigned to me' filter broken.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@979 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-12-12 20:45:32 +00:00
Jean-Philippe Lang 7e9c454478 Fixed: 'LDAP account password is too long' error when leaving the field empty on creation.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@977 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-12-10 20:58:24 +00:00
Jean-Philippe Lang c00b8fd12e Doc and version changes for 0.6.1 release.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@976 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-12-10 18:39:57 +00:00
Jean-Philippe Lang 6d9490ddcc Merged Rails 2.0 compatibility changes.
Compatibility with Rails 1.2 is preserved.

git-svn-id: http://redmine.rubyforge.org/svn/trunk@975 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-12-10 17:58:07 +00:00
Jean-Philippe Lang f58db70bde More detailed html title on several views.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@964 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-12-07 18:42:40 +00:00
Jean-Philippe Lang 51adc242a3 Updated Polish translation (Mariusz Olejnik).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@963 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-12-07 17:58:44 +00:00
Jean-Philippe Lang 7fae4b0892 Bazaar adapter: fixed log with partial revisions parsing.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@962 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-12-07 17:51:54 +00:00
Jean-Philippe Lang 6239e319ac Fixed versions/show layout with IE.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@961 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-12-07 15:02:57 +00:00
Jean-Philippe Lang 60c82a03db Fixed drop down lists overflow on My account.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@960 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-12-07 14:57:17 +00:00
Jean-Philippe Lang 36c0ab196c Added Traditional Chinese translation (by Shortie Lo).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@959 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-12-07 14:43:07 +00:00
Jean-Philippe Lang a7c2b63fb6 Transaction and performance improvement on workflow copy when creating a new tracker.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@958 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-12-07 13:35:38 +00:00
Jean-Philippe Lang 5e38bd9363 Performance improvement on workflow setup screen.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@957 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-12-07 11:19:30 +00:00
Jean-Philippe Lang a79cf8d574 Fixed a parenthesis warning in news/show.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@956 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-12-07 10:27:52 +00:00
Jean-Philippe Lang 3c44aac1f5 Added version details view accessible from the roadmap.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@955 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-12-07 10:26:07 +00:00
Jean-Philippe Lang 355289fa67 Roadmap progress bars now differentiate the progress due to closed issues from the open issues progress (2 different colors).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@954 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-12-05 19:21:25 +00:00
Jean-Philippe Lang b536f4c657 Restored wiki syntax quick ref pop-up (accessible from wiki/edit).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@953 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-12-04 18:14:09 +00:00
Jean-Philippe Lang d00014221e Changesets retrieval optimization on the activity view. Prevents additional query from being executed for each displayed changeset.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@952 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-12-03 23:05:45 +00:00
Jean-Philippe Lang 3b4cfe0ba8 Added some unit tests for the Bazaar adapter.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@951 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-12-03 19:19:36 +00:00
Jean-Philippe Lang 056e3703da Added Bazaar adapter.
Fixed 'quick jump to a revision' form on the revisions list.

git-svn-id: http://redmine.rubyforge.org/svn/trunk@950 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-12-03 17:40:43 +00:00
Jean-Philippe Lang 92a23c05bb Project name format limitation removed (name can now contain any character).
Project identifier maximum length changed from 12 to 20.

git-svn-id: http://redmine.rubyforge.org/svn/trunk@949 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-12-03 10:28:08 +00:00
Jean-Philippe Lang 7d8af70a63 Changed the maximum length of LDAP account to 255 characters.
Added length validations on AuthSource model.

git-svn-id: http://redmine.rubyforge.org/svn/trunk@948 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-12-02 22:07:56 +00:00
Jean-Philippe Lang 8c65cc4712 Added Annotate/Blame view for Subversion, CVS and Mercurial repositories.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@947 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-12-02 20:58:02 +00:00
Jean-Philippe Lang e4724c7626 Trac importer:
* should now support mysql and postgresql Trac database
* minor fixes

git-svn-id: http://redmine.rubyforge.org/svn/trunk@946 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-12-02 15:09:43 +00:00
Jean-Philippe Lang 6e74a06808 Fixed: error on admin/info if there's more than 1 plugin installed.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@945 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-12-02 14:20:28 +00:00
Jean-Philippe Lang bc060b31ae Email notifications are now sent as Blind carbon copy by default. This can be changed in email notifications settings (new setting added).
Emission email address setting moved to the email notifications settings view.

git-svn-id: http://redmine.rubyforge.org/svn/trunk@944 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-12-02 13:52:16 +00:00
Jean-Philippe Lang aebcfb1eda When creating a new role, permissions are pre-filled with 'Non member' role permissions.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@943 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-12-02 12:58:07 +00:00
Jean-Philippe Lang 457c9a8e72 Fixed: svn or ldap password can be found in clear text in the html source in editing mode.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@942 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-12-01 22:03:45 +00:00
Jean-Philippe Lang 3f2f7153a9 Fixed: Date and time formats defined in settings not applied to the issues CSV export.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@941 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-12-01 21:34:16 +00:00
Jean-Philippe Lang f5d68cf688 Fixed a broken link in the SCM browser
git-svn-id: http://redmine.rubyforge.org/svn/trunk@940 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-12-01 21:33:15 +00:00
Jean-Philippe Lang 81ada666bb 'Assigned to' drop down list is now sorted by user's lastname.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@939 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-12-01 17:42:26 +00:00
Jean-Philippe Lang db002edabd * Added links to previous and next revisions on revision view (patch by Cyril Mougel slightly edited)
* Fixed TimelogController#report december error
* Fixed ProjectsControllerTest#test_activity 1st and 2nd day of the month failure

git-svn-id: http://redmine.rubyforge.org/svn/trunk@938 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-12-01 17:15:42 +00:00
Jean-Philippe Lang 3baf086e2d Fixed: 'View all issues' link doesn't work on issues/show.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@937 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-11-29 20:14:01 +00:00
Jean-Philippe Lang 2cf11bd64e Fixed Mantis importer: projects trackers and modules assignment
Fixed Trac and Mantis importers: roles assignments

git-svn-id: http://redmine.rubyforge.org/svn/trunk@936 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-11-29 20:08:14 +00:00
Jean-Philippe Lang bf6e02c739 Search engine: search can be restricted to an exact phrase by using quotation marks (eg. hello "bye bye" can be used to search for "hello" and "bye bye" strings).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@935 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-11-29 18:33:42 +00:00
Jean-Philippe Lang 233990dac3 * Updated German translation (Thomas Löber)
* Updated Spanish translation (Gumer Coronel Pérez)
* Fixed: test in method project in Attachment Model useless (Cyril Mougel)
* Fixed: public/.htaccess lacks support for mod_fcgid and adds handlers without checking for their existance (Nils Adermann)

git-svn-id: http://redmine.rubyforge.org/svn/trunk@934 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-11-27 17:20:57 +00:00
Jean-Philippe Lang c383486d71 Added custom fields marked as "For all projects" to the csv export of the cross project issue list.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@933 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-11-27 17:04:13 +00:00
Jean-Philippe Lang 508b0bbb8e * Updated Spanish translation (Gumer Coronel Pérez)
* Fixed mailer test errors

git-svn-id: http://redmine.rubyforge.org/svn/trunk@932 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-11-26 18:47:49 +00:00
Jean-Philippe Lang 3937caeb3c Trac importer improvements (by Mat Trudel):
* better support for wiki internal links (still not perfect, but much improved)
* support for unordered lists
* support for most of trac's highlighting tags (underline, bold, etc)
* import progress dots now flush to stdout on every dot, so the import doesn't look frozen
* support for migration of multiple trac instances into a single Redmine install (as separate projects)

git-svn-id: http://redmine.rubyforge.org/svn/trunk@931 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-11-25 12:11:40 +00:00
Jean-Philippe Lang f2a12d9eb5 Added user status criteria in Redmine.pm
git-svn-id: http://redmine.rubyforge.org/svn/trunk@930 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-11-24 16:55:08 +00:00
Jean-Philippe Lang acad206063 Issue context menu now also available on 'My page'.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@929 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-11-24 15:16:04 +00:00
Jean-Philippe Lang f80f04e379 Slight optimization in User#role_for_project.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@928 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-11-24 15:14:32 +00:00
Jean-Philippe Lang fde4a42e2a Removed the 12 characters limit on passwords.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@927 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-11-24 12:52:29 +00:00
Jean-Philippe Lang 29b3614bcb Forums enhancements:
* messages can now be edited/deleted (explicit permissions need to be given)
* topics can be locked so that no reply can be added (only by users allowed to edit messages)
* topics can be marked as sticky so that they always appear at the top of the list (only by users allowed to edit messages)

git-svn-id: http://redmine.rubyforge.org/svn/trunk@926 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-11-24 12:25:07 +00:00
Jean-Philippe Lang 866e9e2503 Fixed: error on account/register when validation fails.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@925 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-11-23 23:23:39 +00:00
Jean-Philippe Lang c21ee95ade * Updated Russian translation (iGor kMeta)
* Fixed front page typo: View all issues instead of View all news (Derek Montgomery)

git-svn-id: http://redmine.rubyforge.org/svn/trunk@924 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-11-23 18:27:26 +00:00
Jean-Philippe Lang 7704e2d919 Forums: attachments can now be added to replies.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@923 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-11-22 19:19:47 +00:00
Jean-Philippe Lang 0634591b3d Themes:
* Fixed: themes are not found when running Apache+fastcgi
* Added 'Classic' theme (inspired from the v0.51 design)

git-svn-id: http://redmine.rubyforge.org/svn/trunk@922 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-11-22 18:03:02 +00:00
Jean-Philippe Lang 5e6fa147da * Fixed: Error when displaying the issue list if a float custom field is marked as 'used as filter'
* Fixed: Mercurial adapter breaks on missing :files entry in changeset hash (James Britt)
* Fixed: Wrong feed URLs on the home page

git-svn-id: http://redmine.rubyforge.org/svn/trunk@921 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-11-21 18:45:18 +00:00
Jean-Philippe Lang 8d91afc33e Added per-project tracker selection. Trackers can be selected on project settings.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@920 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-11-20 20:29:03 +00:00
Jean-Philippe Lang 987a5aa221 Anonymous users can now be allowed to create, edit, comment issues, comment news and post messages in the forums.
These permissions need to be explicitly given to the Anonymous role (Admin -> Roles & Permissions -> Anonymous).

git-svn-id: http://redmine.rubyforge.org/svn/trunk@919 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-11-20 15:40:16 +00:00
Jean-Philippe Lang 99f9aea80a * Referencing issues in commit messages: enter * in 'Referencing keywords' to link any issue id without using keywords.
* Updated Polish translation (Mariusz Olejnik).

git-svn-id: http://redmine.rubyforge.org/svn/trunk@918 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-11-20 12:07:28 +00:00
Jean-Philippe Lang deb182337d * Added time zone support: users can select their time zone on their account view.
* Updated Polish translation (Mariusz Olejnik).
* Fixed: Projects should be listed with case mixed.

git-svn-id: http://redmine.rubyforge.org/svn/trunk@917 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-11-19 22:28:43 +00:00
Nicolas Chuche a1f3497ec4 * add Redmine.pm to authenticate with mod_perl
* add a --test option in reposman.rb
* change owner right to fit with apache write access to repositories
* add a deprecated warning in reposman.pl


git-svn-id: http://redmine.rubyforge.org/svn/trunk@916 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-11-18 18:51:48 +00:00
Jean-Philippe Lang 9c9ae21771 There's now 3 account activation strategies (available in application settings):
* activation by email: the user receives an email containing a link to active his account
* manual activation: an email is sent to administrators for account approval (default)
* automatic activation: the user can log in as soon as he has registered

git-svn-id: http://redmine.rubyforge.org/svn/trunk@915 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-11-18 17:46:55 +00:00
Jean-Philippe Lang f8aa2dc9b7 'fixed version' field can now be displayed on the issue list.
Category and fixed version fields added to the CSV export.

git-svn-id: http://redmine.rubyforge.org/svn/trunk@914 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-11-18 15:53:58 +00:00
Jean-Philippe Lang bb2de53d93 Added an alternate theme which provides issue list colorization based on issues priority.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@912 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-11-18 14:32:39 +00:00
Jean-Philippe Lang e2c606e974 Fixed: Update of time entry fails when the issue has been moved to an other project.
Fixed: Error when moving an issue without changing its tracker (Postgresql).

git-svn-id: http://redmine.rubyforge.org/svn/trunk@909 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-11-17 17:45:21 +00:00
Jean-Philippe Lang 9ad79612fe Roadmap: more accurate completion percentage calculation (done ratio of open issues is now taken into account).
Issues and issues list: 'done ratio' field now displayed as a progress bar.


git-svn-id: http://redmine.rubyforge.org/svn/trunk@908 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-11-17 15:34:10 +00:00
Jean-Philippe Lang 1469a4c8df Fixed (CVS adapter): changes not recorded when using :pserver string.
Updated Russian js calendar (iGor kMeta).

git-svn-id: http://redmine.rubyforge.org/svn/trunk@907 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-11-16 21:24:22 +00:00
Jean-Philippe Lang 76cdef46f2 Fixed: localization problem on issue list headers.
Updated Russian translation (iGor kMeta).

git-svn-id: http://redmine.rubyforge.org/svn/trunk@906 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-11-16 20:46:33 +00:00
Jean-Philippe Lang e4ce95c3a1 Added a couple of new formats for the 'date format' setting.
Added a 'time format' setting.

git-svn-id: http://redmine.rubyforge.org/svn/trunk@905 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-11-16 20:26:36 +00:00
Jean-Philippe Lang 4951676172 Added Russian translation (iGor kMeta).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@904 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-11-15 18:24:18 +00:00
Jean-Philippe Lang b0a8888e35 Fixed: admin should be able to move issues to any project.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@903 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-11-14 21:54:16 +00:00
Jean-Philippe Lang 833c7bd659 Fixed: adding an attachment is not possible when changing the status of an issue.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@902 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-11-12 17:29:41 +00:00
Jean-Philippe Lang 5944696b6d Custom fields can now be reordered.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@901 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-11-12 17:23:14 +00:00
Jean-Philippe Lang a727f0d25a Removed hard coded string on 'My account'.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@900 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-11-12 16:47:07 +00:00
Jean-Philippe Lang 0fe5c7b3e0 Added an option on 'My account' for users who don't want to be notified of changes that they make.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@899 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-11-12 16:43:49 +00:00
Jean-Philippe Lang 843d04f0e3 Fixed: No mime-types in documents/files downloading
git-svn-id: http://redmine.rubyforge.org/svn/trunk@898 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-11-12 15:13:42 +00:00
Jean-Philippe Lang 8a8f819d27 Added wiki macros support. 2 builtin macros are defined: hello_world (sample macro that displays the arguments) and macro_list (display the list of installed macros).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@897 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-11-12 14:36:33 +00:00
Jean-Philippe Lang a8419c1425 Fixed: error on activity page when displaying a document (undefined method 'author').
git-svn-id: http://redmine.rubyforge.org/svn/trunk@896 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-11-12 08:00:00 +00:00
Jean-Philippe Lang 362364fd6f Fixed: CSV constant warning
git-svn-id: http://redmine.rubyforge.org/svn/trunk@895 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-11-11 20:55:27 +00:00
Jean-Philippe Lang b0bb1baeaa Trac importer: user can now choose between sqlite and sqlite3 adapter for Trac database.
Trac importer: issues and wiki modules are enabled by default for the imported project.
Fixed: 404 error when trying to save a custom query under certain circumstance.


git-svn-id: http://redmine.rubyforge.org/svn/trunk@894 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-11-09 13:32:14 +00:00
Jean-Philippe Lang 1454a711ca Diff style (inline or side by side) automatically saved as a user preference.
Fixed a Postgres test failure.

git-svn-id: http://redmine.rubyforge.org/svn/trunk@893 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-11-08 19:14:34 +00:00
Jean-Philippe Lang fa95501fe5 Added issues status changes on the activity view (initial patch by Cyril Mougel).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@892 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-11-08 19:00:37 +00:00
Jean-Philippe Lang a069c4afcf Added forums topics on the activity view (disabled by default).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@891 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-11-07 23:01:24 +00:00
Jean-Philippe Lang cc8220dde8 Fixed: error when sorting the messages if there's only one board for the project.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@890 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-11-07 21:21:12 +00:00
Jean-Philippe Lang 63ceea2e21 Custom fields can now be displayed as columns on the issue list.
Custom fields marked as "for all projects" can be added to the default columns of the issue list (in application settings).
Project specific custom fields can be displayed on custom queries.

git-svn-id: http://redmine.rubyforge.org/svn/trunk@889 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-11-07 20:42:28 +00:00
Jean-Philippe Lang ad68a82be1 Moved ProjectsController#list_news to NewsController#index.
Removed FeedsController, issues and news feeds are now handled by issues and news controllers.

git-svn-id: http://redmine.rubyforge.org/svn/trunk@888 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-11-05 22:22:51 +00:00
Jean-Philippe Lang 8509cf80f0 ProjectsController#list_issues, #export_issues_csv and #export_issues_pdf merged into IssuesController#index
git-svn-id: http://redmine.rubyforge.org/svn/trunk@887 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-11-05 18:38:42 +00:00
Jean-Philippe Lang 26a1ae4808 Fixed: <td> closed with a </th>
git-svn-id: http://redmine.rubyforge.org/svn/trunk@886 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-11-05 17:33:28 +00:00
Jean-Philippe Lang 040cfa6ae2 Fixed: <<me>> doesn't appear in the drop down filters on a project issue list.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@885 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-11-05 17:30:45 +00:00
344 changed files with 14674 additions and 1748 deletions
+48 -26
View File
@@ -21,7 +21,7 @@ class AccountController < ApplicationController
include CustomFieldsHelper include CustomFieldsHelper
# prevents login action to be filtered by check_if_login_required application scope filter # prevents login action to be filtered by check_if_login_required application scope filter
skip_before_filter :check_if_login_required, :only => [:login, :lost_password, :register] skip_before_filter :check_if_login_required, :only => [:login, :lost_password, :register, :activate]
# Show user's account # Show user's account
def show def show
@@ -30,7 +30,7 @@ class AccountController < ApplicationController
# show only public projects and private projects that the logged in user is also a member of # show only public projects and private projects that the logged in user is also a member of
@memberships = @user.memberships.select do |membership| @memberships = @user.memberships.select do |membership|
membership.project.is_public? || (User.current.role_for_project(membership.project)) membership.project.is_public? || (User.current.member_of?(membership.project))
end end
rescue ActiveRecord::RecordNotFound rescue ActiveRecord::RecordNotFound
render_404 render_404
@@ -106,40 +106,62 @@ class AccountController < ApplicationController
# User self-registration # User self-registration
def register def register
redirect_to(home_url) && return unless Setting.self_registration? redirect_to(home_url) && return unless Setting.self_registration?
if params[:token] if request.get?
token = Token.find_by_action_and_value("register", params[:token]) @user = User.new(:language => Setting.default_language)
redirect_to(home_url) && return unless token and !token.expired? @custom_values = UserCustomField.find(:all).collect { |x| CustomValue.new(:custom_field => x, :customized => @user) }
user = token.user
redirect_to(home_url) && return unless user.status == User::STATUS_REGISTERED
user.status = User::STATUS_ACTIVE
if user.save
token.destroy
flash[:notice] = l(:notice_account_activated)
redirect_to :action => 'login'
return
end
else else
if request.get? @user = User.new(params[:user])
@user = User.new(:language => Setting.default_language) @user.admin = false
@custom_values = UserCustomField.find(:all).collect { |x| CustomValue.new(:custom_field => x, :customized => @user) } @user.login = params[:user][:login]
else @user.status = User::STATUS_REGISTERED
@user = User.new(params[:user]) @user.password, @user.password_confirmation = params[:password], params[:password_confirmation]
@user.admin = false @custom_values = UserCustomField.find(:all).collect { |x| CustomValue.new(:custom_field => x,
@user.login = params[:user][:login] :customized => @user,
@user.status = User::STATUS_REGISTERED :value => (params["custom_fields"] ? params["custom_fields"][x.id.to_s] : nil)) }
@user.password, @user.password_confirmation = params[:password], params[:password_confirmation] @user.custom_values = @custom_values
@custom_values = UserCustomField.find(:all).collect { |x| CustomValue.new(:custom_field => x, :customized => @user, :value => params["custom_fields"][x.id.to_s]) } case Setting.self_registration
@user.custom_values = @custom_values when '1'
# Email activation
token = Token.new(:user => @user, :action => "register") token = Token.new(:user => @user, :action => "register")
if @user.save and token.save if @user.save and token.save
Mailer.deliver_register(token) Mailer.deliver_register(token)
flash[:notice] = l(:notice_account_register_done) flash[:notice] = l(:notice_account_register_done)
redirect_to :controller => 'account', :action => 'login' redirect_to :action => 'login'
end
when '3'
# Automatic activation
@user.status = User::STATUS_ACTIVE
if @user.save
flash[:notice] = l(:notice_account_activated)
redirect_to :action => 'login'
end
else
# Manual activation by the administrator
if @user.save
# Sends an email to the administrators
Mailer.deliver_account_activation_request(@user)
flash[:notice] = l(:notice_account_pending)
redirect_to :action => 'login'
end end
end end
end end
end end
# Token based account activation
def activate
redirect_to(home_url) && return unless Setting.self_registration? && params[:token]
token = Token.find_by_action_and_value('register', params[:token])
redirect_to(home_url) && return unless token and !token.expired?
user = token.user
redirect_to(home_url) && return unless user.status == User::STATUS_REGISTERED
user.status = User::STATUS_ACTIVE
if user.save
token.destroy
flash[:notice] = l(:notice_account_activated)
end
redirect_to :action => 'login'
end
private private
def logged_user=(user) def logged_user=(user)
if user && user.is_a?(User) if user && user.is_a?(User)
+3 -2
View File
@@ -48,8 +48,9 @@ class AdminController < ApplicationController
def mail_options def mail_options
@notifiables = %w(issue_added issue_updated news_added document_added file_added message_posted) @notifiables = %w(issue_added issue_updated news_added document_added file_added message_posted)
if request.post? if request.post?
Setting.notified_events = (params[:notified_events] || []) settings = (params[:settings] || {}).dup.symbolize_keys
Setting.emails_footer = params[:emails_footer] if params[:emails_footer] settings[:notified_events] ||= []
settings.each { |name, value| Setting[name] = value }
flash[:notice] = l(:notice_successful_update) flash[:notice] = l(:notice_successful_update)
redirect_to :controller => 'admin', :action => 'mail_options' redirect_to :controller => 'admin', :action => 'mail_options'
end end
+13 -4
View File
@@ -23,10 +23,6 @@ class ApplicationController < ActionController::Base
require_dependency "repository/#{scm.underscore}" require_dependency "repository/#{scm.underscore}"
end end
def logged_in_user
User.current.logged? ? User.current : nil
end
def current_role def current_role
@current_role ||= User.current.role_for_project(@project) @current_role ||= User.current.role_for_project(@project)
end end
@@ -148,6 +144,19 @@ class ApplicationController < ActionController::Base
def accept_key_auth_actions def accept_key_auth_actions
self.class.read_inheritable_attribute('accept_key_auth_actions') || [] self.class.read_inheritable_attribute('accept_key_auth_actions') || []
end end
# TODO: move to model
def attach_files(obj, files)
attachments = []
if files && files.is_a?(Array)
files.each do |file|
next unless file.size > 0
a = Attachment.create(:container => obj, :file => file, :author => User.current)
attachments << a unless a.new_record?
end
end
attachments
end
# qvalues http header parser # qvalues http header parser
# code taken from webrick # code taken from webrick
+2 -3
View File
@@ -32,7 +32,6 @@ class BoardsController < ApplicationController
if @boards.size == 1 if @boards.size == 1
@board = @boards.first @board = @boards.first
show show
render :action => 'show'
end end
end end
@@ -42,11 +41,11 @@ class BoardsController < ApplicationController
@topic_count = @board.topics.count @topic_count = @board.topics.count
@topic_pages = Paginator.new self, @topic_count, 25, params['page'] @topic_pages = Paginator.new self, @topic_count, 25, params['page']
@topics = @board.topics.find :all, :order => sort_clause, @topics = @board.topics.find :all, :order => "#{Message.table_name}.sticky DESC, #{sort_clause}",
:include => [:author, {:last_reply => :author}], :include => [:author, {:last_reply => :author}],
:limit => @topic_pages.items_per_page, :limit => @topic_pages.items_per_page,
:offset => @topic_pages.current.offset :offset => @topic_pages.current.offset
render :action => 'show', :layout => false if request.xhr? render :action => 'show', :layout => !request.xhr?
end end
verify :method => :post, :only => [ :destroy ], :redirect_to => { :action => :index } verify :method => :post, :only => [ :destroy ], :redirect_to => { :action => :index }
+18 -3
View File
@@ -45,7 +45,7 @@ class CustomFieldsController < ApplicationController
end end
if request.post? and @custom_field.save if request.post? and @custom_field.save
flash[:notice] = l(:notice_successful_create) flash[:notice] = l(:notice_successful_create)
redirect_to :action => 'list', :tab => @custom_field.type redirect_to :action => 'list', :tab => @custom_field.class.name
end end
@trackers = Tracker.find(:all, :order => 'position') @trackers = Tracker.find(:all, :order => 'position')
end end
@@ -57,14 +57,29 @@ class CustomFieldsController < ApplicationController
@custom_field.trackers = params[:tracker_ids] ? Tracker.find(params[:tracker_ids]) : [] @custom_field.trackers = params[:tracker_ids] ? Tracker.find(params[:tracker_ids]) : []
end end
flash[:notice] = l(:notice_successful_update) flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'list', :tab => @custom_field.type redirect_to :action => 'list', :tab => @custom_field.class.name
end end
@trackers = Tracker.find(:all, :order => 'position') @trackers = Tracker.find(:all, :order => 'position')
end end
def move
@custom_field = CustomField.find(params[:id])
case params[:position]
when 'highest'
@custom_field.move_to_top
when 'higher'
@custom_field.move_higher
when 'lower'
@custom_field.move_lower
when 'lowest'
@custom_field.move_to_bottom
end if params[:position]
redirect_to :action => 'list', :tab => @custom_field.class.name
end
def destroy def destroy
@custom_field = CustomField.find(params[:id]).destroy @custom_field = CustomField.find(params[:id]).destroy
redirect_to :action => 'list', :tab => @custom_field.type redirect_to :action => 'list', :tab => @custom_field.class.name
rescue rescue
flash[:error] = "Unable to delete custom field" flash[:error] = "Unable to delete custom field"
redirect_to :action => 'list' redirect_to :action => 'list'
+3 -9
View File
@@ -39,20 +39,14 @@ class DocumentsController < ApplicationController
def download def download
@attachment = @document.attachments.find(params[:attachment_id]) @attachment = @document.attachments.find(params[:attachment_id])
@attachment.increment_download @attachment.increment_download
send_file @attachment.diskfile, :filename => @attachment.filename send_file @attachment.diskfile, :filename => @attachment.filename, :type => @attachment.content_type
rescue rescue
render_404 render_404
end end
def add_attachment def add_attachment
# Save the attachments attachments = attach_files(@document, params[:attachments])
@attachments = [] Mailer.deliver_attachments_added(attachments) if !attachments.empty? && Setting.notified_events.include?('document_added')
params[:attachments].each { |file|
next unless file.size > 0
a = Attachment.create(:container => @document, :file => file, :author => logged_in_user)
@attachments << a unless a.new_record?
} if params[:attachments] and params[:attachments].is_a? Array
Mailer.deliver_attachments_added(@attachments) if !@attachments.empty? && Setting.notified_events.include?('document_added')
redirect_to :action => 'show', :id => @document redirect_to :action => 'show', :id => @document
end end
-98
View File
@@ -1,98 +0,0 @@
# 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 FeedsController < ApplicationController
before_filter :find_scope
session :off
helper :issues
include IssuesHelper
helper :custom_fields
include CustomFieldsHelper
# news feeds
def news
News.with_scope(:find => @find_options) do
@news = News.find :all, :order => "#{News.table_name}.created_on DESC", :include => [ :author, :project ]
end
headers["Content-Type"] = "application/rss+xml"
render :action => 'news_atom' if 'atom' == params[:format]
end
# issue feeds
def issues
if @project && params[:query_id]
query = Query.find(params[:query_id])
query.executed_by = @user
# ignore query if it's not valid
query = nil unless query.valid?
# override with query conditions
@find_options[:conditions] = query.statement if query.valid? and @project == query.project
end
Issue.with_scope(:find => @find_options) do
@issues = Issue.find :all, :include => [:project, :author, :tracker, :status],
:order => "#{Issue.table_name}.created_on DESC"
end
@title = (@project ? @project.name : Setting.app_title) + ": " + (query ? query.name : l(:label_reported_issues))
headers["Content-Type"] = "application/rss+xml"
render :action => 'issues_atom' if 'atom' == params[:format]
end
# issue changes feeds
def history
if @project && params[:query_id]
query = Query.find(params[:query_id])
query.executed_by = @user
# ignore query if it's not valid
query = nil unless query.valid?
# override with query conditions
@find_options[:conditions] = query.statement if query.valid? and @project == query.project
end
Journal.with_scope(:find => @find_options) do
@journals = Journal.find :all, :include => [ :details, :user, {:issue => [:project, :author, :tracker, :status]} ],
:order => "#{Journal.table_name}.created_on DESC"
end
@title = (@project ? @project.name : Setting.app_title) + ": " + (query ? query.name : l(:label_changes_details))
headers["Content-Type"] = "application/rss+xml"
render :action => 'history_atom' if 'atom' == params[:format]
end
private
# override for feeds specific authentication
def check_if_login_required
@user = User.find_by_rss_key(params[:key])
render(:nothing => true, :status => 403) and return false if !@user && Setting.login_required?
end
def find_scope
if params[:project_id]
# project feed
# check if project is public or if the user is a member
@project = Project.find(params[:project_id])
render(:nothing => true, :status => 403) and return false unless @project.is_public? || (@user && @user.role_for_project(@project))
scope = ["#{Project.table_name}.id=?", params[:project_id].to_i]
else
# global feed
scope = ["#{Project.table_name}.is_public=?", true]
end
@find_options = {:conditions => scope, :limit => Setting.feeds_limit.to_i}
return true
end
end
+78 -56
View File
@@ -16,9 +16,10 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class IssuesController < ApplicationController class IssuesController < ApplicationController
layout 'base', :except => :export_pdf layout 'base'
before_filter :find_project, :authorize, :except => [:index, :preview] before_filter :find_project, :authorize, :except => [:index, :changes, :preview]
accept_key_auth :index before_filter :find_optional_project, :only => [:index, :changes]
accept_key_auth :index, :changes
cache_sweeper :issue_sweeper, :only => [ :edit, :change_status, :destroy ] cache_sweeper :issue_sweeper, :only => [ :edit, :change_status, :destroy ]
@@ -37,47 +38,65 @@ class IssuesController < ApplicationController
helper :queries helper :queries
helper :sort helper :sort
include SortHelper include SortHelper
include IssuesHelper
def index def index
sort_init "#{Issue.table_name}.id", "desc" sort_init "#{Issue.table_name}.id", "desc"
sort_update sort_update
retrieve_query retrieve_query
if @query.valid? if @query.valid?
@issue_count = Issue.count(:include => [:status, :project], :conditions => @query.statement) limit = %w(pdf csv).include?(params[:format]) ? Setting.issues_export_limit.to_i : 25
@issue_pages = Paginator.new self, @issue_count, 25, params['page'] @issue_count = Issue.count(:include => [:status, :project], :conditions => @query.statement)
@issue_pages = Paginator.new self, @issue_count, limit, params['page']
@issues = Issue.find :all, :order => sort_clause, @issues = Issue.find :all, :order => sort_clause,
:include => [ :assigned_to, :status, :tracker, :project, :priority, :category ], :include => [ :assigned_to, :status, :tracker, :project, :priority, :category, :fixed_version ],
:conditions => @query.statement, :conditions => @query.statement,
:limit => @issue_pages.items_per_page, :limit => limit,
:offset => @issue_pages.current.offset :offset => @issue_pages.current.offset
end respond_to do |format|
respond_to do |format| format.html { render :template => 'issues/index.rhtml', :layout => !request.xhr? }
format.html { render :layout => false if request.xhr? } format.atom { render_feed(@issues, :title => l(:label_issue_plural)) }
format.atom { render_feed(@issues, :title => l(:label_issue_plural)) } format.csv { send_data(issues_to_csv(@issues, @project).read, :type => 'text/csv; header=present', :filename => 'export.csv') }
format.pdf { send_data(render(:template => 'issues/index.rfpdf', :layout => false), :type => 'application/pdf', :filename => 'export.pdf') }
end
else
# Send html if the query is not valid
render(:template => 'issues/index.rhtml', :layout => !request.xhr?)
end end
end end
def changes
sort_init "#{Issue.table_name}.id", "desc"
sort_update
retrieve_query
if @query.valid?
@changes = Journal.find :all, :include => [ :details, :user, {:issue => [:project, :author, :tracker, :status]} ],
:conditions => @query.statement,
:limit => 25,
:order => "#{Journal.table_name}.created_on DESC"
end
@title = (@project ? @project.name : Setting.app_title) + ": " + (@query.new_record? ? l(:label_changes_details) : @query.name)
render :layout => false, :content_type => 'application/atom+xml'
end
def show def show
@custom_values = @issue.custom_values.find(:all, :include => :custom_field) @custom_values = @issue.custom_values.find(:all, :include => :custom_field, :order => "#{CustomField.table_name}.position")
@journals = @issue.journals.find(:all, :include => [:user, :details], :order => "#{Journal.table_name}.created_on ASC") @journals = @issue.journals.find(:all, :include => [:user, :details], :order => "#{Journal.table_name}.created_on ASC")
@status_options = @issue.status.find_new_statuses_allowed_to(User.current.role_for_project(@project), @issue.tracker)
if params[:format]=='pdf' respond_to do |format|
@options_for_rfpdf ||= {} format.html { render :template => 'issues/show.rhtml' }
@options_for_rfpdf[:file_name] = "#{@project.identifier}-#{@issue.id}.pdf" format.pdf { send_data(render(:template => 'issues/show.rfpdf', :layout => false), :type => 'application/pdf', :filename => "#{@project.identifier}-#{@issue.id}.pdf") }
render :template => 'issues/show.rfpdf', :layout => false
else
@status_options = @issue.status.find_new_statuses_allowed_to(logged_in_user.role_for_project(@project), @issue.tracker) if logged_in_user
render :template => 'issues/show.rhtml'
end end
end end
def edit def edit
@priorities = Enumeration::get_values('IPRI') @priorities = Enumeration::get_values('IPRI')
@custom_values = []
if request.get? if request.get?
@custom_values = @project.custom_fields_for_issues(@issue.tracker).collect { |x| @issue.custom_values.find_by_custom_field_id(x.id) || CustomValue.new(:custom_field => x, :customized => @issue) } @custom_values = @project.custom_fields_for_issues(@issue.tracker).collect { |x| @issue.custom_values.find_by_custom_field_id(x.id) || CustomValue.new(:custom_field => x, :customized => @issue) }
else else
begin begin
@issue.init_journal(self.logged_in_user) @issue.init_journal(User.current)
# Retrieve custom fields and values # Retrieve custom fields and values
if params["custom_fields"] if params["custom_fields"]
@custom_values = @project.custom_fields_for_issues(@issue.tracker).collect { |x| CustomValue.new(:custom_field => x, :customized => @issue, :value => params["custom_fields"][x.id.to_s]) } @custom_values = @project.custom_fields_for_issues(@issue.tracker).collect { |x| CustomValue.new(:custom_field => x, :customized => @issue, :value => params["custom_fields"][x.id.to_s]) }
@@ -97,13 +116,8 @@ class IssuesController < ApplicationController
def add_note def add_note
journal = @issue.init_journal(User.current, params[:notes]) journal = @issue.init_journal(User.current, params[:notes])
params[:attachments].each { |file| attachments = attach_files(@issue, params[:attachments])
next unless file.size > 0 attachments.each {|a| journal.details << JournalDetail.new(:property => 'attachment', :prop_key => a.id, :value => a.filename)}
a = Attachment.create(:container => @issue, :file => file, :author => logged_in_user)
journal.details << JournalDetail.new(:property => 'attachment',
:prop_key => a.id,
:value => a.filename) unless a.new_record?
} if params[:attachments] and params[:attachments].is_a? Array
if journal.save if journal.save
flash[:notice] = l(:notice_successful_update) flash[:notice] = l(:notice_successful_update)
Mailer.deliver_issue_edit(journal) if Setting.notified_events.include?('issue_updated') Mailer.deliver_issue_edit(journal) if Setting.notified_events.include?('issue_updated')
@@ -114,25 +128,18 @@ class IssuesController < ApplicationController
end end
def change_status def change_status
@status_options = @issue.status.find_new_statuses_allowed_to(logged_in_user.role_for_project(@project), @issue.tracker) if logged_in_user @status_options = @issue.status.find_new_statuses_allowed_to(User.current.role_for_project(@project), @issue.tracker)
@new_status = IssueStatus.find(params[:new_status_id]) @new_status = IssueStatus.find(params[:new_status_id])
if params[:confirm] if params[:confirm]
begin begin
journal = @issue.init_journal(self.logged_in_user, params[:notes]) journal = @issue.init_journal(User.current, params[:notes])
@issue.status = @new_status @issue.status = @new_status
if @issue.update_attributes(params[:issue]) if @issue.update_attributes(params[:issue])
# Save attachments attachments = attach_files(@issue, params[:attachments])
params[:attachments].each { |file| attachments.each {|a| journal.details << JournalDetail.new(:property => 'attachment', :prop_key => a.id, :value => a.filename)}
next unless file.size > 0
a = Attachment.create(:container => @issue, :file => file, :author => logged_in_user)
journal.details << JournalDetail.new(:property => 'attachment',
:prop_key => a.id,
:value => a.filename) unless a.new_record?
} if params[:attachments] and params[:attachments].is_a? Array
# Log time # Log time
if current_role.allowed_to?(:log_time) if current_role.allowed_to?(:log_time)
@time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :user => logged_in_user, :spent_on => Date.today) @time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => Date.today)
@time_entry.attributes = params[:time_entry] @time_entry.attributes = params[:time_entry]
@time_entry.save @time_entry.save
end end
@@ -152,13 +159,13 @@ class IssuesController < ApplicationController
def destroy def destroy
@issue.destroy @issue.destroy
redirect_to :controller => 'projects', :action => 'list_issues', :id => @project redirect_to :action => 'index', :project_id => @project
end end
def destroy_attachment def destroy_attachment
a = @issue.attachments.find(params[:attachment_id]) a = @issue.attachments.find(params[:attachment_id])
a.destroy a.destroy
journal = @issue.init_journal(self.logged_in_user) journal = @issue.init_journal(User.current)
journal.details << JournalDetail.new(:property => 'attachment', journal.details << JournalDetail.new(:property => 'attachment',
:prop_key => a.id, :prop_key => a.id,
:old_value => a.filename) :old_value => a.filename)
@@ -176,6 +183,7 @@ class IssuesController < ApplicationController
:change_status => User.current.allowed_to?(:change_issue_status, @project), :change_status => User.current.allowed_to?(:change_issue_status, @project),
:add => User.current.allowed_to?(:add_issues, @project), :add => User.current.allowed_to?(:add_issues, @project),
:move => User.current.allowed_to?(:move_issues, @project), :move => User.current.allowed_to?(:move_issues, @project),
:copy => (@project.trackers.include?(@issue.tracker) && User.current.allowed_to?(:add_issues, @project)),
:delete => User.current.allowed_to?(:delete_issues, @project)} :delete => User.current.allowed_to?(:delete_issues, @project)}
render :layout => false render :layout => false
end end
@@ -195,23 +203,37 @@ private
render_404 render_404
end end
def find_optional_project
return true unless params[:project_id]
@project = Project.find(params[:project_id])
authorize
rescue ActiveRecord::RecordNotFound
render_404
end
# Retrieve query from session or build a new query # Retrieve query from session or build a new query
def retrieve_query def retrieve_query
if params[:set_filter] or !session[:query] or session[:query].project_id if params[:query_id]
# Give it a name, required to be valid @query = Query.find(params[:query_id], :conditions => {:project_id => (@project ? @project.id : nil)})
@query = Query.new(:name => "_", :executed_by => logged_in_user)
if params[:fields] and params[:fields].is_a? Array
params[:fields].each do |field|
@query.add_filter(field, params[:operators][field], params[:values][field])
end
else
@query.available_filters.keys.each do |field|
@query.add_short_filter(field, params[field]) if params[field]
end
end
session[:query] = @query session[:query] = @query
else else
@query = session[:query] if params[:set_filter] or !session[:query] or session[:query].project != @project
# Give it a name, required to be valid
@query = Query.new(:name => "_")
@query.project = @project
if params[:fields] and params[:fields].is_a? Array
params[:fields].each do |field|
@query.add_filter(field, params[:operators][field], params[:values][field])
end
else
@query.available_filters.keys.each do |field|
@query.add_short_filter(field, params[field]) if params[field]
end
end
session[:query] = @query
else
@query = session[:query]
end
end end
end end
end end
+49 -12
View File
@@ -17,44 +17,81 @@
class MessagesController < ApplicationController class MessagesController < ApplicationController
layout 'base' layout 'base'
before_filter :find_project, :authorize before_filter :find_board, :only => :new
before_filter :find_message, :except => :new
before_filter :authorize
verify :method => :post, :only => [ :reply, :destroy ], :redirect_to => { :action => :show } verify :method => :post, :only => [ :reply, :destroy ], :redirect_to => { :action => :show }
helper :attachments helper :attachments
include AttachmentsHelper include AttachmentsHelper
# Show a topic and its replies
def show def show
@reply = Message.new(:subject => "RE: #{@message.subject}") @reply = Message.new(:subject => "RE: #{@message.subject}")
render :action => "show", :layout => false if request.xhr? render :action => "show", :layout => false if request.xhr?
end end
# Create a new topic
def new def new
@message = Message.new(params[:message]) @message = Message.new(params[:message])
@message.author = logged_in_user @message.author = User.current
@message.board = @board @message.board = @board
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 if request.post? && @message.save
params[:attachments].each { |file| attach_files(@message, params[:attachments])
next unless file.size > 0
Attachment.create(:container => @message, :file => file, :author => logged_in_user)
} if params[:attachments] and params[:attachments].is_a? Array
redirect_to :action => 'show', :id => @message redirect_to :action => 'show', :id => @message
end end
end end
# Reply to a topic
def reply def reply
@reply = Message.new(params[:reply]) @reply = Message.new(params[:reply])
@reply.author = logged_in_user @reply.author = User.current
@reply.board = @board @reply.board = @board
@message.children << @reply @topic.children << @reply
redirect_to :action => 'show', :id => @message if !@reply.new_record?
attach_files(@reply, params[:attachments])
end
redirect_to :action => 'show', :id => @topic
end
# Edit a message
def edit
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.update_attributes(params[:message])
attach_files(@message, params[:attachments])
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'show', :id => @topic
end
end
# Delete a messages
def destroy
@message.destroy
redirect_to @message.parent.nil? ?
{ :controller => 'boards', :action => 'show', :project_id => @project, :id => @board } :
{ :action => 'show', :id => @message.parent }
end end
private private
def find_project def find_message
find_board
@message = @board.messages.find(params[:id], :include => :parent)
@topic = @message.root
rescue ActiveRecord::RecordNotFound
render_404
end
def find_board
@board = Board.find(params[:board_id], :include => :project) @board = Board.find(params[:board_id], :include => :project)
@project = @board.project @project = @board.project
@message = @board.topics.find(params[:id]) if params[:id]
rescue ActiveRecord::RecordNotFound rescue ActiveRecord::RecordNotFound
render_404 render_404
end end
+6 -5
View File
@@ -44,7 +44,7 @@ class MyController < ApplicationController
# Show user's page # Show user's page
def page def page
@user = self.logged_in_user @user = User.current
@blocks = @user.pref[:my_page_layout] || DEFAULT_LAYOUT @blocks = @user.pref[:my_page_layout] || DEFAULT_LAYOUT
end end
@@ -56,6 +56,7 @@ class MyController < ApplicationController
@user.attributes = params[:user] @user.attributes = params[:user]
@user.mail_notification = (params[:notification_option] == 'all') @user.mail_notification = (params[:notification_option] == 'all')
@user.pref.attributes = params[:pref] @user.pref.attributes = params[:pref]
@user.pref[:no_self_notified] = (params[:no_self_notified] == '1')
if @user.save if @user.save
@user.pref.save @user.pref.save
@user.notified_project_ids = (params[:notification_option] == 'selected' ? params[:notified_project_ids] : []) @user.notified_project_ids = (params[:notification_option] == 'selected' ? params[:notified_project_ids] : [])
@@ -75,7 +76,7 @@ class MyController < ApplicationController
# Manage user's password # Manage user's password
def password def password
@user = self.logged_in_user @user = User.current
flash[:error] = l(:notice_can_t_change_password) and redirect_to :action => 'account' and return if @user.auth_source_id flash[:error] = l(:notice_can_t_change_password) and redirect_to :action => 'account' and return if @user.auth_source_id
if request.post? if request.post?
if @user.check_password?(params[:password]) if @user.check_password?(params[:password])
@@ -101,7 +102,7 @@ class MyController < ApplicationController
# User's page layout configuration # User's page layout configuration
def page_layout def page_layout
@user = self.logged_in_user @user = User.current
@blocks = @user.pref[:my_page_layout] || DEFAULT_LAYOUT.dup @blocks = @user.pref[:my_page_layout] || DEFAULT_LAYOUT.dup
session[:page_layout] = @blocks session[:page_layout] = @blocks
%w(top left right).each {|f| session[:page_layout][f] ||= [] } %w(top left right).each {|f| session[:page_layout][f] ||= [] }
@@ -115,7 +116,7 @@ class MyController < ApplicationController
def add_block def add_block
block = params[:block] block = params[:block]
render(:nothing => true) and return unless block && (BLOCKS.keys.include? block) render(:nothing => true) and return unless block && (BLOCKS.keys.include? block)
@user = self.logged_in_user @user = User.current
# remove if already present in a group # remove if already present in a group
%w(top left right).each {|f| (session[:page_layout][f] ||= []).delete block } %w(top left right).each {|f| (session[:page_layout][f] ||= []).delete block }
# add it on top # add it on top
@@ -150,7 +151,7 @@ class MyController < ApplicationController
# Save user's page layout # Save user's page layout
def page_layout_save def page_layout_save
@user = self.logged_in_user @user = User.current
@user.pref[:my_page_layout] = session[:page_layout] if session[:page_layout] @user.pref[:my_page_layout] = session[:page_layout] if session[:page_layout]
@user.pref.save @user.pref.save
session[:page_layout] = nil session[:page_layout] = nil
+27 -5
View File
@@ -17,8 +17,22 @@
class NewsController < ApplicationController class NewsController < ApplicationController
layout 'base' layout 'base'
before_filter :find_project, :authorize before_filter :find_project, :authorize, :except => :index
before_filter :find_optional_project, :only => :index
accept_key_auth :index
def index
@news_pages, @newss = paginate :news,
:per_page => 10,
:conditions => (@project ? {:project_id => @project.id} : Project.visible_by(User.current)),
:include => [:author, :project],
:order => "#{News.table_name}.created_on DESC"
respond_to do |format|
format.html { render :layout => false if request.xhr? }
format.atom { render_feed(@newss, :title => (@project ? @project.name : Setting.app_title) + ": #{l(:label_news_plural)}") }
end
end
def show def show
end end
@@ -31,7 +45,7 @@ class NewsController < ApplicationController
def add_comment def add_comment
@comment = Comment.new(params[:comment]) @comment = Comment.new(params[:comment])
@comment.author = logged_in_user @comment.author = User.current
if @news.comments << @comment if @news.comments << @comment
flash[:notice] = l(:label_comment_added) flash[:notice] = l(:label_comment_added)
redirect_to :action => 'show', :id => @news redirect_to :action => 'show', :id => @news
@@ -47,7 +61,7 @@ class NewsController < ApplicationController
def destroy def destroy
@news.destroy @news.destroy
redirect_to :controller => 'projects', :action => 'list_news', :id => @project redirect_to :action => 'index', :project_id => @project
end end
private private
@@ -56,5 +70,13 @@ private
@project = @news.project @project = @news.project
rescue ActiveRecord::RecordNotFound rescue ActiveRecord::RecordNotFound
render_404 render_404
end end
def find_optional_project
return true unless params[:project_id]
@project = Project.find(params[:project_id])
authorize
rescue ActiveRecord::RecordNotFound
render_404
end
end end
+65 -206
View File
@@ -15,8 +15,6 @@
# along with this program; if not, write to the Free Software # along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require 'csv'
class ProjectsController < ApplicationController class ProjectsController < ApplicationController
layout 'base' layout 'base'
before_filter :find_project, :except => [ :index, :list, :add ] before_filter :find_project, :except => [ :index, :list, :add ]
@@ -50,7 +48,7 @@ class ProjectsController < ApplicationController
# Lists visible projects # Lists visible projects
def list def list
projects = Project.find :all, projects = Project.find :all,
:conditions => Project.visible_by(logged_in_user), :conditions => Project.visible_by(User.current),
:include => :parent :include => :parent
@project_tree = projects.group_by {|p| p.parent || p} @project_tree = projects.group_by {|p| p.parent || p}
@project_tree.each_key {|p| @project_tree[p] -= [p]} @project_tree.each_key {|p| @project_tree[p] -= [p]}
@@ -58,15 +56,19 @@ class ProjectsController < ApplicationController
# Add a new project # Add a new project
def add def add
@custom_fields = IssueCustomField.find(:all) @custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position")
@root_projects = Project.find(:all, :conditions => "parent_id IS NULL AND status = #{Project::STATUS_ACTIVE}") @trackers = Tracker.all
@root_projects = Project.find(:all,
:conditions => "parent_id IS NULL AND status = #{Project::STATUS_ACTIVE}",
:order => 'name')
@project = Project.new(params[:project]) @project = Project.new(params[:project])
@project.enabled_module_names = Redmine::AccessControl.available_project_modules @project.enabled_module_names = Redmine::AccessControl.available_project_modules
if request.get? if request.get?
@custom_values = ProjectCustomField.find(:all).collect { |x| CustomValue.new(:custom_field => x, :customized => @project) } @custom_values = ProjectCustomField.find(:all, :order => "#{CustomField.table_name}.position").collect { |x| CustomValue.new(:custom_field => x, :customized => @project) }
@project.trackers = Tracker.all
else else
@project.custom_fields = CustomField.find(params[:custom_field_ids]) if params[:custom_field_ids] @project.custom_fields = CustomField.find(params[:custom_field_ids]) if params[:custom_field_ids]
@custom_values = ProjectCustomField.find(:all).collect { |x| CustomValue.new(:custom_field => x, :customized => @project, :value => (params[:custom_fields] ? params["custom_fields"][x.id.to_s] : nil)) } @custom_values = ProjectCustomField.find(:all, :order => "#{CustomField.table_name}.position").collect { |x| CustomValue.new(:custom_field => x, :customized => @project, :value => (params[:custom_fields] ? params["custom_fields"][x.id.to_s] : nil)) }
@project.custom_values = @custom_values @project.custom_values = @custom_values
if @project.save if @project.save
@project.enabled_module_names = params[:enabled_modules] @project.enabled_module_names = params[:enabled_modules]
@@ -78,11 +80,11 @@ class ProjectsController < ApplicationController
# Show @project # Show @project
def show def show
@custom_values = @project.custom_values.find(:all, :include => :custom_field) @custom_values = @project.custom_values.find(:all, :include => :custom_field, :order => "#{CustomField.table_name}.position")
@members_by_role = @project.members.find(:all, :include => [:user, :role], :order => 'position').group_by {|m| m.role} @members_by_role = @project.members.find(:all, :include => [:user, :role], :order => 'position').group_by {|m| m.role}
@subprojects = @project.active_children @subprojects = @project.active_children
@news = @project.news.find(:all, :limit => 5, :include => [ :author, :project ], :order => "#{News.table_name}.created_on DESC") @news = @project.news.find(:all, :limit => 5, :include => [ :author, :project ], :order => "#{News.table_name}.created_on DESC")
@trackers = Tracker.find(:all, :order => 'position') @trackers = @project.trackers
@open_issues_by_tracker = Issue.count(:group => :tracker, :joins => "INNER JOIN #{IssueStatus.table_name} ON #{IssueStatus.table_name}.id = #{Issue.table_name}.status_id", :conditions => ["project_id=? and #{IssueStatus.table_name}.is_closed=?", @project.id, false]) @open_issues_by_tracker = Issue.count(:group => :tracker, :joins => "INNER JOIN #{IssueStatus.table_name} ON #{IssueStatus.table_name}.id = #{Issue.table_name}.status_id", :conditions => ["project_id=? and #{IssueStatus.table_name}.is_closed=?", @project.id, false])
@total_issues_by_tracker = Issue.count(:group => :tracker, :conditions => ["project_id=?", @project.id]) @total_issues_by_tracker = Issue.count(:group => :tracker, :conditions => ["project_id=?", @project.id])
@total_hours = @project.time_entries.sum(:hours) @total_hours = @project.time_entries.sum(:hours)
@@ -90,11 +92,14 @@ class ProjectsController < ApplicationController
end end
def settings def settings
@root_projects = Project::find(:all, :conditions => ["parent_id IS NULL AND status = #{Project::STATUS_ACTIVE} AND id <> ?", @project.id]) @root_projects = Project.find(:all,
:conditions => ["parent_id IS NULL AND status = #{Project::STATUS_ACTIVE} AND id <> ?", @project.id],
:order => 'name')
@custom_fields = IssueCustomField.find(:all) @custom_fields = IssueCustomField.find(:all)
@issue_category ||= IssueCategory.new @issue_category ||= IssueCategory.new
@member ||= @project.members.new @member ||= @project.members.new
@custom_values ||= ProjectCustomField.find(:all).collect { |x| @project.custom_values.find_by_custom_field_id(x.id) || CustomValue.new(:custom_field => x) } @trackers = Tracker.all
@custom_values ||= ProjectCustomField.find(:all, :order => "#{CustomField.table_name}.position").collect { |x| @project.custom_values.find_by_custom_field_id(x.id) || CustomValue.new(:custom_field => x) }
@repository ||= @project.repository @repository ||= @project.repository
@wiki ||= @project.wiki @wiki ||= @project.wiki
end end
@@ -104,7 +109,7 @@ class ProjectsController < ApplicationController
if request.post? if request.post?
@project.custom_fields = IssueCustomField.find(params[:custom_field_ids]) if params[:custom_field_ids] @project.custom_fields = IssueCustomField.find(params[:custom_field_ids]) if params[:custom_field_ids]
if params[:custom_fields] if params[:custom_fields]
@custom_values = ProjectCustomField.find(:all).collect { |x| CustomValue.new(:custom_field => x, :customized => @project, :value => params["custom_fields"][x.id.to_s]) } @custom_values = ProjectCustomField.find(:all, :order => "#{CustomField.table_name}.position").collect { |x| CustomValue.new(:custom_field => x, :customized => @project, :value => params["custom_fields"][x.id.to_s]) }
@project.custom_values = @custom_values @project.custom_values = @custom_values
end end
@project.attributes = params[:project] @project.attributes = params[:project]
@@ -176,10 +181,7 @@ class ProjectsController < ApplicationController
def add_document def add_document
@document = @project.documents.build(params[:document]) @document = @project.documents.build(params[:document])
if request.post? and @document.save if request.post? and @document.save
# Save the attachments attach_files(@document, params[:attachments])
params[:attachments].each { |a|
Attachment.create(:container => @document, :file => a, :author => logged_in_user) unless a.size == 0
} if params[:attachments] and params[:attachments].is_a? Array
flash[:notice] = l(:notice_successful_create) flash[:notice] = l(:notice_successful_create)
Mailer.deliver_document_added(@document) if Setting.notified_events.include?('document_added') Mailer.deliver_document_added(@document) if Setting.notified_events.include?('document_added')
redirect_to :action => 'list_documents', :id => @project redirect_to :action => 'list_documents', :id => @project
@@ -209,7 +211,7 @@ class ProjectsController < ApplicationController
@issue = params[:copy_from] ? Issue.new.copy_from(params[:copy_from]) : Issue.new(params[:issue]) @issue = params[:copy_from] ? Issue.new.copy_from(params[:copy_from]) : Issue.new(params[:issue])
@issue.project = @project @issue.project = @project
@issue.author = User.current @issue.author = User.current
@issue.tracker ||= Tracker.find(params[:tracker_id]) @issue.tracker ||= @project.trackers.find(params[:tracker_id])
default_status = IssueStatus.default default_status = IssueStatus.default
unless default_status unless default_status
@@ -218,7 +220,7 @@ class ProjectsController < ApplicationController
return return
end end
@issue.status = default_status @issue.status = default_status
@allowed_statuses = ([default_status] + default_status.find_new_statuses_allowed_to(logged_in_user.role_for_project(@project), @issue.tracker))if logged_in_user @allowed_statuses = ([default_status] + default_status.find_new_statuses_allowed_to(User.current.role_for_project(@project), @issue.tracker))
if request.get? if request.get?
@issue.start_date ||= Date.today @issue.start_date ||= Date.today
@@ -232,124 +234,16 @@ class ProjectsController < ApplicationController
@custom_values = @project.custom_fields_for_issues(@issue.tracker).collect { |x| CustomValue.new(:custom_field => x, :customized => @issue, :value => params["custom_fields"][x.id.to_s]) } @custom_values = @project.custom_fields_for_issues(@issue.tracker).collect { |x| CustomValue.new(:custom_field => x, :customized => @issue, :value => params["custom_fields"][x.id.to_s]) }
@issue.custom_values = @custom_values @issue.custom_values = @custom_values
if @issue.save if @issue.save
if params[:attachments] && params[:attachments].is_a?(Array) attach_files(@issue, params[:attachments])
# Save attachments
params[:attachments].each {|a| Attachment.create(:container => @issue, :file => a, :author => User.current) unless a.size == 0}
end
flash[:notice] = l(:notice_successful_create) flash[:notice] = l(:notice_successful_create)
Mailer.deliver_issue_add(@issue) if Setting.notified_events.include?('issue_added') Mailer.deliver_issue_add(@issue) if Setting.notified_events.include?('issue_added')
redirect_to :action => 'list_issues', :id => @project redirect_to :controller => 'issues', :action => 'index', :project_id => @project
return return
end end
end end
@priorities = Enumeration::get_values('IPRI') @priorities = Enumeration::get_values('IPRI')
end end
# Show filtered/sorted issues list of @project
def list_issues
sort_init "#{Issue.table_name}.id", "desc"
sort_update
retrieve_query
@results_per_page_options = [ 15, 25, 50, 100 ]
if params[:per_page] and @results_per_page_options.include? params[:per_page].to_i
@results_per_page = params[:per_page].to_i
session[:results_per_page] = @results_per_page
else
@results_per_page = session[:results_per_page] || 25
end
if @query.valid?
@issue_count = Issue.count(:include => [:status, :project], :conditions => @query.statement)
@issue_pages = Paginator.new self, @issue_count, @results_per_page, params['page']
@issues = Issue.find :all, :order => sort_clause,
:include => [ :assigned_to, :status, :tracker, :project, :priority, :category ],
:conditions => @query.statement,
:limit => @issue_pages.items_per_page,
:offset => @issue_pages.current.offset
end
render :layout => false if request.xhr?
end
# Export filtered/sorted issues list to CSV
def export_issues_csv
sort_init "#{Issue.table_name}.id", "desc"
sort_update
retrieve_query
render :action => 'list_issues' and return unless @query.valid?
@issues = Issue.find :all, :order => sort_clause,
:include => [ :assigned_to, :author, :status, :tracker, :priority, :project, {:custom_values => :custom_field} ],
:conditions => @query.statement,
:limit => Setting.issues_export_limit.to_i
ic = Iconv.new(l(:general_csv_encoding), 'UTF-8')
export = StringIO.new
CSV::Writer.generate(export, l(:general_csv_separator)) do |csv|
# csv header fields
headers = [ "#", l(:field_status),
l(:field_project),
l(:field_tracker),
l(:field_priority),
l(:field_subject),
l(:field_assigned_to),
l(:field_author),
l(:field_start_date),
l(:field_due_date),
l(:field_done_ratio),
l(:field_created_on),
l(:field_updated_on)
]
for custom_field in @project.all_custom_fields
headers << custom_field.name
end
csv << headers.collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end }
# csv lines
@issues.each do |issue|
fields = [issue.id, issue.status.name,
issue.project.name,
issue.tracker.name,
issue.priority.name,
issue.subject,
(issue.assigned_to ? issue.assigned_to.name : ""),
issue.author.name,
issue.start_date ? l_date(issue.start_date) : nil,
issue.due_date ? l_date(issue.due_date) : nil,
issue.done_ratio,
l_datetime(issue.created_on),
l_datetime(issue.updated_on)
]
for custom_field in @project.all_custom_fields
fields << (show_value issue.custom_value_for(custom_field))
end
csv << fields.collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end }
end
end
export.rewind
send_data(export.read, :type => 'text/csv; header=present', :filename => 'export.csv')
end
# Export filtered/sorted issues to PDF
def export_issues_pdf
sort_init "#{Issue.table_name}.id", "desc"
sort_update
retrieve_query
render :action => 'list_issues' and return unless @query.valid?
@issues = Issue.find :all, :order => sort_clause,
:include => [ :author, :status, :tracker, :priority, :project ],
:conditions => @query.statement,
:limit => Setting.issues_export_limit.to_i
@options_for_rfpdf ||= {}
@options_for_rfpdf[:file_name] = "export.pdf"
render :layout => false
end
# Bulk edit issues # Bulk edit issues
def bulk_edit_issues def bulk_edit_issues
if request.post? if request.post?
@@ -383,7 +277,7 @@ class ProjectsController < ApplicationController
else else
flash[:error] = l(:notice_failed_to_save_issues, unsaved_issue_ids.size, issues.size, '#' + unsaved_issue_ids.join(', #')) flash[:error] = l(:notice_failed_to_save_issues, unsaved_issue_ids.size, issues.size, '#' + unsaved_issue_ids.join(', #'))
end end
redirect_to :action => 'list_issues', :id => @project redirect_to :controller => 'issues', :action => 'index', :project_id => @project
return return
end end
if current_role && User.current.allowed_to?(:change_issue_status, @project) if current_role && User.current.allowed_to?(:change_issue_status, @project)
@@ -399,70 +293,54 @@ class ProjectsController < ApplicationController
def move_issues def move_issues
@issues = @project.issues.find(params[:issue_ids]) if params[:issue_ids] @issues = @project.issues.find(params[:issue_ids]) if params[:issue_ids]
redirect_to :action => 'list_issues', :id => @project and return unless @issues redirect_to :controller => 'issues', :action => 'index', :project_id => @project and return unless @issues
@projects = [] @projects = []
# find projects to which the user is allowed to move the issue # find projects to which the user is allowed to move the issue
User.current.memberships.each {|m| @projects << m.project if m.role.allowed_to?(:controller => 'projects', :action => 'move_issues')} if User.current.admin?
# issue can be moved to any tracker # admin is allowed to move issues to any active (visible) project
@trackers = Tracker.find(:all) @projects = Project.find(:all, :conditions => Project.visible_by(User.current), :order => 'name')
if request.post? and params[:new_project_id] and params[:new_tracker_id] else
new_project = Project.find_by_id(params[:new_project_id]) User.current.memberships.each {|m| @projects << m.project if m.role.allowed_to?(:move_issues)}
new_tracker = Tracker.find_by_id(params[:new_tracker_id])
@issues.each do |i|
if new_project && i.project_id != new_project.id
# issue is moved to another project
i.category = nil
i.fixed_version = nil
# delete issue relations
i.relations_from.clear
i.relations_to.clear
i.project = new_project
end
if new_tracker
i.tracker = new_tracker
end
i.save
end
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'list_issues', :id => @project
end end
@target_project = @projects.detect {|p| p.id.to_s == params[:new_project_id]} if params[:new_project_id]
@target_project ||= @project
@trackers = @target_project.trackers
if request.post?
new_tracker = params[:new_tracker_id].blank? ? nil : @target_project.trackers.find_by_id(params[:new_tracker_id])
unsaved_issue_ids = []
@issues.each do |issue|
unsaved_issue_ids << issue.id unless issue.move_to(@target_project, new_tracker)
end
if unsaved_issue_ids.empty?
flash[:notice] = l(:notice_successful_update) unless @issues.empty?
else
flash[:error] = l(:notice_failed_to_save_issues, unsaved_issue_ids.size, @issues.size, '#' + unsaved_issue_ids.join(', #'))
end
redirect_to :controller => 'issues', :action => 'index', :project_id => @project
return
end
render :layout => false if request.xhr?
end end
# Add a news to @project # Add a news to @project
def add_news def add_news
@news = News.new(:project => @project) @news = News.new(:project => @project, :author => User.current)
if request.post? if request.post?
@news.attributes = params[:news] @news.attributes = params[:news]
@news.author_id = self.logged_in_user.id if self.logged_in_user
if @news.save if @news.save
flash[:notice] = l(:notice_successful_create) flash[:notice] = l(:notice_successful_create)
Mailer.deliver_news_added(@news) if Setting.notified_events.include?('news_added') Mailer.deliver_news_added(@news) if Setting.notified_events.include?('news_added')
redirect_to :action => 'list_news', :id => @project redirect_to :controller => 'news', :action => 'index', :project_id => @project
end end
end end
end end
# Show news list of @project
def list_news
@news_pages, @newss = paginate :news, :per_page => 10, :conditions => ["project_id=?", @project.id], :include => :author, :order => "#{News.table_name}.created_on DESC"
respond_to do |format|
format.html { render :layout => false if request.xhr? }
format.atom { render_feed(@newss, :title => "#{@project.name}: #{l(:label_news_plural)}") }
end
end
def add_file def add_file
if request.post? if request.post?
@version = @project.versions.find_by_id(params[:version_id]) @version = @project.versions.find_by_id(params[:version_id])
# Save the attachments attachments = attach_files(@version, params[:attachments])
@attachments = [] Mailer.deliver_attachments_added(attachments) if !attachments.empty? && Setting.notified_events.include?('file_added')
params[:attachments].each { |file|
next unless file.size > 0
a = Attachment.create(:container => @version, :file => file, :author => logged_in_user)
@attachments << a unless a.new_record?
} if params[:attachments] and params[:attachments].is_a? Array
Mailer.deliver_attachments_added(@attachments) if !@attachments.empty? && Setting.notified_events.include?('file_added')
redirect_to :controller => 'projects', :action => 'list_files', :id => @project redirect_to :controller => 'projects', :action => 'list_files', :id => @project
end end
@versions = @project.versions.sort @versions = @project.versions.sort
@@ -474,13 +352,13 @@ class ProjectsController < ApplicationController
# Show changelog for @project # Show changelog for @project
def changelog def changelog
@trackers = Tracker.find(:all, :conditions => ["is_in_chlog=?", true], :order => 'position') @trackers = @project.trackers.find(:all, :conditions => ["is_in_chlog=?", true], :order => 'position')
retrieve_selected_tracker_ids(@trackers) retrieve_selected_tracker_ids(@trackers)
@versions = @project.versions.sort @versions = @project.versions.sort
end end
def roadmap def roadmap
@trackers = Tracker.find(:all, :conditions => ["is_in_roadmap=?", true], :order => 'position') @trackers = @project.trackers.find(:all, :conditions => ["is_in_roadmap=?", true])
retrieve_selected_tracker_ids(@trackers) retrieve_selected_tracker_ids(@trackers)
@versions = @project.versions.sort @versions = @project.versions.sort
@versions = @versions.select {|v| !v.completed? } unless params[:completed] @versions = @versions.select {|v| !v.completed? } unless params[:completed]
@@ -507,20 +385,22 @@ class ProjectsController < ApplicationController
@date_to = @date_from >> 1 @date_to = @date_from >> 1
end end
@event_types = %w(issues news files documents wiki_pages changesets) @event_types = %w(issues news files documents changesets wiki_pages messages)
@event_types.delete('wiki_pages') unless @project.wiki @event_types.delete('wiki_pages') unless @project.wiki
@event_types.delete('changesets') unless @project.repository @event_types.delete('changesets') unless @project.repository
@event_types.delete('messages') unless @project.boards.any?
# only show what the user is allowed to view # only show what the user is allowed to view
@event_types = @event_types.select {|o| User.current.allowed_to?("view_#{o}".to_sym, @project)} @event_types = @event_types.select {|o| User.current.allowed_to?("view_#{o}".to_sym, @project)}
@scope = @event_types.select {|t| params["show_#{t}"]} @scope = @event_types.select {|t| params["show_#{t}"]}
# default events if none is specified in parameters # default events if none is specified in parameters
@scope = (@event_types - %w(wiki_pages))if @scope.empty? @scope = (@event_types - %w(wiki_pages messages))if @scope.empty?
@events = [] @events = []
if @scope.include?('issues') if @scope.include?('issues')
@events += @project.issues.find(:all, :include => [:author, :tracker], :conditions => ["#{Issue.table_name}.created_on>=? and #{Issue.table_name}.created_on<=?", @date_from, @date_to] ) @events += @project.issues.find(:all, :include => [:author, :tracker], :conditions => ["#{Issue.table_name}.created_on>=? and #{Issue.table_name}.created_on<=?", @date_from, @date_to] )
@events += @project.issues_status_changes(@date_from, @date_to)
end end
if @scope.include?('news') if @scope.include?('news')
@@ -550,7 +430,13 @@ class ProjectsController < ApplicationController
end end
if @scope.include?('changesets') if @scope.include?('changesets')
@events += @project.repository.changesets.find(:all, :conditions => ["#{Changeset.table_name}.committed_on BETWEEN ? AND ?", @date_from, @date_to]) @events += Changeset.find(:all, :include => :repository, :conditions => ["#{Repository.table_name}.project_id = ? AND #{Changeset.table_name}.committed_on BETWEEN ? AND ?", @project.id, @date_from, @date_to])
end
if @scope.include?('messages')
@events += Message.find(:all,
:include => [:board, :author],
:conditions => ["#{Board.table_name}.project_id=? AND #{Message.table_name}.parent_id IS NULL AND #{Message.table_name}.created_on BETWEEN ? AND ?", @project.id, @date_from, @date_to])
end end
@events_by_day = @events.group_by(&:event_date) @events_by_day = @events.group_by(&:event_date)
@@ -659,31 +545,4 @@ private
@selected_tracker_ids = selectable_trackers.collect {|t| t.id.to_s } @selected_tracker_ids = selectable_trackers.collect {|t| t.id.to_s }
end end
end end
# Retrieve query from session or build a new query
def retrieve_query
if params[:query_id]
@query = @project.queries.find(params[:query_id])
@query.executed_by = logged_in_user
session[:query] = @query
else
if params[:set_filter] or !session[:query] or session[:query].project_id != @project.id
# Give it a name, required to be valid
@query = Query.new(:name => "_", :executed_by => logged_in_user)
@query.project = @project
if params[:fields] and params[:fields].is_a? Array
params[:fields].each do |field|
@query.add_filter(field, params[:operators][field], params[:values][field])
end
else
@query.available_filters.keys.each do |field|
@query.add_short_filter(field, params[field]) if params[field]
end
end
session[:query] = @query
else
@query = session[:query]
end
end
end
end end
+5 -7
View File
@@ -22,14 +22,13 @@ class QueriesController < ApplicationController
def index def index
@queries = @project.queries.find(:all, @queries = @project.queries.find(:all,
:order => "name ASC", :order => "name ASC",
:conditions => ["is_public = ? or user_id = ?", true, (logged_in_user ? logged_in_user.id : 0)]) :conditions => ["is_public = ? or user_id = ?", true, (User.current.logged? ? User.current.id : 0)])
end end
def new def new
@query = Query.new(params[:query]) @query = Query.new(params[:query])
@query.project = @project @query.project = @project
@query.user = logged_in_user @query.user = User.current
@query.executed_by = logged_in_user
@query.is_public = false unless current_role.allowed_to?(:manage_public_queries) @query.is_public = false unless current_role.allowed_to?(:manage_public_queries)
@query.column_names = nil if params[:default_columns] @query.column_names = nil if params[:default_columns]
@@ -39,7 +38,7 @@ class QueriesController < ApplicationController
if request.post? && params[:confirm] && @query.save if request.post? && params[:confirm] && @query.save
flash[:notice] = l(:notice_successful_create) flash[:notice] = l(:notice_successful_create)
redirect_to :controller => 'projects', :action => 'list_issues', :id => @project, :query_id => @query redirect_to :controller => 'issues', :action => 'index', :project_id => @project, :query_id => @query
return return
end end
render :layout => false if request.xhr? render :layout => false if request.xhr?
@@ -57,7 +56,7 @@ class QueriesController < ApplicationController
if @query.save if @query.save
flash[:notice] = l(:notice_successful_update) flash[:notice] = l(:notice_successful_update)
redirect_to :controller => 'projects', :action => 'list_issues', :id => @project, :query_id => @query redirect_to :controller => 'issues', :action => 'index', :project_id => @project, :query_id => @query
end end
end end
end end
@@ -71,9 +70,8 @@ private
def find_project def find_project
if params[:id] if params[:id]
@query = Query.find(params[:id]) @query = Query.find(params[:id])
@query.executed_by = logged_in_user
@project = @query.project @project = @query.project
render_403 unless @query.editable_by?(logged_in_user) render_403 unless @query.editable_by?(User.current)
else else
@project = Project.find(params[:project_id]) @project = Project.find(params[:project_id])
end end
+2 -2
View File
@@ -25,7 +25,7 @@ class ReportsController < ApplicationController
case params[:detail] case params[:detail]
when "tracker" when "tracker"
@field = "tracker_id" @field = "tracker_id"
@rows = Tracker.find :all, :order => 'position' @rows = @project.trackers
@data = issues_by_tracker @data = issues_by_tracker
@report_title = l(:field_tracker) @report_title = l(:field_tracker)
render :template => "reports/issue_report_details" render :template => "reports/issue_report_details"
@@ -60,7 +60,7 @@ class ReportsController < ApplicationController
@report_title = l(:field_subproject) @report_title = l(:field_subproject)
render :template => "reports/issue_report_details" render :template => "reports/issue_report_details"
else else
@trackers = Tracker.find(:all, :order => 'position') @trackers = @project.trackers
@versions = @project.versions.sort @versions = @project.versions.sort
@priorities = Enumeration::get_values('IPRI') @priorities = Enumeration::get_values('IPRI')
@categories = @project.issue_categories @categories = @project.issue_categories
+27 -4
View File
@@ -19,6 +19,9 @@ require 'SVG/Graph/Bar'
require 'SVG/Graph/BarHorizontal' require 'SVG/Graph/BarHorizontal'
require 'digest/sha1' require 'digest/sha1'
class ChangesetNotFound < Exception
end
class RepositoriesController < ApplicationController class RepositoriesController < ApplicationController
layout 'base' layout 'base'
before_filter :find_repository, :except => :edit before_filter :find_repository, :except => :edit
@@ -89,24 +92,44 @@ class RepositoriesController < ApplicationController
show_error and return unless @content show_error and return unless @content
if 'raw' == params[:format] if 'raw' == params[:format]
send_data @content, :filename => @path.split('/').last send_data @content, :filename => @path.split('/').last
else
# Prevent empty lines when displaying a file with Windows style eol
@content.gsub!("\r\n", "\n")
end end
end end
def annotate
@annotate = @repository.scm.annotate(@path, @rev)
show_error and return if @annotate.nil? || @annotate.empty?
end
def revision def revision
@changeset = @repository.changesets.find_by_revision(@rev) @changeset = @repository.changesets.find_by_revision(@rev)
show_error and return unless @changeset raise ChangesetNotFound unless @changeset
@changes_count = @changeset.changes.size @changes_count = @changeset.changes.size
@changes_pages = Paginator.new self, @changes_count, 150, params['page'] @changes_pages = Paginator.new self, @changes_count, 150, params['page']
@changes = @changeset.changes.find(:all, @changes = @changeset.changes.find(:all,
:limit => @changes_pages.items_per_page, :limit => @changes_pages.items_per_page,
:offset => @changes_pages.current.offset) :offset => @changes_pages.current.offset)
render :action => "revision", :layout => false if request.xhr? respond_to do |format|
format.html
format.js {render :layout => false}
end
rescue ChangesetNotFound
show_error
end end
def diff def diff
@rev_to = params[:rev_to] ? params[:rev_to].to_i : (@rev - 1) @rev_to = params[:rev_to] ? params[:rev_to].to_i : (@rev - 1)
@diff_type = ('sbs' == params[:type]) ? 'sbs' : 'inline' @diff_type = params[:type] || User.current.pref[:diff_type] || 'inline'
@diff_type = 'inline' unless %w(inline sbs).include?(@diff_type)
# Save diff type as user preference
if User.current.logged? && @diff_type != User.current.pref[:diff_type]
User.current.pref[:diff_type] = @diff_type
User.current.preference.save
end
@cache_key = "repositories/diff/#{@repository.id}/" + Digest::MD5.hexdigest("#{@path}-#{@rev}-#{@rev_to}-#{@diff_type}") @cache_key = "repositories/diff/#{@repository.id}/" + Digest::MD5.hexdigest("#{@path}-#{@rev}-#{@rev_to}-#{@diff_type}")
unless read_fragment(@cache_key) unless read_fragment(@cache_key)
+3 -2
View File
@@ -33,7 +33,8 @@ class RolesController < ApplicationController
end end
def new def new
@role = Role.new(params[:role]) # Prefills the form with 'Non member' role permissions
@role = Role.new(params[:role] || {:permissions => Role.non_member.permissions})
if request.post? && @role.save if request.post? && @role.save
flash[:notice] = l(:notice_successful_create) flash[:notice] = l(:notice_successful_create)
redirect_to :action => 'list' redirect_to :action => 'list'
@@ -93,7 +94,7 @@ class RolesController < ApplicationController
end end
@roles = Role.find(:all, :order => 'builtin, position') @roles = Role.find(:all, :order => 'builtin, position')
@trackers = Tracker.find(:all, :order => 'position') @trackers = Tracker.find(:all, :order => 'position')
@statuses = IssueStatus.find(:all, :include => :workflows, :order => 'position') @statuses = IssueStatus.find(:all, :order => 'position')
end end
def report def report
+9 -6
View File
@@ -31,7 +31,7 @@ class SearchController < ApplicationController
begin; offset = params[:offset].to_time if params[:offset]; rescue; end begin; offset = params[:offset].to_time if params[:offset]; rescue; end
# quick jump to an issue # quick jump to an issue
if @question.match(/^#?(\d+)$/) && Issue.find_by_id($1, :include => :project, :conditions => Project.visible_by(logged_in_user)) if @question.match(/^#?(\d+)$/) && Issue.find_by_id($1, :include => :project, :conditions => Project.visible_by(User.current))
redirect_to :controller => "issues", :action => "show", :id => $1 redirect_to :controller => "issues", :action => "show", :id => $1
return return
end end
@@ -52,8 +52,11 @@ class SearchController < ApplicationController
@object_types = @scope = %w(projects) @object_types = @scope = %w(projects)
end end
# 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 3 character long # tokens must be at least 3 character long
@tokens = @question.split.uniq.select {|w| w.length > 2 } @tokens = @tokens.uniq.select {|w| w.length > 2 }
if !@tokens.empty? if !@tokens.empty?
# no more than 5 tokens to search for # no more than 5 tokens to search for
@@ -87,13 +90,13 @@ class SearchController < ApplicationController
end end
else else
operator = @all_words ? ' AND ' : ' OR ' operator = @all_words ? ' AND ' : ' OR '
Project.with_scope(:find => {:conditions => Project.visible_by(logged_in_user)}) do @results += Project.find(:all,
@results += Project.find(:all, :limit => limit, :conditions => [ (["(LOWER(name) like ? OR LOWER(description) like ?)"] * like_tokens.size).join(operator), * (like_tokens * 2).sort] ) if @scope.include? 'projects' :limit => limit,
end :conditions => [ (["(#{Project.visible_by(User.current)}) AND (LOWER(name) like ? OR LOWER(description) like ?)"] * like_tokens.size).join(operator), * (like_tokens * 2).sort]
) if @scope.include? 'projects'
# if only one project is found, user is redirected to its overview # if only one project is found, user is redirected to its overview
redirect_to :controller => 'projects', :action => 'show', :id => @results.first and return if @results.size == 1 redirect_to :controller => 'projects', :action => 'show', :id => @results.first and return if @results.size == 1
end end
@question = @tokens.join(" ")
else else
@question = "" @question = ""
end end
+4 -4
View File
@@ -54,7 +54,7 @@ class TimelogController < ApplicationController
begin; @date_to = params[:date_to].to_date; rescue; end begin; @date_to = params[:date_to].to_date; rescue; end
end end
@date_from ||= Date.civil(Date.today.year, 1, 1) @date_from ||= Date.civil(Date.today.year, 1, 1)
@date_to ||= Date.civil(Date.today.year, Date.today.month+1, 1) - 1 @date_to ||= (Date.civil(Date.today.year, Date.today.month, 1) >> 1) - 1
unless @criterias.empty? unless @criterias.empty?
sql_select = @criterias.collect{|criteria| @available_criterias[criteria][:sql] + " AS " + criteria}.join(', ') sql_select = @criterias.collect{|criteria| @available_criterias[criteria][:sql] + " AS " + criteria}.join(', ')
@@ -107,15 +107,15 @@ class TimelogController < ApplicationController
@entries = (@issue ? @issue : @project).time_entries.find(:all, :include => [:activity, :user, {:issue => [:tracker, :assigned_to, :priority]}], :order => sort_clause) @entries = (@issue ? @issue : @project).time_entries.find(:all, :include => [:activity, :user, {:issue => [:tracker, :assigned_to, :priority]}], :order => sort_clause)
@total_hours = @entries.inject(0) { |sum,entry| sum + entry.hours } @total_hours = @entries.inject(0) { |sum,entry| sum + entry.hours }
@owner_id = logged_in_user ? logged_in_user.id : 0 @owner_id = User.current.id
send_csv and return if 'csv' == params[:export] send_csv and return if 'csv' == params[:export]
render :action => 'details', :layout => false if request.xhr? render :action => 'details', :layout => false if request.xhr?
end end
def edit def edit
render_404 and return if @time_entry && @time_entry.user != logged_in_user render_404 and return if @time_entry && @time_entry.user != User.current
@time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :user => logged_in_user, :spent_on => Date.today) @time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => Date.today)
@time_entry.attributes = params[:time_entry] @time_entry.attributes = params[:time_entry]
if request.post? and @time_entry.save if request.post? and @time_entry.save
flash[:notice] = l(:notice_successful_update) flash[:notice] = l(:notice_successful_update)
+4 -2
View File
@@ -37,8 +37,10 @@ class TrackersController < ApplicationController
if request.post? and @tracker.save if request.post? and @tracker.save
# workflow copy # workflow copy
if !params[:copy_workflow_from].blank? && (copy_from = Tracker.find_by_id(params[:copy_workflow_from])) if !params[:copy_workflow_from].blank? && (copy_from = Tracker.find_by_id(params[:copy_workflow_from]))
copy_from.workflows.each do |w| Workflow.transaction do
@tracker.workflows << w.clone copy_from.workflows.find(:all, :include => [:role, :old_status, :new_status]).each do |w|
Workflow.create(:tracker_id => @tracker.id, :role => w.role, :old_status => w.old_status, :new_status => w.new_status)
end
end end
end end
flash[:notice] = l(:notice_successful_create) flash[:notice] = l(:notice_successful_create)
+4 -12
View File
@@ -52,13 +52,13 @@ class UsersController < ApplicationController
def add def add
if request.get? if request.get?
@user = User.new(:language => Setting.default_language) @user = User.new(:language => Setting.default_language)
@custom_values = UserCustomField.find(:all).collect { |x| CustomValue.new(:custom_field => x, :customized => @user) } @custom_values = UserCustomField.find(:all, :order => "#{CustomField.table_name}.position").collect { |x| CustomValue.new(:custom_field => x, :customized => @user) }
else else
@user = User.new(params[:user]) @user = User.new(params[:user])
@user.admin = params[:user][:admin] || false @user.admin = params[:user][:admin] || false
@user.login = params[:user][:login] @user.login = params[:user][:login]
@user.password, @user.password_confirmation = params[:password], params[:password_confirmation] unless @user.auth_source_id @user.password, @user.password_confirmation = params[:password], params[:password_confirmation] unless @user.auth_source_id
@custom_values = UserCustomField.find(:all).collect { |x| CustomValue.new(:custom_field => x, :customized => @user, :value => (params[:custom_fields] ? params["custom_fields"][x.id.to_s] : nil)) } @custom_values = UserCustomField.find(:all, :order => "#{CustomField.table_name}.position").collect { |x| CustomValue.new(:custom_field => x, :customized => @user, :value => (params[:custom_fields] ? params["custom_fields"][x.id.to_s] : nil)) }
@user.custom_values = @custom_values @user.custom_values = @custom_values
if @user.save if @user.save
Mailer.deliver_account_information(@user, params[:password]) if params[:send_information] Mailer.deliver_account_information(@user, params[:password]) if params[:send_information]
@@ -72,13 +72,13 @@ class UsersController < ApplicationController
def edit def edit
@user = User.find(params[:id]) @user = User.find(params[:id])
if request.get? if request.get?
@custom_values = UserCustomField.find(:all).collect { |x| @user.custom_values.find_by_custom_field_id(x.id) || CustomValue.new(:custom_field => x) } @custom_values = UserCustomField.find(:all, :order => "#{CustomField.table_name}.position").collect { |x| @user.custom_values.find_by_custom_field_id(x.id) || CustomValue.new(:custom_field => x) }
else else
@user.admin = params[:user][:admin] if params[:user][:admin] @user.admin = params[:user][:admin] if params[:user][:admin]
@user.login = params[:user][:login] if params[:user][:login] @user.login = params[:user][:login] if params[:user][:login]
@user.password, @user.password_confirmation = params[:password], params[:password_confirmation] unless params[:password].nil? or params[:password].empty? or @user.auth_source_id @user.password, @user.password_confirmation = params[:password], params[:password_confirmation] unless params[:password].nil? or params[:password].empty? or @user.auth_source_id
if params[:custom_fields] if params[:custom_fields]
@custom_values = UserCustomField.find(:all).collect { |x| CustomValue.new(:custom_field => x, :customized => @user, :value => params["custom_fields"][x.id.to_s]) } @custom_values = UserCustomField.find(:all, :order => "#{CustomField.table_name}.position").collect { |x| CustomValue.new(:custom_field => x, :customized => @user, :value => params["custom_fields"][x.id.to_s]) }
@user.custom_values = @custom_values @user.custom_values = @custom_values
end end
if @user.update_attributes(params[:user]) if @user.update_attributes(params[:user])
@@ -109,12 +109,4 @@ class UsersController < ApplicationController
end end
redirect_to :action => 'edit', :id => @user and return redirect_to :action => 'edit', :id => @user and return
end end
def destroy
User.find(params[:id]).destroy
redirect_to :action => 'list'
rescue
flash[:error] = "Unable to delete user"
redirect_to :action => 'list'
end
end end
+11 -1
View File
@@ -21,6 +21,9 @@ class VersionsController < ApplicationController
cache_sweeper :version_sweeper, :only => [ :edit, :destroy ] cache_sweeper :version_sweeper, :only => [ :edit, :destroy ]
def show
end
def edit def edit
if request.post? and @version.update_attributes(params[:version]) if request.post? and @version.update_attributes(params[:version])
flash[:notice] = l(:notice_successful_update) flash[:notice] = l(:notice_successful_update)
@@ -39,7 +42,7 @@ class VersionsController < ApplicationController
def download def download
@attachment = @version.attachments.find(params[:attachment_id]) @attachment = @version.attachments.find(params[:attachment_id])
@attachment.increment_download @attachment.increment_download
send_file @attachment.diskfile, :filename => @attachment.filename send_file @attachment.diskfile, :filename => @attachment.filename, :type => @attachment.content_type
rescue rescue
render_404 render_404
end end
@@ -49,6 +52,13 @@ class VersionsController < ApplicationController
flash[:notice] = l(:notice_successful_delete) flash[:notice] = l(:notice_successful_delete)
redirect_to :controller => 'projects', :action => 'list_files', :id => @project redirect_to :controller => 'projects', :action => 'list_files', :id => @project
end end
def status_by
respond_to do |format|
format.html { render :action => 'show' }
format.js { render(:update) {|page| page.replace_html 'status_by', render_issue_status_by(@version, params[:status_by])} }
end
end
private private
def find_project def find_project
+2 -2
View File
@@ -19,7 +19,7 @@ class WelcomeController < ApplicationController
layout 'base' layout 'base'
def index def index
@news = News.latest logged_in_user @news = News.latest User.current
@projects = Project.latest logged_in_user @projects = Project.latest User.current
end end
end end
+2 -6
View File
@@ -69,7 +69,7 @@ class WikiController < ApplicationController
#@content.text = params[:content][:text] #@content.text = params[:content][:text]
#@content.comments = params[:content][:comments] #@content.comments = params[:content][:comments]
@content.attributes = params[:content] @content.attributes = params[:content]
@content.author = logged_in_user @content.author = User.current
# if page is new @page.save will also save content, but not if page isn't a new record # if page 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) if (@page.new_record? ? @page.save : @content.save)
redirect_to :action => 'index', :id => @project, :page => @page.title redirect_to :action => 'index', :id => @project, :page => @page.title
@@ -154,11 +154,7 @@ class WikiController < ApplicationController
def add_attachment def add_attachment
@page = @wiki.find_page(params[:page]) @page = @wiki.find_page(params[:page])
# Save the attachments attach_files(@page, params[:attachments])
params[:attachments].each { |file|
next unless file.size > 0
a = Attachment.create(:container => @page, :file => file, :author => logged_in_user)
} if params[:attachments] and params[:attachments].is_a? Array
redirect_to :action => 'index', :page => @page.title redirect_to :action => 'index', :page => @page.title
end end
+1 -1
View File
@@ -23,7 +23,7 @@ class WikisController < ApplicationController
def edit def edit
@wiki = @project.wiki || Wiki.new(:project => @project) @wiki = @project.wiki || Wiki.new(:project => @project)
@wiki.attributes = params[:wiki] @wiki.attributes = params[:wiki]
@wiki.save if @request.post? @wiki.save if request.post?
render(:update) {|page| page.replace_html "tab-content-wiki", :partial => 'projects/settings/wiki'} render(:update) {|page| page.replace_html "tab-content-wiki", :partial => 'projects/settings/wiki'}
end end
+51 -13
View File
@@ -16,6 +16,7 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
module ApplicationHelper module ApplicationHelper
include Redmine::WikiFormatting::Macros::Definitions
def current_role def current_role
@current_role ||= User.current.role_for_project(@project) @current_role ||= User.current.role_for_project(@project)
@@ -33,7 +34,7 @@ module ApplicationHelper
# Display a link to user's account page # Display a link to user's account page
def link_to_user(user) def link_to_user(user)
link_to user.name, :controller => 'account', :action => 'show', :id => user user ? link_to(user, :controller => 'account', :action => 'show', :id => user) : 'Anonymous'
end end
def link_to_issue(issue) def link_to_issue(issue)
@@ -70,20 +71,28 @@ module ApplicationHelper
def format_date(date) def format_date(date)
return nil unless date return nil unless date
@date_format ||= (Setting.date_format.to_i == 0 ? l(:general_fmt_date) : "%Y-%m-%d") # "Setting.date_format.size < 2" is a temporary fix (content of date_format setting changed)
@date_format ||= (Setting.date_format.blank? || Setting.date_format.size < 2 ? l(:general_fmt_date) : Setting.date_format)
date.strftime(@date_format) date.strftime(@date_format)
end end
def format_time(time) def format_time(time, include_date = true)
return nil unless time return nil unless time
@date_format_setting ||= Setting.date_format.to_i
time = time.to_time if time.is_a?(String) time = time.to_time if time.is_a?(String)
@date_format_setting == 0 ? l_datetime(time) : (time.strftime("%Y-%m-%d") + ' ' + l_time(time)) zone = User.current.time_zone
if time.utc?
local = zone ? zone.adjust(time) : time.getlocal
else
local = zone ? zone.adjust(time.getutc) : time
end
@date_format ||= (Setting.date_format.blank? || Setting.date_format.size < 2 ? l(:general_fmt_date) : Setting.date_format)
@time_format ||= (Setting.time_format.blank? ? l(:general_fmt_time) : Setting.time_format)
include_date ? local.strftime("#{@date_format} #{@time_format}") : local.strftime(@time_format)
end end
def authoring(created, author) def authoring(created, author)
time_tag = content_tag('acronym', distance_of_time_in_words(Time.now, created), :title => format_time(created)) time_tag = content_tag('acronym', distance_of_time_in_words(Time.now, created), :title => format_time(created))
l(:label_added_time_by, author.name, time_tag) l(:label_added_time_by, author || 'Anonymous', time_tag)
end end
def day_name(day) def day_name(day)
@@ -130,15 +139,28 @@ module ApplicationHelper
:preview => 'r', :preview => 'r',
:quick_search => 'f', :quick_search => 'f',
:search => '4', :search => '4',
}.freeze }.freeze unless const_defined?(:ACCESSKEYS)
def accesskey(s) def accesskey(s)
ACCESSKEYS[s] ACCESSKEYS[s]
end end
# format text according to system settings # Formats text according to system settings.
def textilizable(text, options = {}) # 2 ways to call this method:
return "" if text.blank? # * with a String: textilizable(text, options)
# * with an object and one of its attribute: textilizable(issue, :description, options)
def textilizable(*args)
options = args.last.is_a?(Hash) ? args.pop : {}
case args.size
when 1
obj = nil
text = args.shift || ''
when 2
obj = args.shift
text = obj.send(args.shift)
else
raise ArgumentError, 'invalid arguments to textilizable'
end
# when using an image link, try to use an attachment, if possible # when using an image link, try to use an attachment, if possible
attachments = options[:attachments] attachments = options[:attachments]
@@ -158,7 +180,8 @@ module ApplicationHelper
end end
text = (Setting.text_formatting == 'textile') ? text = (Setting.text_formatting == 'textile') ?
Redmine::WikiFormatting.to_html(text) : simple_format(auto_link(h(text))) Redmine::WikiFormatting.to_html(text) { |macro, args| exec_macro(macro, obj, args) } :
simple_format(auto_link(h(text)))
# different methods for formatting wiki links # different methods for formatting wiki links
case options[:wiki_links] case options[:wiki_links]
@@ -208,7 +231,7 @@ module ApplicationHelper
# example: # example:
# #52 -> <a href="/issues/show/52">#52</a> # #52 -> <a href="/issues/show/52">#52</a>
# r52 -> <a href="/repositories/revision/6?rev=52">r52</a> (project.id is 6) # r52 -> <a href="/repositories/revision/6?rev=52">r52</a> (project.id is 6)
text = text.gsub(%r{([\s,-^])(#|r)(\d+)(?=[[:punct:]]|\s|<|$)}) do |m| text = text.gsub(%r{([\s\(,-^])(#|r)(\d+)(?=[[:punct:]]|\s|<|$)}) do |m|
leading, otype, oid = $1, $2, $3 leading, otype, oid = $1, $2, $3
link = nil link = nil
if otype == 'r' if otype == 'r'
@@ -276,7 +299,7 @@ module ApplicationHelper
def lang_options_for_select(blank=true) def lang_options_for_select(blank=true)
(blank ? [["(auto)", ""]] : []) + (blank ? [["(auto)", ""]] : []) +
GLoc.valid_languages.collect{|lang| [ ll(lang.to_s, :general_lang_name), lang.to_s]}.sort{|x,y| x.first <=> y.first } GLoc.valid_languages.collect{|lang| [ ll(lang.to_s, :general_lang_name), lang.to_s]}.sort{|x,y| x.last <=> y.last }
end end
def label_tag_for(name, option_tags = nil, options = {}) def label_tag_for(name, option_tags = nil, options = {})
@@ -296,6 +319,21 @@ module ApplicationHelper
link_to_function(l(:button_uncheck_all), "checkAll('#{form_name}', false)") link_to_function(l(:button_uncheck_all), "checkAll('#{form_name}', false)")
end end
def progress_bar(pcts, options={})
pcts = [pcts, pcts] unless pcts.is_a?(Array)
pcts[1] = pcts[1] - pcts[0]
pcts << (100 - pcts[1] - pcts[0])
width = options[:width] || '100px;'
legend = options[:legend] || ''
content_tag('table',
content_tag('tr',
(pcts[0] > 0 ? content_tag('td', '', :width => "#{pcts[0].floor}%;", :class => 'closed') : '') +
(pcts[1] > 0 ? content_tag('td', '', :width => "#{pcts[1].floor}%;", :class => 'done') : '') +
(pcts[2] > 0 ? content_tag('td', '', :width => "#{pcts[2].floor}%;", :class => 'todo') : '')
), :class => 'progress', :style => "width: #{width};") +
content_tag('p', legend, :class => 'pourcent')
end
def context_menu_link(name, url, options={}) def context_menu_link(name, url, options={})
options[:class] ||= '' options[:class] ||= ''
if options.delete(:selected) if options.delete(:selected)
+55
View File
@@ -15,7 +15,10 @@
# along with this program; if not, write to the Free Software # along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require 'csv'
module IssuesHelper module IssuesHelper
include ApplicationHelper
def render_issue_tooltip(issue) def render_issue_tooltip(issue)
@cached_label_start_date ||= l(:field_start_date) @cached_label_start_date ||= l(:field_start_date)
@@ -101,4 +104,56 @@ module IssuesHelper
end end
end end
end end
def issues_to_csv(issues, project = nil)
ic = Iconv.new(l(:general_csv_encoding), 'UTF-8')
export = StringIO.new
CSV::Writer.generate(export, l(:general_csv_separator)) do |csv|
# csv header fields
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_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_custom_fields
custom_fields.each {|f| headers << f.name}
csv << headers.collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end }
# csv lines
issues.each do |issue|
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,
format_time(issue.created_on),
format_time(issue.updated_on)
]
custom_fields.each {|f| fields << show_value(issue.custom_value_for(f)) }
csv << fields.collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end }
end
end
export.rewind
export
end
end end
+3 -3
View File
@@ -179,9 +179,9 @@ module ProjectsHelper
end end
# today red line # today red line
if Date.today >= @date_from and Date.today <= @date_to if Date.today >= date_from and Date.today <= date_to
gc.stroke('red') gc.stroke('red')
x = (Date.today-@date_from+1)*zoom + subject_width x = (Date.today-date_from+1)*zoom + subject_width
gc.line(x, headers_heigth, x, headers_heigth + g_height-1) gc.line(x, headers_heigth, x, headers_heigth + g_height-1)
end end
@@ -190,7 +190,7 @@ module ProjectsHelper
end if Object.const_defined?(:Magick) end if Object.const_defined?(:Magick)
def new_issue_selector def new_issue_selector
trackers = Tracker.find(:all, :order => 'position') trackers = @project.trackers
# can't use form tag inside helper # can't use form tag inside helper
content_tag('form', content_tag('form',
select_tag('tracker_id', '<option></option>' + options_from_collection_for_select(trackers, 'id', 'name'), :onchange => "if (this.value != '') {this.form.submit()}"), select_tag('tracker_id', '<option></option>' + options_from_collection_for_select(trackers, 'id', 'name'), :onchange => "if (this.value != '') {this.form.submit()}"),
+20 -14
View File
@@ -22,24 +22,30 @@ module QueriesHelper
end end
def column_header(column) def column_header(column)
if column.sortable column.sortable ? sort_header_tag(column.sortable, :caption => column.caption) : content_tag('th', column.caption)
sort_header_tag(column.sortable, :caption => l("field_#{column.name}"))
else
content_tag('th', l("field_#{column.name}"))
end
end end
def column_content(column, issue) def column_content(column, issue)
value = issue.send(column.name) if column.is_a?(QueryCustomFieldColumn)
if value.is_a?(Date) cv = issue.custom_values.detect {|v| v.custom_field_id == column.custom_field.id}
format_date(value) show_value(cv)
elsif value.is_a?(Time)
format_time(value)
elsif column.name == :subject
((@project.nil? || @project != issue.project) ? "#{issue.project.name} - " : '') +
link_to(h(value), :controller => 'issues', :action => 'show', :id => issue)
else else
h(value) value = issue.send(column.name)
if value.is_a?(Date)
format_date(value)
elsif value.is_a?(Time)
format_time(value)
else
case column.name
when :subject
h((@project.nil? || @project != issue.project) ? "#{issue.project.name} - " : '') +
link_to(h(value), :controller => 'issues', :action => 'show', :id => issue)
when :done_ratio
progress_bar(value, :width => '80px')
else
h(value)
end
end
end end
end end
end end
+8 -1
View File
@@ -62,7 +62,10 @@ module RepositoriesHelper
content_tag('p', form.text_field(:url, :size => 60, :required => true, :disabled => (repository && !repository.root_url.blank?)) + content_tag('p', form.text_field(:url, :size => 60, :required => true, :disabled => (repository && !repository.root_url.blank?)) +
'<br />(http://, https://, svn://, file:///)') + '<br />(http://, https://, svn://, file:///)') +
content_tag('p', form.text_field(:login, :size => 30)) + content_tag('p', form.text_field(:login, :size => 30)) +
content_tag('p', form.password_field(:password, :size => 30)) content_tag('p', form.password_field(:password, :size => 30, :name => 'ignore',
:value => ((repository.new_record? || repository.password.blank?) ? '' : ('x'*15)),
:onfocus => "this.value=''; this.name='repository[password]';",
:onchange => "this.name='repository[password]';"))
end end
def darcs_field_tags(form, repository) def darcs_field_tags(form, repository)
@@ -77,4 +80,8 @@ module RepositoriesHelper
content_tag('p', form.text_field(:root_url, :label => 'CVSROOT', :size => 60, :required => true, :disabled => !repository.new_record?)) + content_tag('p', form.text_field(:root_url, :label => 'CVSROOT', :size => 60, :required => true, :disabled => !repository.new_record?)) +
content_tag('p', form.text_field(:url, :label => 'Module', :size => 30, :required => true, :disabled => !repository.new_record?)) content_tag('p', form.text_field(:url, :label => 'Module', :size => 30, :required => true, :disabled => !repository.new_record?))
end end
def bazaar_field_tags(form, repository)
content_tag('p', form.text_field(:url, :label => 'Root directory', :size => 60, :required => true, :disabled => (repository && !repository.new_record?)))
end
end end
+28
View File
@@ -16,4 +16,32 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
module VersionsHelper module VersionsHelper
STATUS_BY_CRITERIAS = %w(category tracker priority author assigned_to)
def render_issue_status_by(version, criteria)
criteria ||= 'category'
raise 'Unknown criteria' unless STATUS_BY_CRITERIAS.include?(criteria)
h = Hash.new {|k,v| k[v] = [0, 0]}
begin
# Total issue count
Issue.count(:group => criteria,
:conditions => ["#{Issue.table_name}.fixed_version_id = ?", version.id]).each {|c,s| h[c][0] = s}
# Open issues count
Issue.count(:group => criteria,
:include => :status,
:conditions => ["#{Issue.table_name}.fixed_version_id = ? AND #{IssueStatus.table_name}.is_closed = ?", version.id, false]).each {|c,s| h[c][1] = s}
rescue ActiveRecord::RecordNotFound
# When grouping by an association, Rails throws this exception if there's no result (bug)
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
end end
+2 -7
View File
@@ -21,7 +21,7 @@ class Attachment < ActiveRecord::Base
belongs_to :container, :polymorphic => true belongs_to :container, :polymorphic => true
belongs_to :author, :class_name => "User", :foreign_key => "author_id" belongs_to :author, :class_name => "User", :foreign_key => "author_id"
validates_presence_of :container, :filename validates_presence_of :container, :filename, :author
validates_length_of :filename, :maximum => 255 validates_length_of :filename, :maximum => 255
validates_length_of :disk_filename, :maximum => 255 validates_length_of :disk_filename, :maximum => 255
@@ -82,14 +82,9 @@ class Attachment < ActiveRecord::Base
def increment_download def increment_download
increment!(:downloads) increment!(:downloads)
end end
# returns last created projects
def self.most_downloaded
find(:all, :limit => 5, :order => "downloads DESC")
end
def project def project
container.is_a?(Project) ? container : container.project container.project
end end
def image? def image?
+4
View File
@@ -20,6 +20,10 @@ class AuthSource < ActiveRecord::Base
validates_presence_of :name validates_presence_of :name
validates_uniqueness_of :name validates_uniqueness_of :name
validates_length_of :name, :host, :maximum => 60
validates_length_of :account_password, :maximum => 60, :allow_nil => true
validates_length_of :account, :base_dn, :maximum => 255
validates_length_of :attr_login, :attr_firstname, :attr_lastname, :attr_mail, :maximum => 30
def authenticate(login, password) def authenticate(login, password)
end end
+1
View File
@@ -27,6 +27,7 @@ class AuthSourceLdap < AuthSource
end end
def authenticate(login, password) def authenticate(login, password)
return nil if login.blank? || password.blank?
attrs = [] attrs = []
# get user's DN # get user's DN
ldap_con = initialize_ldap_con(self.account, self.account_password) ldap_con = initialize_ldap_con(self.account, self.account_password)
+19
View File
@@ -63,6 +63,14 @@ class Changeset < ActiveRecord::Base
return if kw_regexp.blank? return if kw_regexp.blank?
referenced_issues = [] referenced_issues = []
if ref_keywords.delete('*')
# find any issue ID in the comments
target_issue_ids = []
comments.scan(%r{([\s\(,-^])#(\d+)(?=[[:punct:]]|\s|<|$)}).each { |m| target_issue_ids << m[1] }
referenced_issues += repository.project.issues.find_all_by_id(target_issue_ids)
end
comments.scan(Regexp.new("(#{kw_regexp})[\s:]+(([\s,;&]*#?\\d+)+)", Regexp::IGNORECASE)).each do |match| comments.scan(Regexp.new("(#{kw_regexp})[\s:]+(([\s,;&]*#?\\d+)+)", Regexp::IGNORECASE)).each do |match|
action = match[0] action = match[0]
target_issue_ids = match[1].scan(/\d+/) target_issue_ids = match[1].scan(/\d+/)
@@ -80,6 +88,17 @@ class Changeset < ActiveRecord::Base
end end
referenced_issues += target_issues referenced_issues += target_issues
end end
self.issues = referenced_issues.uniq self.issues = referenced_issues.uniq
end end
# Returns the previous changeset
def previous
@previous ||= Changeset.find(:first, :conditions => ['revision < ? AND repository_id = ?', self.revision, self.repository_id], :order => 'revision DESC')
end
# Returns the next changeset
def next
@next ||= Changeset.find(:first, :conditions => ['revision > ? AND repository_id = ?', self.revision, self.repository_id], :order => 'revision ASC')
end
end end
+8
View File
@@ -17,6 +17,7 @@
class CustomField < ActiveRecord::Base class CustomField < ActiveRecord::Base
has_many :custom_values, :dependent => :delete_all has_many :custom_values, :dependent => :delete_all
acts_as_list :scope => 'type = \'#{self.class}\''
serialize :possible_values serialize :possible_values
FIELD_FORMATS = { "string" => { :name => :label_string, :order => 1 }, FIELD_FORMATS = { "string" => { :name => :label_string, :order => 1 },
@@ -42,6 +43,9 @@ class CustomField < ActiveRecord::Base
def before_validation def before_validation
# remove empty values # remove empty values
self.possible_values = self.possible_values.collect{|v| v unless v.empty?}.compact self.possible_values = self.possible_values.collect{|v| v unless v.empty?}.compact
# make sure these fields are not searchable
self.searchable = false if %w(int float date bool).include?(field_format)
true
end end
def validate def validate
@@ -51,6 +55,10 @@ class CustomField < ActiveRecord::Base
end end
end end
def <=>(field)
position <=> field.position
end
# to move in project_custom_field # to move in project_custom_field
def self.for_all def self.for_all
find(:all, :conditions => ["is_for_all=?", true]) find(:all, :conditions => ["is_for_all=?", true])
+2 -2
View File
@@ -31,9 +31,9 @@ protected
when 'float' when 'float'
begin; !value.blank? && Kernel.Float(value); rescue; errors.add(:value, :activerecord_error_invalid) end begin; !value.blank? && Kernel.Float(value); rescue; errors.add(:value, :activerecord_error_invalid) end
when 'date' when 'date'
errors.add(:value, :activerecord_error_not_a_date) unless value =~ /^\d{4}-\d{2}-\d{2}$/ or value.empty? errors.add(:value, :activerecord_error_not_a_date) unless value =~ /^\d{4}-\d{2}-\d{2}$/ or value.blank?
when 'list' when 'list'
errors.add(:value, :activerecord_error_inclusion) unless custom_field.possible_values.include? value or value.empty? errors.add(:value, :activerecord_error_inclusion) unless custom_field.possible_values.include?(value) or value.blank?
end end
end end
end end
+1
View File
@@ -22,6 +22,7 @@ class Document < ActiveRecord::Base
acts_as_searchable :columns => ['title', 'description'] acts_as_searchable :columns => ['title', 'description']
acts_as_event :title => Proc.new {|o| "#{l(:label_document)}: #{o.title}"}, acts_as_event :title => Proc.new {|o| "#{l(:label_document)}: #{o.title}"},
:author => Proc.new {|o| (a = o.attachments.find(:first, :order => "#{Attachment.table_name}.created_on ASC")) ? a.author : nil },
:url => Proc.new {|o| {:controller => 'documents', :action => 'show', :id => o.id}} :url => Proc.new {|o| {:controller => 'documents', :action => 'show', :id => o.id}}
validates_presence_of :project, :title, :category validates_presence_of :project, :title, :category
+31 -1
View File
@@ -40,7 +40,7 @@ class Issue < ActiveRecord::Base
acts_as_event :title => Proc.new {|o| "#{o.tracker.name} ##{o.id}: #{o.subject}"}, acts_as_event :title => Proc.new {|o| "#{o.tracker.name} ##{o.id}: #{o.subject}"},
:url => Proc.new {|o| {:controller => 'issues', :action => 'show', :id => o.id}} :url => Proc.new {|o| {:controller => 'issues', :action => 'show', :id => o.id}}
validates_presence_of :subject, :description, :priority, :tracker, :author, :status validates_presence_of :subject, :description, :priority, :project, :tracker, :author, :status
validates_length_of :subject, :maximum => 255 validates_length_of :subject, :maximum => 255
validates_inclusion_of :done_ratio, :in => 0..100 validates_inclusion_of :done_ratio, :in => 0..100
validates_numericality_of :estimated_hours, :allow_nil => true validates_numericality_of :estimated_hours, :allow_nil => true
@@ -61,6 +61,32 @@ class Issue < ActiveRecord::Base
self self
end end
# Move an issue to a new project and tracker
def move_to(new_project, new_tracker = nil)
transaction do
if new_project && project_id != new_project.id
# delete issue relations
self.relations_from.clear
self.relations_to.clear
# issue is moved to another project
self.category = nil
self.fixed_version = nil
self.project = new_project
end
if new_tracker
self.tracker = new_tracker
end
if save
# Manually update project_id on related time entries
TimeEntry.update_all("project_id = #{new_project.id}", {:issue_id => id})
else
rollback_db_transaction
return false
end
end
return true
end
def priority_id=(pid) def priority_id=(pid)
self.priority = nil self.priority = nil
write_attribute(:priority_id, pid) write_attribute(:priority_id, pid)
@@ -80,6 +106,10 @@ class Issue < ActiveRecord::Base
end end
end end
def validate_on_create
errors.add :tracker_id, :activerecord_error_invalid unless project.trackers.include?(tracker)
end
def before_create def before_create
# default assignment based on category # default assignment based on category
if assigned_to.nil? && category && category.assigned_to if assigned_to.nil? && category && category.assigned_to
+4
View File
@@ -35,5 +35,9 @@ class IssueCategory < ActiveRecord::Base
destroy_without_reassign destroy_without_reassign
end end
def <=>(category)
name <=> category.name
end
def to_s; name end def to_s; name end
end end
+11
View File
@@ -29,8 +29,19 @@ class Journal < ActiveRecord::Base
:project_key => "#{Issue.table_name}.project_id", :project_key => "#{Issue.table_name}.project_id",
:date_column => "#{Issue.table_name}.created_on" :date_column => "#{Issue.table_name}.created_on"
acts_as_event :title => Proc.new {|o| "#{o.issue.tracker.name} ##{o.issue.id}: #{o.issue.subject}" + ((s = o.new_status) ? " (#{s})" : '') },
:description => :notes,
:author => :user,
:url => Proc.new {|o| {:controller => 'issues', :action => 'show', :id => o.issue.id}}
def save def save
# Do not save an empty journal # Do not save an empty journal
(details.empty? && notes.blank?) ? false : super (details.empty? && notes.blank?) ? false : super
end end
# Returns the new status if the journal contains a status change, otherwise nil
def new_status
c = details.detect {|detail| detail.prop_key == 'status_id'}
(c && c.value) ? IssueStatus.find_by_id(c.value.to_i) : nil
end
end end
+32 -2
View File
@@ -88,6 +88,14 @@ class Mailer < ActionMailer::Base
:password => password, :password => password,
:login_url => url_for(:controller => 'account', :action => 'login') :login_url => url_for(:controller => 'account', :action => 'login')
end end
def account_activation_request(user)
# Send the email to all active administrators
recipients User.find_active(:all, :conditions => {:admin => true}).collect { |u| u.mail }.compact
subject l(:mail_subject_account_activation_request)
body :user => user,
:url => url_for(:controller => 'users', :action => 'index', :status => User::STATUS_REGISTERED, :sort_key => 'created_on', :sort_order => 'desc')
end
def lost_password(token) def lost_password(token)
set_language_if_valid(token.user.language) set_language_if_valid(token.user.language)
@@ -102,7 +110,7 @@ class Mailer < ActionMailer::Base
recipients token.user.mail recipients token.user.mail
subject l(:mail_subject_register) subject l(:mail_subject_register)
body :token => token, body :token => token,
:url => url_for(:controller => 'account', :action => 'register', :token => token.value) :url => url_for(:controller => 'account', :action => 'activate', :token => token.value)
end end
def test(user) def test(user)
@@ -121,10 +129,32 @@ class Mailer < ActionMailer::Base
default_url_options[:protocol] = Setting.protocol default_url_options[:protocol] = Setting.protocol
end end
# Overrides the create_mail method
def create_mail
# Removes the current user from the recipients and cc
# if he doesn't want to receive notifications about what he does
if User.current.pref[:no_self_notified]
recipients.delete(User.current.mail) if recipients
cc.delete(User.current.mail) if cc
end
# Blind carbon copy recipients
if Setting.bcc_recipients?
bcc([recipients, cc].flatten.compact.uniq)
recipients []
cc []
end
super
end
# Renders a message with the corresponding layout # Renders a message with the corresponding layout
def render_message(method_name, body) def render_message(method_name, body)
layout = method_name.match(%r{text\.html\.(rhtml|rxml)}) ? 'layout.text.html.rhtml' : 'layout.text.plain.rhtml' layout = method_name.match(%r{text\.html\.(rhtml|rxml)}) ? 'layout.text.html.rhtml' : 'layout.text.plain.rhtml'
body[:content_for_layout] = render(:file => method_name, :body => body) body[:content_for_layout] = render(:file => method_name, :body => body)
ActionView::Base.new(File.join(template_root, 'mailer'), body, self).render(:file => layout) ActionView::Base.new(template_root, body, self).render(:file => "mailer/#{layout}")
end end
# Makes partial rendering work with Rails 1.2 (retro-compatibility)
def self.controller_path
''
end unless respond_to?('controller_path')
end end
+18
View File
@@ -30,9 +30,15 @@ class Message < ActiveRecord::Base
:description => :content, :description => :content,
:url => Proc.new {|o| {:controller => 'messages', :action => 'show', :board_id => o.board_id, :id => o.id}} :url => Proc.new {|o| {:controller => 'messages', :action => 'show', :board_id => o.board_id, :id => o.id}}
attr_protected :locked, :sticky
validates_presence_of :subject, :content validates_presence_of :subject, :content
validates_length_of :subject, :maximum => 255 validates_length_of :subject, :maximum => 255
def validate_on_create
# Can not reply to a locked topic
errors.add_to_base 'Topic is locked' if root.locked?
end
def after_create def after_create
board.update_attribute(:last_message_id, self.id) board.update_attribute(:last_message_id, self.id)
board.increment! :messages_count board.increment! :messages_count
@@ -43,6 +49,18 @@ class Message < ActiveRecord::Base
end end
end end
def after_destroy
# The following line is required so that the previous counter
# updates (due to children removal) are not overwritten
board.reload
board.decrement! :messages_count
board.decrement! :topics_count unless parent
end
def sticky?
sticky == 1
end
def project def project
board.project board.project
end end
+32 -8
View File
@@ -20,10 +20,11 @@ class Project < ActiveRecord::Base
STATUS_ACTIVE = 1 STATUS_ACTIVE = 1
STATUS_ARCHIVED = 9 STATUS_ARCHIVED = 9
has_many :members, :dependent => :delete_all, :include => :user, :conditions => "#{User.table_name}.status=#{User::STATUS_ACTIVE}" has_many :members, :include => :user, :conditions => "#{User.table_name}.status=#{User::STATUS_ACTIVE}"
has_many :users, :through => :members has_many :users, :through => :members
has_many :custom_values, :dependent => :delete_all, :as => :customized has_many :custom_values, :dependent => :delete_all, :as => :customized
has_many :enabled_modules, :dependent => :delete_all has_many :enabled_modules, :dependent => :delete_all
has_and_belongs_to_many :trackers, :order => "#{Tracker.table_name}.position"
has_many :issues, :dependent => :destroy, :order => "#{Issue.table_name}.created_on DESC", :include => [:status, :tracker] has_many :issues, :dependent => :destroy, :order => "#{Issue.table_name}.created_on DESC", :include => [:status, :tracker]
has_many :issue_changes, :through => :issues, :source => :journals has_many :issue_changes, :through => :issues, :source => :journals
has_many :versions, :dependent => :destroy, :order => "#{Version.table_name}.effective_date DESC, #{Version.table_name}.name DESC" has_many :versions, :dependent => :destroy, :order => "#{Version.table_name}.effective_date DESC, #{Version.table_name}.name DESC"
@@ -36,7 +37,13 @@ class Project < ActiveRecord::Base
has_one :repository, :dependent => :destroy has_one :repository, :dependent => :destroy
has_many :changesets, :through => :repository has_many :changesets, :through => :repository
has_one :wiki, :dependent => :destroy has_one :wiki, :dependent => :destroy
has_and_belongs_to_many :custom_fields, :class_name => 'IssueCustomField', :join_table => "#{table_name_prefix}custom_fields_projects#{table_name_suffix}", :association_foreign_key => 'custom_field_id' # Custom field for the project issues
has_and_belongs_to_many :custom_fields,
:class_name => 'IssueCustomField',
:order => "#{CustomField.table_name}.position",
:join_table => "#{table_name_prefix}custom_fields_projects#{table_name_suffix}",
:association_foreign_key => 'custom_field_id'
acts_as_tree :order => "name", :counter_cache => true acts_as_tree :order => "name", :counter_cache => true
acts_as_searchable :columns => ['name', 'description'], :project_key => 'id' acts_as_searchable :columns => ['name', 'description'], :project_key => 'id'
@@ -50,12 +57,13 @@ class Project < ActiveRecord::Base
validates_associated :custom_values, :on => :update validates_associated :custom_values, :on => :update
validates_associated :repository, :wiki validates_associated :repository, :wiki
validates_length_of :name, :maximum => 30 validates_length_of :name, :maximum => 30
validates_format_of :name, :with => /^[\w\s\'\-]*$/i
validates_length_of :description, :maximum => 255 validates_length_of :description, :maximum => 255
validates_length_of :homepage, :maximum => 60 validates_length_of :homepage, :maximum => 60
validates_length_of :identifier, :in => 3..12 validates_length_of :identifier, :in => 3..20
validates_format_of :identifier, :with => /^[a-z0-9\-]*$/ validates_format_of :identifier, :with => /^[a-z0-9\-]*$/
before_destroy :delete_all_members
def identifier=(identifier) def identifier=(identifier)
super unless identifier_frozen? super unless identifier_frozen?
end end
@@ -71,11 +79,22 @@ class Project < ActiveRecord::Base
conditions = ["#{Issue.table_name}.project_id IN (#{ids.join(',')})"] conditions = ["#{Issue.table_name}.project_id IN (#{ids.join(',')})"]
end end
conditions ||= ["#{Issue.table_name}.project_id = ?", id] conditions ||= ["#{Issue.table_name}.project_id = ?", id]
Issue.with_scope :find => { :conditions => conditions } do # Quick and dirty fix for Rails 2 compatibility
Issue.send(:with_scope, :find => { :conditions => conditions }) do
yield yield
end end
end end
# Return all issues status changes for the project between the 2 given dates
def issues_status_changes(from, to)
Journal.find(:all, :include => [:issue, :details, :user],
:conditions => ["#{Journal.table_name}.journalized_type = 'Issue'" +
" AND #{Issue.table_name}.project_id = ?" +
" AND #{JournalDetail.table_name}.prop_key = 'status_id'" +
" AND #{Journal.table_name}.created_on BETWEEN ? AND ?",
id, from, to+1])
end
# returns latest created projects # returns latest created projects
# non public projects will be returned only if user is a member of those # non public projects will be returned only if user is a member of those
def self.latest(user=nil, count=5) def self.latest(user=nil, count=5)
@@ -113,9 +132,14 @@ class Project < ActiveRecord::Base
children.select {|child| child.active?} children.select {|child| child.active?}
end end
# Deletes all project's members
def delete_all_members
Member.delete_all(['project_id = ?', id])
end
# Users issues can be assigned to # Users issues can be assigned to
def assignable_users def assignable_users
members.select {|m| m.role.assignable?}.collect {|m| m.user} members.select {|m| m.role.assignable?}.collect {|m| m.user}.sort
end end
# Returns the mail adresses of users that should be always notified on project events # Returns the mail adresses of users that should be always notified on project events
@@ -134,7 +158,7 @@ class Project < ActiveRecord::Base
end end
def <=>(project) def <=>(project)
name <=> project.name name.downcase <=> project.name.downcase
end end
def allows_to?(action) def allows_to?(action)
+38 -16
View File
@@ -17,13 +17,34 @@
class QueryColumn class QueryColumn
attr_accessor :name, :sortable attr_accessor :name, :sortable
include GLoc
def initialize(name, options={}) def initialize(name, options={})
self.name = name self.name = name
self.sortable = options[:sortable] self.sortable = options[:sortable]
end end
def default?; default end def caption
set_language_if_valid(User.current.language)
l("field_#{name}")
end
end
class QueryCustomFieldColumn < QueryColumn
def initialize(custom_field)
self.name = "cf_#{custom_field.id}".to_sym
self.sortable = false
@cf = custom_field
end
def caption
@cf.name
end
def custom_field
@cf
end
end end
class Query < ActiveRecord::Base class Query < ActiveRecord::Base
@@ -33,7 +54,6 @@ class Query < ActiveRecord::Base
serialize :column_names serialize :column_names
attr_protected :project, :user attr_protected :project, :user
attr_accessor :executed_by
validates_presence_of :name, :on => :save validates_presence_of :name, :on => :save
validates_length_of :name, :maximum => 255 validates_length_of :name, :maximum => 255
@@ -79,6 +99,7 @@ class Query < ActiveRecord::Base
QueryColumn.new(:assigned_to, :sortable => "#{User.table_name}.lastname"), QueryColumn.new(:assigned_to, :sortable => "#{User.table_name}.lastname"),
QueryColumn.new(:updated_on, :sortable => "#{Issue.table_name}.updated_on"), QueryColumn.new(:updated_on, :sortable => "#{Issue.table_name}.updated_on"),
QueryColumn.new(:category, :sortable => "#{IssueCategory.table_name}.name"), QueryColumn.new(:category, :sortable => "#{IssueCategory.table_name}.name"),
QueryColumn.new(:fixed_version),
QueryColumn.new(:start_date, :sortable => "#{Issue.table_name}.start_date"), QueryColumn.new(:start_date, :sortable => "#{Issue.table_name}.start_date"),
QueryColumn.new(:due_date, :sortable => "#{Issue.table_name}.due_date"), QueryColumn.new(:due_date, :sortable => "#{Issue.table_name}.due_date"),
QueryColumn.new(:estimated_hours, :sortable => "#{Issue.table_name}.estimated_hours"), QueryColumn.new(:estimated_hours, :sortable => "#{Issue.table_name}.estimated_hours"),
@@ -90,11 +111,7 @@ class Query < ActiveRecord::Base
def initialize(attributes = nil) def initialize(attributes = nil)
super attributes super attributes
self.filters ||= { 'status_id' => {:operator => "o", :values => [""]} } self.filters ||= { 'status_id' => {:operator => "o", :values => [""]} }
end set_language_if_valid(User.current.language)
def executed_by=(user)
@executed_by = user
set_language_if_valid(user.language) if user
end end
def validate def validate
@@ -126,12 +143,12 @@ class Query < ActiveRecord::Base
"done_ratio" => { :type => :integer, :order => 13 }} "done_ratio" => { :type => :integer, :order => 13 }}
user_values = [] user_values = []
user_values << ["<< #{l(:label_me)} >>", "me"] if User.current.logged?
if project if project
user_values += project.users.collect{|s| [s.name, s.id.to_s] } user_values += project.users.sort.collect{|s| [s.name, s.id.to_s] }
elsif executed_by else
user_values << ["<< #{l(:label_me)} >>", "me"] if executed_by
# members of the user's projects # members of the user's projects
user_values += executed_by.projects.collect(&:users).flatten.uniq.sort.collect{|s| [s.name, s.id.to_s] } user_values += User.current.projects.collect(&:users).flatten.uniq.sort.collect{|s| [s.name, s.id.to_s] }
end end
@available_filters["assigned_to_id"] = { :type => :list_optional, :order => 4, :values => user_values } unless user_values.empty? @available_filters["assigned_to_id"] = { :type => :list_optional, :order => 4, :values => user_values } unless user_values.empty?
@available_filters["author_id"] = { :type => :list, :order => 5, :values => user_values } unless user_values.empty? @available_filters["author_id"] = { :type => :list, :order => 5, :values => user_values } unless user_values.empty?
@@ -145,8 +162,6 @@ class Query < ActiveRecord::Base
end end
@project.all_custom_fields.select(&:is_filter?).each do |field| @project.all_custom_fields.select(&:is_filter?).each do |field|
case field.field_format case field.field_format
when "string", "int"
options = { :type => :string, :order => 20 }
when "text" when "text"
options = { :type => :text, :order => 20 } options = { :type => :text, :order => 20 }
when "list" when "list"
@@ -155,6 +170,8 @@ class Query < ActiveRecord::Base
options = { :type => :date, :order => 20 } options = { :type => :date, :order => 20 }
when "bool" when "bool"
options = { :type => :list, :values => [[l(:general_text_yes), "1"], [l(:general_text_no), "0"]], :order => 20 } options = { :type => :list, :values => [[l(:general_text_yes), "1"], [l(:general_text_no), "0"]], :order => 20 }
else
options = { :type => :string, :order => 20 }
end end
@available_filters["cf_#{field.id}"] = options.merge({ :name => field.name }) @available_filters["cf_#{field.id}"] = options.merge({ :name => field.name })
end end
@@ -203,7 +220,12 @@ class Query < ActiveRecord::Base
end end
def available_columns def available_columns
cols = Query.available_columns return @available_columns if @available_columns
@available_columns = Query.available_columns
@available_columns += (project ?
project.custom_fields :
IssueCustomField.find(:all, :conditions => {:is_for_all => true})
).collect {|cf| QueryCustomFieldColumn.new(cf) }
end end
def columns def columns
@@ -243,7 +265,7 @@ class Query < ActiveRecord::Base
elsif project elsif project
clause << "#{Issue.table_name}.project_id=%d" % project.id clause << "#{Issue.table_name}.project_id=%d" % project.id
else else
clause << Project.visible_by(executed_by) clause << Project.visible_by(User.current)
end end
# filters clauses # filters clauses
@@ -268,7 +290,7 @@ class Query < ActiveRecord::Base
# "me" value subsitution # "me" value subsitution
if %w(assigned_to_id author_id).include?(field) if %w(assigned_to_id author_id).include?(field)
v.push(executed_by ? executed_by.id.to_s : "0") if v.delete("me") v.push(User.current.logged? ? User.current.id.to_s : "0") if v.delete("me")
end end
case operator_for field case operator_for field
+4
View File
@@ -33,6 +33,10 @@ class Repository < ActiveRecord::Base
def supports_cat? def supports_cat?
scm.supports_cat? scm.supports_cat?
end end
def supports_annotate?
scm.supports_annotate?
end
def entries(path=nil, identifier=nil) def entries(path=nil, identifier=nil)
scm.entries(path, identifier) scm.entries(path, identifier)
+86
View File
@@ -0,0 +1,86 @@
# 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.
require 'redmine/scm/adapters/bazaar_adapter'
class Repository::Bazaar < Repository
attr_protected :root_url
validates_presence_of :url
def scm_adapter
Redmine::Scm::Adapters::BazaarAdapter
end
def self.scm_name
'Bazaar'
end
def entries(path=nil, identifier=nil)
entries = scm.entries(path, identifier)
if entries
entries.each do |e|
next if e.lastrev.revision.blank?
c = Change.find(:first,
:include => :changeset,
:conditions => ["#{Change.table_name}.revision = ? and #{Changeset.table_name}.repository_id = ?", e.lastrev.revision, id],
:order => "#{Changeset.table_name}.revision DESC")
if c
e.lastrev.identifier = c.changeset.revision
e.lastrev.name = c.changeset.revision
e.lastrev.author = c.changeset.committer
end
end
end
end
def fetch_changesets
scm_info = scm.info
if scm_info
# latest revision found in database
db_revision = latest_changeset ? latest_changeset.revision : 0
# latest revision in the repository
scm_revision = scm_info.lastrev.identifier.to_i
if db_revision < scm_revision
logger.debug "Fetching changesets for repository #{url}" if logger && logger.debug?
identifier_from = db_revision + 1
while (identifier_from <= scm_revision)
# loads changesets by batches of 200
identifier_to = [identifier_from + 199, scm_revision].min
revisions = scm.revisions('', identifier_to, identifier_from, :with_paths => true)
transaction do
revisions.reverse_each do |revision|
changeset = Changeset.create(:repository => self,
:revision => revision.identifier,
:committer => revision.author,
:committed_on => revision.time,
:scmid => revision.scmid,
:comments => revision.message)
revision.paths.each do |change|
Change.create(:changeset => changeset,
:action => change[:action],
:path => change[:path],
:revision => change[:revision])
end
end
end unless revisions.nil?
identifier_from = identifier_to + 1
end
end
end
end
end
+17
View File
@@ -17,6 +17,23 @@
class Setting < ActiveRecord::Base class Setting < ActiveRecord::Base
DATE_FORMATS = [
'%Y-%m-%d',
'%d/%m/%Y',
'%d.%m.%Y',
'%d-%m-%Y',
'%m/%d/%Y',
'%d %b %Y',
'%d %B %Y',
'%b %d, %Y',
'%B %d, %Y'
]
TIME_FORMATS = [
'%H:%M',
'%I:%M %p'
]
cattr_accessor :available_settings cattr_accessor :available_settings
@@available_settings = YAML::load(File.open("#{RAILS_ROOT}/config/settings.yml")) @@available_settings = YAML::load(File.open("#{RAILS_ROOT}/config/settings.yml"))
Redmine::Plugin.registered_plugins.each do |id, plugin| Redmine::Plugin.registered_plugins.each do |id, plugin|
+8
View File
@@ -29,6 +29,14 @@ class Tracker < ActiveRecord::Base
def to_s; name end def to_s; name end
def <=>(tracker)
name <=> tracker.name
end
def self.all
find(:all, :order => 'position')
end
private private
def check_integrity def check_integrity
raise "Can't delete tracker" if Issue.find(:first, :conditions => ["tracker_id=?", self.id]) raise "Can't delete tracker" if Issue.find(:first, :conditions => ["tracker_id=?", self.id])
+41 -22
View File
@@ -19,6 +19,7 @@ require "digest/sha1"
class User < ActiveRecord::Base class User < ActiveRecord::Base
# Account statuses # Account statuses
STATUS_ANONYMOUS = 0
STATUS_ACTIVE = 1 STATUS_ACTIVE = 1
STATUS_REGISTERED = 2 STATUS_REGISTERED = 2
STATUS_LOCKED = 3 STATUS_LOCKED = 3
@@ -36,17 +37,16 @@ class User < ActiveRecord::Base
# Prevents unauthorized assignments # Prevents unauthorized assignments
attr_protected :login, :admin, :password, :password_confirmation, :hashed_password attr_protected :login, :admin, :password, :password_confirmation, :hashed_password
validates_presence_of :login, :firstname, :lastname, :mail validates_presence_of :login, :firstname, :lastname, :mail, :if => Proc.new { |user| !user.is_a?(AnonymousUser) }
validates_uniqueness_of :login, :mail validates_uniqueness_of :login, :mail
# Login must contain lettres, numbers, underscores only # Login must contain lettres, numbers, underscores only
validates_format_of :login, :with => /^[a-z0-9_\-@\.]+$/i validates_format_of :login, :with => /^[a-z0-9_\-@\.]*$/i
validates_length_of :login, :maximum => 30 validates_length_of :login, :maximum => 30
validates_format_of :firstname, :lastname, :with => /^[\w\s\'\-]*$/i validates_format_of :firstname, :lastname, :with => /^[\w\s\'\-]*$/i
validates_length_of :firstname, :lastname, :maximum => 30 validates_length_of :firstname, :lastname, :maximum => 30
validates_format_of :mail, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i validates_format_of :mail, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i, :allow_nil => true
validates_length_of :mail, :maximum => 60 validates_length_of :mail, :maximum => 60, :allow_nil => true
# Password length between 4 and 12 validates_length_of :password, :minimum => 4, :allow_nil => true
validates_length_of :password, :in => 4..12, :allow_nil => true
validates_confirmation_of :password, :allow_nil => true validates_confirmation_of :password, :allow_nil => true
validates_associated :custom_values, :on => :update validates_associated :custom_values, :on => :update
@@ -74,6 +74,8 @@ class User < ActiveRecord::Base
# Returns the user that matches provided login and password, or nil # Returns the user that matches provided login and password, or nil
def self.try_to_login(login, password) def self.try_to_login(login, password)
# Make sure no one can sign in with an empty password
return nil if password.to_s.empty?
user = find(:first, :conditions => ["login=?", login]) user = find(:first, :conditions => ["login=?", login])
if user if user
# user is already in local database # user is already in local database
@@ -130,6 +132,10 @@ class User < ActiveRecord::Base
self.preference ||= UserPreference.new(:user => self) self.preference ||= UserPreference.new(:user => self)
end end
def time_zone
self.pref.time_zone.nil? ? nil : TimeZone[self.pref.time_zone]
end
# Return user's RSS key (a 40 chars long string), used to access feeds # Return user's RSS key (a 40 chars long string), used to access feeds
def rss_key def rss_key
token = self.rss_token || Token.create(:user => self, :action => 'feeds') token = self.rss_token || Token.create(:user => self, :action => 'feeds')
@@ -159,7 +165,7 @@ class User < ActiveRecord::Base
end end
def <=>(user) def <=>(user)
lastname == user.lastname ? firstname <=> user.firstname : lastname <=> user.lastname user.nil? ? -1 : (lastname == user.lastname ? firstname <=> user.firstname : lastname <=> user.lastname)
end end
def to_s def to_s
@@ -174,14 +180,16 @@ class User < ActiveRecord::Base
def role_for_project(project) def role_for_project(project)
# No role on archived projects # No role on archived projects
return nil unless project && project.active? return nil unless project && project.active?
# Find project membership if logged?
membership = memberships.detect {|m| m.project_id == project.id} # Find project membership
if membership membership = memberships.detect {|m| m.project_id == project.id}
membership.role if membership
elsif logged? membership.role
Role.non_member else
@role_non_member ||= Role.non_member
end
else else
Role.anonymous @role_anonymous ||= Role.anonymous
end end
end end
@@ -212,11 +220,17 @@ class User < ActiveRecord::Base
end end
def self.current def self.current
@current_user ||= AnonymousUser.new @current_user ||= User.anonymous
end end
def self.anonymous def self.anonymous
AnonymousUser.new return @anonymous_user if @anonymous_user
anonymous_user = AnonymousUser.find(:first)
if anonymous_user.nil?
anonymous_user = AnonymousUser.create(:lastname => 'Anonymous', :firstname => '', :mail => '', :login => '', :status => 0)
raise 'Unable to create the anonymous user.' if anonymous_user.new_record?
end
@anonymous_user = anonymous_user
end end
private private
@@ -227,12 +241,17 @@ private
end end
class AnonymousUser < User class AnonymousUser < User
def logged?
false def validate_on_create
# There should be only one AnonymousUser in the database
errors.add_to_base 'An anonymous user already exists.' if AnonymousUser.find(:first)
end end
# Anonymous user has no RSS key # Overrides a few properties
def rss_key def logged?; false end
nil def admin; false end
end def name; 'Anonymous' end
def mail; nil end
def time_zone; nil end
def rss_key; nil end
end end
+18
View File
@@ -39,6 +39,24 @@ class Version < ActiveRecord::Base
effective_date && (effective_date <= Date.today) && (open_issues_count == 0) effective_date && (effective_date <= Date.today) && (open_issues_count == 0)
end end
def completed_pourcent
if fixed_issues.count == 0
0
elsif open_issues_count == 0
100
else
(closed_issues_count * 100 + Issue.sum('done_ratio', :include => 'status', :conditions => ["fixed_version_id = ? AND is_closed = ?", id, false]).to_f) / fixed_issues.count
end
end
def closed_pourcent
if fixed_issues.count == 0
0
else
closed_issues_count * 100.0 / fixed_issues.count
end
end
# Returns true if the version is overdue: due date reached and some open issues # Returns true if the version is overdue: due date reached and some open issues
def overdue? def overdue?
effective_date && (effective_date < Date.today) && (open_issues_count > 0) effective_date && (effective_date < Date.today) && (open_issues_count > 0)
+2 -2
View File
@@ -25,8 +25,8 @@ class WikiContent < ActiveRecord::Base
acts_as_versioned acts_as_versioned
class Version class Version
belongs_to :page, :class_name => 'WikiPage', :foreign_key => 'page_id' belongs_to :page, :class_name => '::WikiPage', :foreign_key => 'page_id'
belongs_to :author, :class_name => 'User', :foreign_key => 'author_id' belongs_to :author, :class_name => '::User', :foreign_key => 'author_id'
attr_protected :data attr_protected :data
acts_as_event :title => Proc.new {|o| "#{l(:label_wiki_edit)}: #{o.page.title} (##{o.version})"}, acts_as_event :title => Proc.new {|o| "#{l(:label_wiki_edit)}: #{o.page.title} (##{o.version})"},
+1 -1
View File
@@ -6,7 +6,7 @@
<div class="box tabular"> <div class="box tabular">
<p><label for="new_password"><%=l(:field_new_password)%> <span class="required">*</span></label> <p><label for="new_password"><%=l(:field_new_password)%> <span class="required">*</span></label>
<%= password_field_tag 'new_password', nil, :size => 25 %><br /> <%= password_field_tag 'new_password', nil, :size => 25 %><br />
<em><%= l(:text_length_between, 4, 12) %></em></p> <em><%= l(:text_caracters_minimum, 4) %></em></p>
<p><label for="new_password_confirmation"><%=l(:field_password_confirmation)%> <span class="required">*</span></label> <p><label for="new_password_confirmation"><%=l(:field_password_confirmation)%> <span class="required">*</span></label>
<%= password_field_tag 'new_password_confirmation', nil, :size => 25 %></p> <%= password_field_tag 'new_password_confirmation', nil, :size => 25 %></p>
+2 -4
View File
@@ -9,7 +9,8 @@
<%= text_field 'user', 'login', :size => 25 %></p> <%= text_field 'user', 'login', :size => 25 %></p>
<p><label for="password"><%=l(:field_password)%> <span class="required">*</span></label> <p><label for="password"><%=l(:field_password)%> <span class="required">*</span></label>
<%= password_field_tag 'password', nil, :size => 25 %></p> <%= password_field_tag 'password', nil, :size => 25 %><br />
<em><%= l(:text_caracters_minimum, 4) %></em></p>
<p><label for="password_confirmation"><%=l(:field_password_confirmation)%> <span class="required">*</span></label> <p><label for="password_confirmation"><%=l(:field_password_confirmation)%> <span class="required">*</span></label>
<%= password_field_tag 'password_confirmation', nil, :size => 25 %></p> <%= password_field_tag 'password_confirmation', nil, :size => 25 %></p>
@@ -29,9 +30,6 @@
<% for @custom_value in @custom_values %> <% for @custom_value in @custom_values %>
<p><%= custom_field_tag_with_label @custom_value %></p> <p><%= custom_field_tag_with_label @custom_value %></p>
<% end %> <% end %>
<p><label for="user_mail_notification"><%=l(:field_mail_notification)%></label>
<%= check_box 'user', 'mail_notification' %></p>
<!--[eoform:user]--> <!--[eoform:user]-->
</div> </div>
+3 -1
View File
@@ -42,4 +42,6 @@
<p class="icon22 icon22-info"> <p class="icon22 icon22-info">
<%= link_to l(:label_information_plural), :controller => 'admin', :action => 'info' %> <%= link_to l(:label_information_plural), :controller => 'admin', :action => 'info' %>
</p> </p>
<% set_html_title l(:label_administration) -%>
+3 -1
View File
@@ -12,7 +12,7 @@
&nbsp; &nbsp;
<h3 class="icon22 icon22-plugin">Plugins</h3> <h3 class="icon22 icon22-plugin">Plugins</h3>
<table class="list"> <table class="list">
<% @plugins.keys.sort.each do |plugin| %> <% @plugins.keys.sort {|x,y| x.to_s <=> y.to_s}.each do |plugin| %>
<tr class="<%= cycle('odd', 'even') %>"> <tr class="<%= cycle('odd', 'even') %>">
<td><%=h @plugins[plugin].name %></td> <td><%=h @plugins[plugin].name %></td>
<td><%=h @plugins[plugin].description %></td> <td><%=h @plugins[plugin].description %></td>
@@ -23,3 +23,5 @@
<% end %> <% end %>
</table> </table>
<% end %> <% end %>
<% set_html_title(l(:label_information_plural)) -%>
+13 -2
View File
@@ -6,17 +6,28 @@
<% form_tag({:action => 'mail_options'}, :id => 'mail-options-form') do %> <% form_tag({:action => 'mail_options'}, :id => 'mail-options-form') do %>
<fieldset class="box tabular settings"><legend><%=l(:label_settings)%></legend>
<p><label><%= l(:setting_mail_from) %></label>
<%= text_field_tag 'settings[mail_from]', Setting.mail_from, :size => 60 %></p>
<p><label><%= l(:setting_bcc_recipients) %></label>
<%= check_box_tag 'settings[bcc_recipients]', 1, Setting.bcc_recipients? %>
<%= hidden_field_tag 'settings[bcc_recipients]', 0 %></p>
</fieldset>
<fieldset class="box"><legend><%=l(:text_select_mail_notifications)%></legend> <fieldset class="box"><legend><%=l(:text_select_mail_notifications)%></legend>
<% @notifiables.each do |notifiable| %> <% @notifiables.each do |notifiable| %>
<label><%= check_box_tag "notified_events[]", notifiable, Setting.notified_events.include?(notifiable) %> <label><%= check_box_tag 'settings[notified_events][]', notifiable, Setting.notified_events.include?(notifiable) %>
<%= notifiable.humanize %></label><br /> <%= notifiable.humanize %></label><br />
<% end %> <% end %>
<p><%= check_all_links('mail-options-form') %></p> <p><%= check_all_links('mail-options-form') %></p>
</fieldset> </fieldset>
<fieldset class="box"><legend><%= l(:setting_emails_footer) %></legend> <fieldset class="box"><legend><%= l(:setting_emails_footer) %></legend>
<%= text_area_tag 'emails_footer', Setting.emails_footer, :class => 'wiki-edit', :rows => 5 %> <%= text_area_tag 'settings[emails_footer]', Setting.emails_footer, :class => 'wiki-edit', :rows => 5 %>
</fieldset> </fieldset>
<%= submit_tag l(:button_save) %> <%= submit_tag l(:button_save) %>
<% end %> <% end %>
<% set_html_title(l(:field_mail_notification)) -%>
+3 -1
View File
@@ -26,7 +26,7 @@
<tbody> <tbody>
<% for project in @projects %> <% for project in @projects %>
<tr class="<%= cycle("odd", "even") %>"> <tr class="<%= cycle("odd", "even") %>">
<td><%= project.active? ? link_to(project.name, :controller => 'projects', :action => 'settings', :id => project) : h(project.name) %> <td><%= project.active? ? link_to(h(project.name), :controller => 'projects', :action => 'settings', :id => project) : h(project.name) %>
<td><%= textilizable project.description, :project => project %> <td><%= textilizable project.description, :project => project %>
<td align="center"><%= image_tag 'true.png' if project.is_public? %> <td align="center"><%= image_tag 'true.png' if project.is_public? %>
<td align="center"><%= project.children.size %> <td align="center"><%= project.children.size %>
@@ -47,3 +47,5 @@
<p><%= pagination_links_full @project_pages, :status => @status %> <p><%= pagination_links_full @project_pages, :status => @status %>
[ <%= @project_pages.current.first_item %> - <%= @project_pages.current.last_item %> / <%= @project_count %> ]</p> [ <%= @project_pages.current.first_item %> - <%= @project_pages.current.last_item %> / <%= @project_count %> ]</p>
<% set_html_title l(:label_project_plural) -%>
+4 -1
View File
@@ -15,7 +15,10 @@
<%= text_field 'auth_source', 'account' %></p> <%= text_field 'auth_source', 'account' %></p>
<p><label for="auth_source_account_password"><%=l(:field_password)%></label> <p><label for="auth_source_account_password"><%=l(:field_password)%></label>
<%= password_field 'auth_source', 'account_password' %></p> <%= password_field 'auth_source', 'account_password', :name => 'ignore',
:value => ((@auth_source.new_record? || @auth_source.account_password.blank?) ? '' : ('x'*15)),
:onfocus => "this.value=''; this.name='auth_source[account_password]';",
:onchange => "this.name='auth_source[account_password]';" %></p>
<p><label for="auth_source_base_dn"><%=l(:field_base_dn)%> <span class="required">*</span></label> <p><label for="auth_source_base_dn"><%=l(:field_base_dn)%> <span class="required">*</span></label>
<%= text_field 'auth_source', 'base_dn', :size => 60 %></p> <%= text_field 'auth_source', 'base_dn', :size => 60 %></p>
+1 -1
View File
@@ -19,7 +19,7 @@
<td> <td>
<small> <small>
<% if board.last_message %> <% if board.last_message %>
<%= board.last_message.author.name %>, <%= format_time(board.last_message.created_on) %><br /> <%= authoring board.last_message.created_on, board.last_message.author %><br />
<%= link_to_message board.last_message %> <%= link_to_message board.last_message %>
<% end %> <% end %>
</small> </small>
+8 -10
View File
@@ -18,7 +18,7 @@
<h2><%=h @board.name %></h2> <h2><%=h @board.name %></h2>
<% if @topics.any? %> <% if @topics.any? %>
<table class="list"> <table class="list messages">
<thead><tr> <thead><tr>
<th><%= l(:field_subject) %></th> <th><%= l(:field_subject) %></th>
<th><%= l(:field_author) %></th> <th><%= l(:field_author) %></th>
@@ -28,18 +28,16 @@
</tr></thead> </tr></thead>
<tbody> <tbody>
<% @topics.each do |topic| %> <% @topics.each do |topic| %>
<tr class="<%= cycle 'odd', 'even' %>"> <tr class="message <%= cycle 'odd', 'even' %> <%= topic.sticky? ? 'sticky' : '' %> <%= topic.locked? ? 'locked' : '' %>">
<td><%= link_to h(topic.subject), :controller => 'messages', :action => 'show', :board_id => @board, :id => topic %></td> <td class="subject"><%= link_to h(topic.subject), { :controller => 'messages', :action => 'show', :board_id => @board, :id => topic }, :class => 'icon' %></td>
<td align="center"><%= link_to_user topic.author %></td> <td class="author" align="center"><%= topic.author %></td>
<td align="center"><%= format_time(topic.created_on) %></td> <td class="created_on" align="center"><%= format_time(topic.created_on) %></td>
<td align="center"><%= topic.replies_count %></td> <td class="replies" align="center"><%= topic.replies_count %></td>
<td> <td class="last_message">
<small>
<% if topic.last_reply %> <% if topic.last_reply %>
<%= topic.last_reply.author.name %>, <%= format_time(topic.last_reply.created_on) %><br /> <%= authoring topic.last_reply.created_on, topic.last_reply.author %><br />
<%= link_to_message topic.last_reply %> <%= link_to_message topic.last_reply %>
<% end %> <% end %>
</small>
</td> </td>
</tr> </tr>
<% end %> <% end %>
+13 -4
View File
@@ -7,21 +7,32 @@ function toggle_custom_field_format() {
p_length = $("custom_field_min_length"); p_length = $("custom_field_min_length");
p_regexp = $("custom_field_regexp"); p_regexp = $("custom_field_regexp");
p_values = $("custom_field_possible_values"); p_values = $("custom_field_possible_values");
p_searchable = $("custom_field_searchable");
switch (format.value) { switch (format.value) {
case "list": case "list":
Element.hide(p_length.parentNode); Element.hide(p_length.parentNode);
Element.hide(p_regexp.parentNode); Element.hide(p_regexp.parentNode);
Element.show(p_searchable.parentNode);
Element.show(p_values); Element.show(p_values);
break; break;
case "date": case "date":
case "bool": case "bool":
Element.hide(p_length.parentNode); Element.hide(p_length.parentNode);
Element.hide(p_regexp.parentNode); Element.hide(p_regexp.parentNode);
Element.hide(p_searchable.parentNode);
Element.hide(p_values);
break;
case "float":
case "int":
Element.show(p_length.parentNode);
Element.show(p_regexp.parentNode);
Element.hide(p_searchable.parentNode);
Element.hide(p_values); Element.hide(p_values);
break; break;
default: default:
Element.show(p_length.parentNode); Element.show(p_length.parentNode);
Element.show(p_regexp.parentNode); Element.show(p_regexp.parentNode);
Element.show(p_searchable.parentNode);
Element.hide(p_values); Element.hide(p_values);
break; break;
} }
@@ -47,7 +58,6 @@ function deleteValueField(e) {
//]]> //]]>
</script> </script>
<!--[form:custom_field]-->
<div class="box"> <div class="box">
<p><%= f.text_field :name, :required => true %></p> <p><%= f.text_field :name, :required => true %></p>
<p><%= f.select :field_format, custom_field_formats_for_select, {}, :onchange => "toggle_custom_field_format();" %></p> <p><%= f.select :field_format, custom_field_formats_for_select, {}, :onchange => "toggle_custom_field_format();" %></p>
@@ -59,11 +69,8 @@ function deleteValueField(e) {
<% (@custom_field.possible_values.to_a + [""]).each do |value| %> <% (@custom_field.possible_values.to_a + [""]).each do |value| %>
<span><%= text_field_tag 'custom_field[possible_values][]', value, :size => 30 %> <%= image_to_function "delete.png", "deleteValueField(this);return false" %><br /></span> <span><%= text_field_tag 'custom_field[possible_values][]', value, :size => 30 %> <%= image_to_function "delete.png", "deleteValueField(this);return false" %><br /></span>
<% end %> <% end %>
</p> </p>
</div> </div>
<%= javascript_tag "toggle_custom_field_format();" %>
<!--[eoform:custom_field]-->
<div class="box"> <div class="box">
<% case @custom_field.type.to_s <% case @custom_field.type.to_s
@@ -78,6 +85,7 @@ when "IssueCustomField" %>
<p><%= f.check_box :is_required %></p> <p><%= f.check_box :is_required %></p>
<p><%= f.check_box :is_for_all %></p> <p><%= f.check_box :is_for_all %></p>
<p><%= f.check_box :is_filter %></p> <p><%= f.check_box :is_filter %></p>
<p><%= f.check_box :searchable %></p>
<% when "UserCustomField" %> <% when "UserCustomField" %>
<p><%= f.check_box :is_required %></p> <p><%= f.check_box :is_required %></p>
@@ -87,3 +95,4 @@ when "IssueCustomField" %>
<% end %> <% end %>
</div> </div>
<%= javascript_tag "toggle_custom_field_format();" %>
+11 -2
View File
@@ -19,10 +19,11 @@
<th><%=l(:field_is_for_all)%></th> <th><%=l(:field_is_for_all)%></th>
<th><%=l(:label_used_by)%></th> <th><%=l(:label_used_by)%></th>
<% end %> <% end %>
<th><%=l(:button_sort)%></th>
<th width="10%"></th> <th width="10%"></th>
</tr></thead> </tr></thead>
<tbody> <tbody>
<% for custom_field in (@custom_fields_by_type[type] || []) %> <% for custom_field in (@custom_fields_by_type[type] || []).sort %>
<tr class="<%= cycle("odd", "even") %>"> <tr class="<%= cycle("odd", "even") %>">
<td><%= link_to custom_field.name, :action => 'edit', :id => custom_field %></td> <td><%= link_to custom_field.name, :action => 'edit', :id => custom_field %></td>
<td align="center"><%= l(CustomField::FIELD_FORMATS[custom_field.field_format][:name]) %></td> <td align="center"><%= l(CustomField::FIELD_FORMATS[custom_field.field_format][:name]) %></td>
@@ -31,6 +32,12 @@
<td align="center"><%= image_tag 'true.png' if custom_field.is_for_all? %></td> <td align="center"><%= image_tag 'true.png' if custom_field.is_for_all? %></td>
<td align="center"><%= custom_field.projects.count.to_s + ' ' + lwr(:label_project, custom_field.projects.count) if custom_field.is_a? IssueCustomField and !custom_field.is_for_all? %></td> <td align="center"><%= custom_field.projects.count.to_s + ' ' + lwr(:label_project, custom_field.projects.count) if custom_field.is_a? IssueCustomField and !custom_field.is_for_all? %></td>
<% end %> <% end %>
<td align="center" style="width:15%;">
<%= link_to image_tag('2uparrow.png', :alt => l(:label_sort_highest)), {:action => 'move', :id => custom_field, :position => 'highest'}, :method => :post, :title => l(:label_sort_highest) %>
<%= link_to image_tag('1uparrow.png', :alt => l(:label_sort_higher)), {:action => 'move', :id => custom_field, :position => 'higher'}, :method => :post, :title => l(:label_sort_higher) %> -
<%= link_to image_tag('1downarrow.png', :alt => l(:label_sort_lower)), {:action => 'move', :id => custom_field, :position => 'lower'}, :method => :post, :title => l(:label_sort_lower) %>
<%= link_to image_tag('2downarrow.png', :alt => l(:label_sort_lowest)), {:action => 'move', :id => custom_field, :position => 'lowest'}, :method => :post, :title => l(:label_sort_lowest) %>
</td>
<td align="center"> <td align="center">
<%= button_to l(:button_delete), { :action => 'destroy', :id => custom_field }, :confirm => l(:text_are_you_sure), :class => "button-small" %> <%= button_to l(:button_delete), { :action => 'destroy', :id => custom_field }, :confirm => l(:text_are_you_sure), :class => "button-small" %>
</td> </td>
@@ -43,4 +50,6 @@
</div> </div>
<% end %> <% end %>
<%= javascript_tag "showTab('#{@tab}');" %> <%= javascript_tag "showTab('#{@tab}');" %>
<% set_html_title(l(:label_custom_field_plural)) -%>
+4 -2
View File
@@ -3,9 +3,9 @@
<%= link_to_if_authorized l(:button_delete), {:controller => 'documents', :action => 'destroy', :id => @document}, :confirm => l(:text_are_you_sure), :method => :post, :class => 'icon icon-del' %> <%= link_to_if_authorized l(:button_delete), {:controller => 'documents', :action => 'destroy', :id => @document}, :confirm => l(:text_are_you_sure), :method => :post, :class => 'icon icon-del' %>
</div> </div>
<h2><%= @document.title %></h2> <h2><%=h @document.title %></h2>
<p><em><%= @document.category.name %><br /> <p><em><%=h @document.category.name %><br />
<%= format_date @document.created_on %></em></p> <%= format_date @document.created_on %></em></p>
<%= textilizable @document.description, :attachments => @document.attachments %> <%= textilizable @document.description, :attachments => @document.attachments %>
<br /> <br />
@@ -34,3 +34,5 @@
<%= submit_tag l(:button_add) %> <%= submit_tag l(:button_add) %>
<% end %> <% end %>
<% end %> <% end %>
<% set_html_title h(@document.title) -%>
+2
View File
@@ -24,3 +24,5 @@
<p><%= link_to l(:label_enumeration_new), { :action => 'new', :opt => option } %></p> <p><%= link_to l(:label_enumeration_new), { :action => 'new', :opt => option } %></p>
<% end %> <% end %>
<% set_html_title(l(:label_enumerations)) -%>
-28
View File
@@ -1,28 +0,0 @@
xml.instruct!
xml.rss "version" => "2.0", "xmlns:dc" => "http://purl.org/dc/elements/1.1/" do
xml.channel do
xml.title @title
xml.link url_for(:controller => 'welcome', :only_path => false)
xml.pubDate CGI.rfc1123_date(@journals.first ? @journals.first.created_on : Time.now)
xml.description l(:label_changes_details)
@journals.each do |journal|
issue = journal.issue
xml.item do
xml.title "#{issue.project.name} - #{issue.tracker.name} ##{issue.id}: #{issue.subject}"
url = url_for(:controller => 'issues' , :action => 'show', :id => issue, :only_path => false)
xml.link url
xml.description do
xml.text! h(journal.notes)
xml.text! "<ul>"
journal.details.each do |detail|
xml.text! "<li>" + show_detail(detail, false) + "</li>"
end
xml.text! "</ul>"
end
xml.pubDate CGI.rfc1123_date(journal.created_on)
xml.guid url
xml.author h(journal.user.name)
end
end
end
end
-20
View File
@@ -1,20 +0,0 @@
xml.instruct!
xml.rss "version" => "2.0", "xmlns:dc" => "http://purl.org/dc/elements/1.1/" do
xml.channel do
xml.title @title
xml.link url_for(:controller => 'welcome', :only_path => false)
xml.pubDate CGI.rfc1123_date(@issues.first ? @issues.first.created_on : Time.now)
xml.description l(:label_reported_issues)
@issues.each do |issue|
xml.item do
xml.title "#{issue.project.name} - #{issue.tracker.name} ##{issue.id}: #{issue.subject}"
url = url_for(:controller => 'issues' , :action => 'show', :id => issue, :only_path => false)
xml.link url
xml.description h(issue.description)
xml.pubDate CGI.rfc1123_date(issue.created_on)
xml.guid url
xml.author h(issue.author.name)
end
end
end
end
-22
View File
@@ -1,22 +0,0 @@
xml.instruct!
xml.feed "xmlns" => "http://www.w3.org/2005/Atom" do
xml.title @title
xml.link "rel" => "self", "href" => url_for(:controller => 'feeds', :action => 'issues', :format => 'atom', :only_path => false)
xml.link "rel" => "alternate", "href" => url_for(:controller => 'welcome', :only_path => false)
xml.id url_for(:controller => 'welcome', :only_path => false)
xml.updated CGI.rfc1123_date(@issues.first.updated_on) if @issues.any?
xml.author { xml.name "#{Setting.app_title}" }
@issues.each do |issue|
xml.entry do
xml.title "#{issue.project.name} - #{issue.tracker.name} ##{issue.id}: #{issue.subject}"
xml.link "rel" => "alternate", "href" => url_for(:controller => 'issues' , :action => 'show', :id => issue, :only_path => false)
xml.id url_for(:controller => 'issues' , :action => 'show', :id => issue, :only_path => false)
xml.updated CGI.rfc1123_date(issue.updated_on)
xml.author { xml.name issue.author.name }
xml.summary issue.description
xml.content "type" => "html" do
xml.text! issue.description
end
end
end
end
-20
View File
@@ -1,20 +0,0 @@
xml.instruct!
xml.rss "version" => "2.0", "xmlns:dc" => "http://purl.org/dc/elements/1.1/" do
xml.channel do
xml.title "#{Setting.app_title}: #{l(:label_news_latest)}"
xml.link url_for(:controller => 'welcome', :only_path => false)
xml.pubDate CGI.rfc1123_date(@news.first ? @news.first.created_on : Time.now)
xml.description l(:label_news_latest)
@news.each do |news|
xml.item do
xml.title "#{news.project.name}: #{news.title}"
news_url = url_for(:controller => 'news' , :action => 'show', :id => news, :only_path => false)
xml.link news_url
xml.description h(news.summary)
xml.pubDate CGI.rfc1123_date(news.created_on)
xml.guid news_url
xml.author h(news.author.name)
end
end
end
end
-22
View File
@@ -1,22 +0,0 @@
xml.instruct!
xml.feed "xmlns" => "http://www.w3.org/2005/Atom" do
xml.title "#{Setting.app_title}: #{l(:label_news_latest)}"
xml.link "rel" => "self", "href" => url_for(:controller => 'feeds', :action => 'news', :format => 'atom', :only_path => false)
xml.link "rel" => "alternate", "href" => url_for(:controller => 'welcome', :only_path => false)
xml.id url_for(:controller => 'welcome', :only_path => false)
xml.updated CGI.rfc1123_date(@news.first.created_on) if @news.any?
xml.author { xml.name "#{Setting.app_title}" }
@news.each do |news|
xml.entry do
xml.title news.title
xml.link "rel" => "alternate", "href" => url_for(:controller => 'news' , :action => 'show', :id => news, :only_path => false)
xml.id url_for(:controller => 'news' , :action => 'show', :id => news, :only_path => false)
xml.updated CGI.rfc1123_date(news.created_on)
xml.author { xml.name news.author.name }
xml.summary h(news.summary)
xml.content "type" => "html" do
xml.text! news.description
end
end
end
end
+3 -1
View File
@@ -32,4 +32,6 @@
</tbody> </tbody>
</table> </table>
<%= pagination_links_full @issue_status_pages %> <%= pagination_links_full @issue_status_pages %>
<% set_html_title(l(:label_issue_status_plural)) -%>
+5 -5
View File
@@ -7,14 +7,14 @@
</tr></thead> </tr></thead>
<tbody> <tbody>
<% for issue in issues %> <% for issue in issues %>
<tr class="<%= cycle("odd", "even") %> <%= "status-#{issue.status.position} priority-#{issue.priority.position}" %>"> <tr id="issue-<%= issue.id %>" class="issue hascontextmenu <%= cycle('odd', 'even') %> <%= "status-#{issue.status.position} priority-#{issue.priority.position}" %>">
<td class="id"> <td class="id">
<%= link_to issue.id, :controller => 'issues', :action => 'show', :id => issue %> <%= link_to issue.id, :controller => 'issues', :action => 'show', :id => issue %>
</td> </td>
<td><p class="small"><%= issue.project.name %> - <%= issue.tracker.name %><br /> <td><%=h issue.project.name %> - <%= issue.tracker.name %><br />
<%= issue.status.name %> - <%= format_time(issue.updated_on) %></p></td> <%= issue.status.name %> - <%= format_time(issue.updated_on) %></td>
<td> <td class="subject">
<p class="small"><%= link_to h(issue.subject), :controller => 'issues', :action => 'show', :id => issue %></p> <%= link_to h(issue.subject), :controller => 'issues', :action => 'show', :id => issue %>
</td> </td>
</tr> </tr>
<% end %> <% end %>
+3 -3
View File
@@ -1,10 +1,10 @@
<% if authorize_for('projects', 'add_issue') %> <% if authorize_for('projects', 'add_issue') && @project.trackers.any? %>
<h3><%= l(:label_issue_new) %></h3> <h3><%= l(:label_issue_new) %></h3>
<%= l(:label_tracker) %>: <%= new_issue_selector %> <%= l(:label_tracker) %>: <%= new_issue_selector %>
<% end %> <% end %>
<h3><%= l(:label_issue_plural) %></h3> <h3><%= l(:label_issue_plural) %></h3>
<%= link_to l(:label_issue_view_all), :controller => 'projects', :action => 'list_issues', :id => @project, :set_filter => 1 %><br /> <%= link_to l(:label_issue_view_all), { :controller => 'issues', :action => 'index', :project_id => @project, :set_filter => 1 } %><br />
<%= link_to l(:field_summary), :controller => 'reports', :action => 'issue_report', :id => @project %><br /> <%= link_to l(:field_summary), :controller => 'reports', :action => 'issue_report', :id => @project %><br />
<%= link_to l(:label_change_log), :controller => 'projects', :action => 'changelog', :id => @project %> <%= link_to l(:label_change_log), :controller => 'projects', :action => 'changelog', :id => @project %>
@@ -14,5 +14,5 @@
:order => "name ASC", :order => "name ASC",
:conditions => ["is_public = ? or user_id = ?", true, (User.current.logged? ? User.current.id : 0)]) :conditions => ["is_public = ? or user_id = ?", true, (User.current.logged? ? User.current.id : 0)])
queries.each do |query| %> queries.each do |query| %>
<%= link_to query.name, :controller => 'projects', :action => 'list_issues', :id => @project, :query_id => query %><br /> <%= link_to query.name, :controller => 'issues', :action => 'index', :project_id => @project, :query_id => query %><br />
<% end %> <% end %>
-2
View File
@@ -29,11 +29,9 @@
<p><label for="notes"><%= l(:field_notes) %></label> <p><label for="notes"><%= l(:field_notes) %></label>
<%= text_area_tag 'notes', @notes, :cols => 60, :rows => 10, :class => 'wiki-edit' %></p> <%= text_area_tag 'notes', @notes, :cols => 60, :rows => 10, :class => 'wiki-edit' %></p>
<% if authorize_for('issues', 'add_attachment') %>
<p id="attachments_p"><label><%=l(:label_attachment_new)%> <p id="attachments_p"><label><%=l(:label_attachment_new)%>
<%= image_to_function "add.png", "addFileField();return false" %></label> <%= image_to_function "add.png", "addFileField();return false" %></label>
<%= file_field_tag 'attachments[]', :size => 30 %> <em>(<%= l(:label_max_size) %>: <%= number_to_human_size(Setting.attachment_max_size.to_i.kilobytes) %>)</em></p> <%= file_field_tag 'attachments[]', :size => 30 %> <em>(<%= l(:label_max_size) %>: <%= number_to_human_size(Setting.attachment_max_size.to_i.kilobytes) %>)</em></p>
<% end %>
</div> </div>
<%= submit_tag l(:button_save) %> <%= submit_tag l(:button_save) %>
@@ -4,24 +4,26 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom" do
xml.link "rel" => "self", "href" => url_for(:controller => 'feeds', :action => 'history', :format => 'atom', :only_path => false) xml.link "rel" => "self", "href" => url_for(:controller => 'feeds', :action => 'history', :format => 'atom', :only_path => false)
xml.link "rel" => "alternate", "href" => url_for(:controller => 'welcome', :only_path => false) xml.link "rel" => "alternate", "href" => url_for(:controller => 'welcome', :only_path => false)
xml.id url_for(:controller => 'welcome', :only_path => false) xml.id url_for(:controller => 'welcome', :only_path => false)
xml.updated CGI.rfc1123_date(@journals.first.created_on) if @journals.any? xml.updated((@changes.first ? @changes.first.event_datetime : Time.now).xmlschema)
xml.author { xml.name "#{Setting.app_title}" } xml.author { xml.name "#{Setting.app_title}" }
@journals.each do |journal| @changes.each do |change|
issue = journal.issue issue = change.issue
xml.entry do xml.entry do
xml.title "#{issue.project.name} - #{issue.tracker.name} ##{issue.id}: #{issue.subject}" xml.title "#{issue.project.name} - #{issue.tracker.name} ##{issue.id}: #{issue.subject}"
xml.link "rel" => "alternate", "href" => url_for(:controller => 'issues' , :action => 'show', :id => issue, :only_path => false) xml.link "rel" => "alternate", "href" => url_for(:controller => 'issues' , :action => 'show', :id => issue, :only_path => false)
xml.id url_for(:controller => 'issues' , :action => 'show', :id => issue, :only_path => false) xml.id url_for(:controller => 'issues' , :action => 'show', :id => issue, :journal_id => change, :only_path => false)
xml.updated CGI.rfc1123_date(journal.created_on) xml.updated change.created_on.xmlschema
xml.author { xml.name journal.user.name } xml.author do
xml.summary journal.notes xml.name change.user.name
xml.email(change.user.mail)
end
xml.content "type" => "html" do xml.content "type" => "html" do
xml.text! journal.notes if journal.notes xml.text! '<ul>'
xml.text! "<ul>" change.details.each do |detail|
journal.details.each do |detail| xml.text! '<li>' + show_detail(detail, false) + '</li>'
xml.text! "<li>" + show_detail(detail, false) + "</li>"
end end
xml.text! "</ul>" xml.text! '</ul>'
xml.text! textilizable(change.notes) unless change.notes.blank?
end end
end end
end end
+2 -2
View File
@@ -1,4 +1,4 @@
<% back_to = url_for(:controller => 'projects', :action => 'list_issues', :id => @project) %> <% back_to = url_for(:controller => 'issues', :action => 'index', :project_id => @project) %>
<ul> <ul>
<li><%= context_menu_link l(:button_edit), {:controller => 'issues', :action => 'edit', :id => @issue}, <li><%= context_menu_link l(:button_edit), {:controller => 'issues', :action => 'edit', :id => @issue},
:class => 'icon-edit', :disabled => !@can[:edit] %></li> :class => 'icon-edit', :disabled => !@can[:edit] %></li>
@@ -32,7 +32,7 @@
</ul> </ul>
</li> </li>
<li><%= context_menu_link l(:button_copy), {:controller => 'projects', :action => 'add_issue', :id => @project, :copy_from => @issue}, <li><%= context_menu_link l(:button_copy), {:controller => 'projects', :action => 'add_issue', :id => @project, :copy_from => @issue},
:class => 'icon-copy', :disabled => !@can[:add] %></li> :class => 'icon-copy', :disabled => !@can[:copy] %></li>
<li><%= context_menu_link l(:button_move), {:controller => 'projects', :action => 'move_issues', :id => @project, "issue_ids[]" => @issue.id }, <li><%= context_menu_link l(:button_move), {:controller => 'projects', :action => 'move_issues', :id => @project, "issue_ids[]" => @issue.id },
:class => 'icon-move', :disabled => !@can[:move] %> :class => 'icon-move', :disabled => !@can[:move] %>
<li><%= context_menu_link l(:button_delete), {:controller => 'issues', :action => 'destroy', :id => @issue}, <li><%= context_menu_link l(:button_delete), {:controller => 'issues', :action => 'destroy', :id => @issue},
@@ -1,49 +1,50 @@
<% pdf=IfpdfHelper::IFPDF.new(current_language) <% pdf=IfpdfHelper::IFPDF.new(current_language)
pdf.SetTitle("#{@project.name} - #{l(:label_issue_plural)}") title = @project ? "#{@project.name} - #{l(:label_issue_plural)}" : "#{l(:label_issue_plural)}"
pdf.AliasNbPages pdf.SetTitle(title)
pdf.footer_date = format_date(Date.today) pdf.AliasNbPages
pdf.AddPage("L") pdf.footer_date = format_date(Date.today)
row_height = 7 pdf.AddPage("L")
row_height = 7
#
# title #
# # title
pdf.SetFontStyle('B',11) #
pdf.Cell(190,10, "#{@project.name} - #{l(:label_issue_plural)}") pdf.SetFontStyle('B',11)
pdf.Ln pdf.Cell(190,10, title)
pdf.Ln
#
# headers #
# # headers
pdf.SetFontStyle('B',10) #
pdf.SetFillColor(230, 230, 230) pdf.SetFontStyle('B',10)
pdf.Cell(15, row_height, "#", 0, 0, 'L', 1) pdf.SetFillColor(230, 230, 230)
pdf.Cell(30, row_height, l(:field_tracker), 0, 0, 'L', 1) pdf.Cell(15, row_height, "#", 0, 0, 'L', 1)
pdf.Cell(30, row_height, l(:field_status), 0, 0, 'L', 1) pdf.Cell(30, row_height, l(:field_tracker), 0, 0, 'L', 1)
pdf.Cell(30, row_height, l(:field_priority), 0, 0, 'L', 1) pdf.Cell(30, row_height, l(:field_status), 0, 0, 'L', 1)
pdf.Cell(40, row_height, l(:field_author), 0, 0, 'L', 1) pdf.Cell(30, row_height, l(:field_priority), 0, 0, 'L', 1)
pdf.Cell(25, row_height, l(:field_updated_on), 0, 0, 'L', 1) pdf.Cell(40, row_height, l(:field_assigned_to), 0, 0, 'L', 1)
pdf.Cell(0, row_height, l(:field_subject), 0, 0, 'L', 1) pdf.Cell(25, row_height, l(:field_updated_on), 0, 0, 'L', 1)
pdf.Line(10, pdf.GetY, 287, pdf.GetY) pdf.Cell(0, row_height, l(:field_subject), 0, 0, 'L', 1)
pdf.Ln pdf.Line(10, pdf.GetY, 287, pdf.GetY)
pdf.Line(10, pdf.GetY, 287, pdf.GetY) pdf.Ln
pdf.SetY(pdf.GetY() + 1) pdf.Line(10, pdf.GetY, 287, pdf.GetY)
pdf.SetY(pdf.GetY() + 1)
#
# rows #
# # rows
pdf.SetFontStyle('',9) #
pdf.SetFillColor(255, 255, 255) pdf.SetFontStyle('',9)
@issues.each do |issue| pdf.SetFillColor(255, 255, 255)
pdf.Cell(15, row_height, issue.id.to_s, 0, 0, 'L', 1) @issues.each do |issue|
pdf.Cell(30, row_height, issue.tracker.name, 0, 0, 'L', 1) pdf.Cell(15, row_height, issue.id.to_s, 0, 0, 'L', 1)
pdf.Cell(30, row_height, issue.status.name, 0, 0, 'L', 1) pdf.Cell(30, row_height, issue.tracker.name, 0, 0, 'L', 1)
pdf.Cell(30, row_height, issue.priority.name, 0, 0, 'L', 1) pdf.Cell(30, row_height, issue.status.name, 0, 0, 'L', 1)
pdf.Cell(40, row_height, issue.author.name, 0, 0, 'L', 1) pdf.Cell(30, row_height, issue.priority.name, 0, 0, 'L', 1)
pdf.Cell(25, row_height, format_date(issue.updated_on), 0, 0, 'L', 1) pdf.Cell(40, row_height, issue.assigned_to ? issue.assigned_to.name : '', 0, 0, 'L', 1)
pdf.MultiCell(0, row_height, (@project == issue.project ? issue.subject : "#{issue.project.name} - #{issue.subject}")) pdf.Cell(25, row_height, format_date(issue.updated_on), 0, 0, 'L', 1)
pdf.Line(10, pdf.GetY, 287, pdf.GetY) pdf.MultiCell(0, row_height, (@project == issue.project ? issue.subject : "#{issue.project.name} - #{issue.subject}"))
pdf.SetY(pdf.GetY() + 1) pdf.Line(10, pdf.GetY, 287, pdf.GetY)
end pdf.SetY(pdf.GetY() + 1)
%> end
%>
<%= pdf.Output %> <%= pdf.Output %>
+59 -21
View File
@@ -1,35 +1,73 @@
<h2><%=l(:label_issue_plural)%></h2> <% if @query.new_record? %>
<h2><%=l(:label_issue_plural)%></h2>
<% set_html_title l(:label_issue_plural) %>
<% form_tag({}, :id => 'query_form') do %> <% form_tag({ :controller => 'queries', :action => 'new' }, :id => 'query_form') do %>
<%= render :partial => 'queries/filters', :locals => {:query => @query} %> <%= hidden_field_tag('project_id', @project.id) if @project %>
<%= render :partial => 'queries/filters', :locals => {:query => @query} %>
<div class="contextual">
<%= link_to_remote l(:button_apply),
{ :url => { :set_filter => 1 },
:update => "content",
:with => "Form.serialize('query_form')"
}, :class => 'icon icon-checked' %>
<%= link_to_remote l(:button_clear),
{ :url => { :set_filter => 1 },
:update => "content",
}, :class => 'icon icon-reload' %>
<% if current_role && current_role.allowed_to?(:save_queries) %>
<%= link_to l(:button_save), {}, :onclick => "$('query_form').submit(); return false;", :class => 'icon icon-save' %>
<% end %>
</div>
<br />
&nbsp;
<% end %>
<% else %>
<div class="contextual">
<% if @query.editable_by?(User.current) %>
<%= link_to l(:button_edit), {:controller => 'queries', :action => 'edit', :id => @query}, :class => 'icon icon-edit' %>
<%= link_to l(:button_delete), {:controller => 'queries', :action => 'destroy', :id => @query}, :confirm => l(:text_are_you_sure), :method => :post, :class => 'icon icon-del' %>
<% end %>
</div>
<h2><%= @query.name %></h2>
<div id="query_form"></div>
<% set_html_title @query.name %>
<% end %> <% end %>
<div class="contextual">
<%= link_to_remote l(:button_apply),
{ :url => { :set_filter => 1 },
:update => "content",
:with => "Form.serialize('query_form')"
}, :class => 'icon icon-edit' %>
<%= link_to_remote l(:button_clear),
{ :url => { :set_filter => 1 },
:update => "content",
}, :class => 'icon icon-reload' %>
</div>
<br />&nbsp;
<%= error_messages_for 'query' %> <%= error_messages_for 'query' %>
<% if @query.valid? %> <% if @query.valid? %>
<% if @issues.empty? %> <% if @issues.empty? %>
<p><i><%= l(:label_no_data) %></i></p> <p class="nodata"><%= l(:label_no_data) %></p>
<% else %> <% else %>
&nbsp; <% form_tag({:controller => 'projects', :action => 'bulk_edit_issues', :id => @project}, :id => 'issues_form', :onsubmit => "if (!checkBulkEdit(this)) {alert('#{l(:notice_no_issue_selected)}'); return false;}" ) do %>
<%= render :partial => 'issues/list', :locals => {:issues => @issues, :query => @query} %> <%= render :partial => 'issues/list', :locals => {:issues => @issues, :query => @query} %>
<div class="contextual">
<%= l(:label_export_to) %>
<%= link_to 'CSV', {:format => 'csv'}, :class => 'icon icon-csv' %>,
<%= link_to 'PDF', {:format => 'pdf'}, :class => 'icon icon-pdf' %>
</div>
<p><%= pagination_links_full @issue_pages %> <p><%= pagination_links_full @issue_pages %>
[ <%= @issue_pages.current.first_item %> - <%= @issue_pages.current.last_item %> / <%= @issue_count %> ]</p> [ <%= @issue_pages.current.first_item %> - <%= @issue_pages.current.last_item %> / <%= @issue_count %> ]</p>
<% end %> <% end %>
<% end %> <% end %>
<% end %>
<% content_for :sidebar do %>
<%= render :partial => 'issues/sidebar' %>
<% end if @project%>
<% content_for :header_tags do %> <% content_for :header_tags do %>
<%= auto_discovery_link_tag(:atom, params.merge({:format => 'atom', :page => nil, :key => User.current.rss_key})) %> <%= auto_discovery_link_tag(:atom, {:query_id => @query, :format => 'atom', :page => nil, :key => User.current.rss_key}, :title => l(:label_issue_plural)) %>
<%= auto_discovery_link_tag(:atom, {:action => 'changes', :query_id => @query, :format => 'atom', :page => nil, :key => User.current.rss_key}, :title => l(:label_changes_details)) %>
<%= javascript_include_tag 'calendar/calendar' %>
<%= javascript_include_tag "calendar/lang/calendar-#{current_language}.js" %>
<%= javascript_include_tag 'calendar/calendar-setup' %>
<%= stylesheet_link_tag 'calendar' %>
<%= javascript_include_tag 'context_menu' %>
<%= stylesheet_link_tag 'context_menu' %>
<% end %> <% end %>
<div id="context-menu" style="display: none;"></div>
<%= javascript_tag 'new ContextMenu({})' %>
+3 -3
View File
@@ -10,7 +10,7 @@
<h2><%= @issue.tracker.name %> #<%= @issue.id %></h2> <h2><%= @issue.tracker.name %> #<%= @issue.id %></h2>
<div class="issue"> <div class="issue <%= "status-#{@issue.status.position} priority-#{@issue.priority.position}" %>">
<h3><%=h @issue.subject %></h3> <h3><%=h @issue.subject %></h3>
<p class="author"> <p class="author">
<%= authoring @issue.created_on, @issue.author %>. <%= authoring @issue.created_on, @issue.author %>.
@@ -28,7 +28,7 @@
</tr> </tr>
<tr> <tr>
<td><b><%=l(:field_assigned_to)%> :</b></td><td><%= @issue.assigned_to ? link_to_user(@issue.assigned_to) : "-" %></td> <td><b><%=l(:field_assigned_to)%> :</b></td><td><%= @issue.assigned_to ? link_to_user(@issue.assigned_to) : "-" %></td>
<td><b><%=l(:field_done_ratio)%> :</b></td><td><%= @issue.done_ratio %> %</td> <td><b><%=l(:field_done_ratio)%> :</b></td><td><%= progress_bar @issue.done_ratio, :width => '80px', :legend => "#{@issue.done_ratio}%" %></td>
</tr> </tr>
<tr> <tr>
<td><b><%=l(:field_category)%> :</b></td><td><%=h @issue.category ? @issue.category.name : "-" %></td> <td><b><%=l(:field_category)%> :</b></td><td><%=h @issue.category ? @issue.category.name : "-" %></td>
@@ -64,7 +64,7 @@ end %>
<% end %> <% end %>
<p><strong><%=l(:field_description)%></strong></p> <p><strong><%=l(:field_description)%></strong></p>
<%= textilizable @issue.description, :attachments => @issue.attachments %> <%= textilizable @issue, :description, :attachments => @issue.attachments %>
<% if @issue.attachments.any? %> <% if @issue.attachments.any? %>
<%= link_to_attachments @issue.attachments, :delete_url => (authorize_for('issues', 'destroy_attachment') ? {:controller => 'issues', :action => 'destroy_attachment', :id => @issue} : nil) %> <%= link_to_attachments @issue.attachments, :delete_url => (authorize_for('issues', 'destroy_attachment') ? {:controller => 'issues', :action => 'destroy_attachment', :id => @issue} : nil) %>
+4 -4
View File
@@ -1,12 +1,12 @@
<% user_projects_by_root = User.current.projects.find(:all, :include => :parent).group_by(&:root) %> <% user_projects_by_root = User.current.projects.find(:all, :include => :parent).group_by(&:root) %>
<select onchange="if (this.value != '') { window.location = this.value; }"> <select onchange="if (this.value != '') { window.location = this.value; }">
<option selected><%= l(:label_jump_to_a_project) %></option> <option selected="selected"><%= l(:label_jump_to_a_project) %></option>
<option disabled>---</option> <option disabled="disabled">---</option>
<% user_projects_by_root.keys.sort.each do |root| %> <% user_projects_by_root.keys.sort.each do |root| %>
<%= content_tag('option', root.name, :value => url_for(:controller => 'projects', :action => 'show', :id => root)) %> <%= content_tag('option', h(root.name), :value => url_for(:controller => 'projects', :action => 'show', :id => root)) %>
<% user_projects_by_root[root].sort.each do |project| %> <% user_projects_by_root[root].sort.each do |project| %>
<% next if project == root %> <% next if project == root %>
<%= content_tag('option', ('&#187; ' + project.name), :value => url_for(:controller => 'projects', :action => 'show', :id => project)) %> <%= content_tag('option', ('&#187; ' + h(project.name)), :value => url_for(:controller => 'projects', :action => 'show', :id => project)) %>
<% end %> <% end %>
<% end %> <% end %>
</select> </select>
+11 -11
View File
@@ -1,4 +1,4 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head> <head>
<title><%=h html_title %></title> <title><%=h html_title %></title>
@@ -22,18 +22,18 @@
<div id="account"> <div id="account">
<% if User.current.logged? %> <% if User.current.logged? %>
<%=l(:label_logged_as)%> <%= User.current.login %> - <%=l(:label_logged_as)%> <%= User.current.login %> -
<%= link_to l(:label_my_account), { :controller => 'my', :action => 'account' } %> <%= link_to l(:label_my_account), { :controller => 'my', :action => 'account' }, :class => 'myaccount' %>
<%= link_to l(:label_logout), { :controller => 'account', :action => 'logout' } %> <%= link_to l(:label_logout), { :controller => 'account', :action => 'logout' }, :class => 'logout' %>
<% else %> <% else %>
<%= link_to l(:label_login), { :controller => 'account', :action => 'login' } %> <%= link_to l(:label_login), { :controller => 'account', :action => 'login' }, :class => 'signin' %>
<%= link_to(l(:label_register), :controller => 'account',:action => 'register') if Setting.self_registration? %> <%= link_to(l(:label_register), { :controller => 'account',:action => 'register' }, :class => 'register') if Setting.self_registration? %>
<% end %> <% end %>
</div> </div>
<%= link_to l(:label_home), home_url %> <%= link_to l(:label_home), home_url, :class => 'home' %>
<%= link_to l(:label_my_page), { :controller => 'my', :action => 'page'} if User.current.logged? %> <%= link_to l(:label_my_page), { :controller => 'my', :action => 'page'}, :class => 'mypage' if User.current.logged? %>
<%= link_to l(:label_project_plural), { :controller => 'projects' } %> <%= link_to l(:label_project_plural), { :controller => 'projects' }, :class => 'projects' %>
<%= link_to l(:label_administration), { :controller => 'admin' } if User.current.admin? %> <%= link_to l(:label_administration), { :controller => 'admin' }, :class => 'admin' if User.current.admin? %>
<%= link_to l(:label_help), Redmine::Info.help_url %> <%= link_to l(:label_help), Redmine::Info.help_url, :class => 'help' %>
</div> </div>
<div id="header"> <div id="header">
@@ -73,7 +73,7 @@
<div id="ajax-indicator" style="display:none;"><span><%= l(:label_loading) %></span></div> <div id="ajax-indicator" style="display:none;"><span><%= l(:label_loading) %></span></div>
<div id="footer"> <div id="footer">
Powered by <%= link_to Redmine::Info.app_name, Redmine::Info.url %> <%= Redmine::VERSION %> &copy 2006-2007 Jean-Philippe Lang Powered by <%= link_to Redmine::Info.app_name, Redmine::Info.url %> <%= Redmine::VERSION %> &copy; 2006-2007 Jean-Philippe Lang
</div> </div>
</body> </body>
</html> </html>
@@ -0,0 +1,2 @@
<p><%= l(:mail_body_account_activation_request, @user.login) %></p>
<p><%= link_to @url, @url %></p>
@@ -0,0 +1,2 @@
<%= l(:mail_body_account_activation_request, @user.login) %>
<%= @url %>
+1 -1
View File
@@ -1,3 +1,3 @@
<%= l(:text_issue_added, "##{@issue.id}") %> <%= l(:text_issue_added, "##{@issue.id}") %>
<hr /> <hr />
<%= render :file => "_issue_text_html", :use_full_path => true, :locals => { :issue => @issue, :issue_url => @issue_url } %> <%= render :partial => "issue_text_html", :locals => { :issue => @issue, :issue_url => @issue_url } %>
+1 -1
View File
@@ -1,3 +1,3 @@
<%= l(:text_issue_added, "##{@issue.id}") %> <%= l(:text_issue_added, "##{@issue.id}") %>
---------------------------------------- ----------------------------------------
<%= render :file => "_issue_text_plain", :use_full_path => true, :locals => { :issue => @issue, :issue_url => @issue_url } %> <%= render :partial => "issue_text_plain", :locals => { :issue => @issue, :issue_url => @issue_url } %>
+1 -1
View File
@@ -7,4 +7,4 @@
</ul> </ul>
<%= textilizable(@journal.notes) %> <%= textilizable(@journal.notes) %>
<hr /> <hr />
<%= render :file => "_issue_text_html", :use_full_path => true, :locals => { :issue => @issue, :issue_url => @issue_url } %> <%= render :partial => "issue_text_html", :locals => { :issue => @issue, :issue_url => @issue_url } %>
+1 -1
View File
@@ -5,4 +5,4 @@
<% end %> <% end %>
<%= @journal.notes if @journal.notes? %> <%= @journal.notes if @journal.notes? %>
---------------------------------------- ----------------------------------------
<%= render :file => "_issue_text_plain", :use_full_path => true, :locals => { :issue => @issue, :issue_url => @issue_url } %> <%= render :partial => "issue_text_plain", :locals => { :issue => @issue, :issue_url => @issue_url } %>
@@ -1,4 +1,4 @@
<h1><%=h @message.board.project.name %> - <%=h @message.board.name %>: <%= link_to @message.subject, @message_url %></h1> <h1><%=h @message.board.project.name %> - <%=h @message.board.name %>: <%= link_to @message.subject, @message_url %></h1>
<em><%= @message.author.name %></em> <em><%= @message.author %></em>
<%= textilizable @message.content %> <%= textilizable @message.content %>
@@ -1,4 +1,4 @@
<%= @message_url %> <%= @message_url %>
<%= @message.author.name %> <%= @message.author %>
<%= @message.content %> <%= @message.content %>
+9 -2
View File
@@ -3,12 +3,19 @@
<div class="box"> <div class="box">
<!--[form:message]--> <!--[form:message]-->
<p><label><%= l(:field_subject) %></label><br /> <p><label><%= l(:field_subject) %></label><br />
<%= f.text_field :subject, :required => true, :size => 120 %></p> <%= f.text_field :subject, :required => true, :size => 120 %>
<p><%= f.text_area :content, :required => true, :cols => 80, :rows => 15, :class => 'wiki-edit' %></p> <% if User.current.allowed_to?(:edit_messages, @project) %>
<label><%= f.check_box :sticky %> Sticky</label>
<label><%= f.check_box :locked %> Locked</label>
<% end %>
</p>
<p><%= f.text_area :content, :required => true, :cols => 80, :rows => 15, :class => 'wiki-edit', :id => 'message_content' %></p>
<%= wikitoolbar_for 'message_content' %> <%= wikitoolbar_for 'message_content' %>
<!--[eoform:message]--> <!--[eoform:message]-->
<span class="tabular"> <span class="tabular">
<%= render :partial => 'attachments/form' %> <%= render :partial => 'attachments/form' %>
</span>
</div> </div>
+6
View File
@@ -0,0 +1,6 @@
<h2><%= link_to h(@board.name), :controller => 'boards', :action => 'show', :project_id => @project, :id => @board %> &#187; <%=h @message.subject %></h2>
<% form_for :message, @message, :url => {:action => 'edit'}, :html => {:multipart => true} do |f| %>
<%= render :partial => 'form', :locals => {:f => f} %>
<%= submit_tag l(:button_save) %>
<% end %>
+27 -15
View File
@@ -1,27 +1,39 @@
<h2><%= link_to h(@board.name), :controller => 'boards', :action => 'show', :project_id => @project, :id => @board %> &#187; <%=h @message.subject %></h2> <div class="contextual">
<%= link_to_if_authorized l(:button_edit), {:action => 'edit', :id => @topic}, :class => 'icon icon-edit' %>
<p><span class="author"><%= authoring @message.created_on, @message.author %></span></p> <%= link_to_if_authorized l(:button_delete), {:action => 'destroy', :id => @topic}, :method => :post, :confirm => l(:text_are_you_sure), :class => 'icon icon-del' %>
<div class="wiki"> </div>
<%= textilizable(@message.content, :attachments => @message.attachments) %>
<h2><%= link_to h(@board.name), :controller => 'boards', :action => 'show', :project_id => @project, :id => @board %> &#187; <%=h @topic.subject %></h2>
<div class="message">
<p><span class="author"><%= authoring @topic.created_on, @topic.author %></span></p>
<div class="wiki">
<%= textilizable(@topic.content, :attachments => @topic.attachments) %>
</div>
<%= link_to_attachments @topic.attachments, :no_author => true %>
</div> </div>
<%= link_to_attachments @message.attachments, :no_author => true %>
<br /> <br />
<h3 class="icon22 icon22-comment"><%= l(:label_reply_plural) %></h3> <h3 class="icon22 icon22-comment"><%= l(:label_reply_plural) %></h3>
<% @message.children.each do |message| %> <% @topic.children.each do |message| %>
<a name="<%= "message-#{message.id}" %>"></a> <a name="<%= "message-#{message.id}" %>"></a>
<h4><%=h message.subject %> - <%= message.author.name %>, <%= format_time(message.created_on) %></h4> <div class="contextual">
<%= link_to_if_authorized l(:button_edit), {:action => 'edit', :id => message}, :class => 'icon icon-edit' %>
<%= link_to_if_authorized l(:button_delete), {:action => 'destroy', :id => message}, :method => :post, :confirm => l(:text_are_you_sure), :class => 'icon icon-del' %>
</div>
<div class="message reply">
<h4><%=h message.subject %> - <%= authoring message.created_on, message.author %></h4>
<div class="wiki"><%= textilizable message.content %></div> <div class="wiki"><%= textilizable message.content %></div>
<%= link_to_attachments message.attachments, :no_author => true %>
</div>
<% end %> <% end %>
<% if authorize_for('messages', 'reply') %> <% if !@topic.locked? && authorize_for('messages', 'reply') %>
<p><%= toggle_link l(:button_reply), "reply", :focus => "reply_content" %></p> <p><%= toggle_link l(:button_reply), "reply", :focus => 'message_content' %></p>
<div id="reply" style="display:none;"> <div id="reply" style="display:none;">
<%= error_messages_for 'message' %> <% form_for :reply, @reply, :url => {:action => 'reply', :id => @topic}, :html => {:multipart => true} do |f| %>
<% form_for :reply, @reply, :url => {:action => 'reply', :id => @message} do |f| %> <%= render :partial => 'form', :locals => {:f => f} %>
<p><%= f.text_field :subject, :required => true, :size => 60 %></p> <%= submit_tag l(:button_submit) %>
<p><%= f.text_area :content, :required => true, :cols => 80, :rows => 10 %></p>
<p><%= submit_tag l(:button_submit) %></p>
<% end %> <% end %>
</div> </div>
<% end %> <% end %>
+9 -2
View File
@@ -4,7 +4,10 @@
<h2><%=l(:label_my_account)%></h2> <h2><%=l(:label_my_account)%></h2>
<%= error_messages_for 'user' %> <%= error_messages_for 'user' %>
<% form_for :user, @user, :url => { :action => "account" }, :builder => TabularFormBuilder, :lang => current_language do |f| %> <% form_for :user, @user, :url => { :action => "account" },
:builder => TabularFormBuilder,
:lang => current_language,
:html => { :id => 'my_account_form' } do |f| %>
<div class="splitcontentleft"> <div class="splitcontentleft">
<h3><%=l(:label_information_plural)%></h3> <h3><%=l(:label_information_plural)%></h3>
<div class="box tabular"> <div class="box tabular">
@@ -14,6 +17,7 @@
<p><%= f.select :language, lang_options_for_select %></p> <p><%= f.select :language, lang_options_for_select %></p>
<% fields_for :pref, @user.pref, :builder => TabularFormBuilder, :lang => current_language do |pref_fields| %> <% fields_for :pref, @user.pref, :builder => TabularFormBuilder, :lang => current_language do |pref_fields| %>
<p><%= pref_fields.select :time_zone, TimeZone.all.collect {|z| [ z.to_s, z.name ]}, :include_blank => true %></p>
<p><%= pref_fields.check_box :hide_mail %></p> <p><%= pref_fields.check_box :hide_mail %></p>
<% end %> <% end %>
</div> </div>
@@ -28,10 +32,11 @@
:onchange => 'if ($("notification_option").value == "selected") {Element.show("notified-projects")} else {Element.hide("notified-projects")}' %> :onchange => 'if ($("notification_option").value == "selected") {Element.show("notified-projects")} else {Element.hide("notified-projects")}' %>
<% content_tag 'div', :id => 'notified-projects', :style => (@notification_option == 'selected' ? '' : 'display:none;') do %> <% content_tag 'div', :id => 'notified-projects', :style => (@notification_option == 'selected' ? '' : 'display:none;') do %>
<p><% User.current.projects.each do |project| %> <p><% User.current.projects.each do |project| %>
<label><%= check_box_tag 'notified_project_ids[]', project.id, @user.notified_projects_ids.include?(project.id) %> <%= project.name %></label><br /> <label><%= check_box_tag 'notified_project_ids[]', project.id, @user.notified_projects_ids.include?(project.id) %> <%=h project.name %></label><br />
<% end %></p> <% end %></p>
<p><em><%= l(:text_user_mail_option) %></em></p> <p><em><%= l(:text_user_mail_option) %></em></p>
<% end %> <% end %>
<p><label><%= check_box_tag 'no_self_notified', 1, @user.pref[:no_self_notified] %> <%= l(:label_user_mail_no_self_notified) %></label></p>
</div> </div>
</div> </div>
<% end %> <% end %>
@@ -39,3 +44,5 @@
<% content_for :sidebar do %> <% content_for :sidebar do %>
<%= render :partial => 'sidebar' %> <%= render :partial => 'sidebar' %>
<% end %> <% end %>
<% set_html_title l(:label_my_account) -%>
+9
View File
@@ -31,3 +31,12 @@
<% end if @blocks['right'] %> <% end if @blocks['right'] %>
</div> </div>
<% content_for :header_tags do %>
<%= javascript_include_tag 'context_menu' %>
<%= stylesheet_link_tag 'context_menu' %>
<% end %>
<div id="context-menu" style="display: none;"></div>
<%= javascript_tag 'new ContextMenu({})' %>
<% set_html_title l(:label_my_page) -%>
+1 -1
View File
@@ -9,7 +9,7 @@
<p><label for="new_password"><%=l(:field_new_password)%> <span class="required">*</span></label> <p><label for="new_password"><%=l(:field_new_password)%> <span class="required">*</span></label>
<%= password_field_tag 'new_password', nil, :size => 25 %><br /> <%= password_field_tag 'new_password', nil, :size => 25 %><br />
<em><%= l(:text_length_between, 4, 12) %></em></p> <em><%= l(:text_caracters_minimum, 4) %></em></p>
<p><label for="new_password_confirmation"><%=l(:field_password_confirmation)%> <span class="required">*</span></label> <p><label for="new_password_confirmation"><%=l(:field_password_confirmation)%> <span class="required">*</span></label>
<%= password_field_tag 'new_password_confirmation', nil, :size => 25 %></p> <%= password_field_tag 'new_password_confirmation', nil, :size => 25 %></p>
+2 -2
View File
@@ -1,6 +1,6 @@
<p><%= link_to(h(news.project.name), :controller => 'projects', :action => 'show', :id => news.project) + ': ' unless @project %> <p><%= link_to(h(news.project.name), :controller => 'projects', :action => 'show', :id => news.project) + ': ' unless @project %>
<%= link_to h(news.title), :controller => 'news', :action => 'show', :id => news %> <%= link_to h(news.title), :controller => 'news', :action => 'show', :id => news %>
<%= "(#{news.comments_count} #{lwr(:label_comment, news.comments_count).downcase})" if news.comments_count > 0 %></span> <%= "(#{news.comments_count} #{lwr(:label_comment, news.comments_count).downcase})" if news.comments_count > 0 %>
<br /> <br />
<% unless news.summary.blank? %><span class="summary"><%=h news.summary %></span><br /><% end %> <% unless news.summary.blank? %><span class="summary"><%=h news.summary %></span><br /><% end %>
<span class="author"><%= authoring news.created_on, news.author %></p> <span class="author"><%= authoring news.created_on, news.author %></span></p>
@@ -1,17 +1,17 @@
<div class="contextual"> <div class="contextual">
<%= link_to_if_authorized l(:label_news_new), <%= link_to_if_authorized(l(:label_news_new),
{:controller => 'projects', :action => 'add_news', :id => @project}, {:controller => 'projects', :action => 'add_news', :id => @project},
:class => 'icon icon-add', :class => 'icon icon-add',
:onclick => 'Element.show("add-news"); return false;' %> :onclick => 'Element.show("add-news"); return false;') if @project %>
</div> </div>
<div id="add-news" style="display:none;"> <div id="add-news" style="display:none;">
<h2><%=l(:label_news_new)%></h2> <h2><%=l(:label_news_new)%></h2>
<% labelled_tabular_form_for :news, @news, :url => { :action => "add_news", :id => @project } do |f| %> <% labelled_tabular_form_for :news, @news, :url => { :controller => 'projects', :action => "add_news", :id => @project } do |f| %>
<%= render :partial => 'news/form', :locals => { :f => f } %> <%= render :partial => 'news/form', :locals => { :f => f } %>
<%= submit_tag l(:button_create) %> <%= submit_tag l(:button_create) %>
<%= link_to l(:button_cancel), "#", :onclick => 'Element.hide("add-news")' %> <%= link_to l(:button_cancel), "#", :onclick => 'Element.hide("add-news")' %>
<% end %> <% end if @project %>
</div> </div>
<h2><%=l(:label_news_plural)%></h2> <h2><%=l(:label_news_plural)%></h2>
@@ -20,7 +20,8 @@
<p class="nodata"><%= l(:label_no_data) %></p> <p class="nodata"><%= l(:label_no_data) %></p>
<% else %> <% else %>
<% @newss.each do |news| %> <% @newss.each do |news| %>
<h3><%= link_to h(news.title), :controller => 'news', :action => 'show', :id => news %> <h3><%= link_to(h(news.project.name), :controller => 'projects', :action => 'show', :id => news.project) + ': ' unless news.project == @project %>
<%= link_to h(news.title), :controller => 'news', :action => 'show', :id => news %>
<%= "(#{news.comments_count} #{lwr(:label_comment, news.comments_count).downcase})" if news.comments_count > 0 %></h3> <%= "(#{news.comments_count} #{lwr(:label_comment, news.comments_count).downcase})" if news.comments_count > 0 %></h3>
<p class="author"><%= authoring news.created_on, news.author %></p> <p class="author"><%= authoring news.created_on, news.author %></p>
<%= textilizable(news.description) %> <%= textilizable(news.description) %>
@@ -31,3 +32,5 @@
<% content_for :header_tags do %> <% content_for :header_tags do %>
<%= auto_discovery_link_tag(:atom, params.merge({:format => 'atom', :page => nil, :key => User.current.rss_key})) %> <%= auto_discovery_link_tag(:atom, params.merge({:format => 'atom', :page => nil, :key => User.current.rss_key})) %>
<% end %> <% end %>
<% set_html_title l(:label_news_plural) -%>
+6 -3
View File
@@ -26,18 +26,21 @@
<h3 class="icon22 icon22-comment"><%= l(:label_comment_plural) %></h3> <h3 class="icon22 icon22-comment"><%= l(:label_comment_plural) %></h3>
<% @news.comments.each do |comment| %> <% @news.comments.each do |comment| %>
<% next if comment.new_record? %> <% next if comment.new_record? %>
<h4><%= format_time(comment.created_on) %> - <%= comment.author.name %></h4> <h4><%= authoring comment.created_on, comment.author %></h4>
<div class="contextual"> <div class="contextual">
<%= link_to_if_authorized l(:button_delete), {:controller => 'news', :action => 'destroy_comment', :id => @news, :comment_id => comment}, :confirm => l(:text_are_you_sure), :method => :post, :class => 'icon icon-del' %> <%= link_to_if_authorized l(:button_delete), {:controller => 'news', :action => 'destroy_comment', :id => @news, :comment_id => comment}, :confirm => l(:text_are_you_sure), :method => :post, :class => 'icon icon-del' %>
</div> </div>
<%= simple_format(auto_link(h comment.comments))%> <%= textilizable(comment.comments) %>
<% end if @news.comments_count > 0 %> <% end if @news.comments_count > 0 %>
</div> </div>
<% if authorize_for 'news', 'add_comment' %> <% if authorize_for 'news', 'add_comment' %>
<p><%= toggle_link l(:label_comment_add), "add_comment_form", :focus => "comment_comments" %></p> <p><%= toggle_link l(:label_comment_add), "add_comment_form", :focus => "comment_comments" %></p>
<% form_tag({:action => 'add_comment', :id => @news}, :id => "add_comment_form", :style => "display:none;") do %> <% form_tag({:action => 'add_comment', :id => @news}, :id => "add_comment_form", :style => "display:none;") do %>
<%= text_area 'comment', 'comments', :cols => 60, :rows => 6 %> <%= text_area 'comment', 'comments', :cols => 80, :rows => 15, :class => 'wiki-edit' %>
<%= wikitoolbar_for 'comment_comments' %>
<p><%= submit_tag l(:button_add) %></p> <p><%= submit_tag l(:button_add) %></p>
<% end %> <% end %>
<% end %> <% end %>
<% set_html_title(h(@news.title)) -%>
+21 -5
View File
@@ -9,7 +9,7 @@
<% end %> <% end %>
<p><%= f.text_area :description, :required => true, :cols => 60, :rows => 5 %><em><%= l(:text_caracters_maximum, 255) %></em></p> <p><%= f.text_area :description, :required => true, :cols => 60, :rows => 5 %><em><%= l(:text_caracters_maximum, 255) %></em></p>
<p><%= f.text_field :identifier, :required => true, :size => 15, :disabled => @project.identifier_frozen? %><br /><em><%= l(:text_length_between, 3, 12) %> <%= l(:text_project_identifier_info) unless @project.identifier_frozen? %></em></p> <p><%= f.text_field :identifier, :required => true, :disabled => @project.identifier_frozen? %><br /><em><%= l(:text_length_between, 3, 20) %> <%= l(:text_project_identifier_info) unless @project.identifier_frozen? %></em></p>
<p><%= f.text_field :homepage, :size => 40 %></p> <p><%= f.text_field :homepage, :size => 40 %></p>
<p><%= f.check_box :is_public %></p> <p><%= f.check_box :is_public %></p>
<%= wikitoolbar_for 'project_description' %> <%= wikitoolbar_for 'project_description' %>
@@ -17,16 +17,32 @@
<% for @custom_value in @custom_values %> <% for @custom_value in @custom_values %>
<p><%= custom_field_tag_with_label @custom_value %></p> <p><%= custom_field_tag_with_label @custom_value %></p>
<% end %> <% end %>
</div>
<% unless @trackers.empty? %>
<fieldset class="box"><legend><%=l(:label_tracker_plural)%></legend>
<% @trackers.each do |tracker| %>
<label class="floating">
<%= check_box_tag 'project[tracker_ids][]', tracker.id, @project.trackers.include?(tracker) %>
<%= tracker %>
</label>
<% end %>
<%= hidden_field_tag 'project[tracker_ids][]', '' %>
</fieldset>
<% end %>
<% unless @custom_fields.empty? %> <% unless @custom_fields.empty? %>
<p><label><%=l(:label_custom_field_plural)%></label> <fieldset class="box"><legend><%=l(:label_custom_field_plural)%></legend>
<% for custom_field in @custom_fields %> <% for custom_field in @custom_fields %>
<label class="floating">
<%= check_box_tag "custom_field_ids[]", custom_field.id, ((@project.custom_fields.include? custom_field) or custom_field.is_for_all?), (custom_field.is_for_all? ? {:disabled => "disabled"} : {}) %> <%= check_box_tag "custom_field_ids[]", custom_field.id, ((@project.custom_fields.include? custom_field) or custom_field.is_for_all?), (custom_field.is_for_all? ? {:disabled => "disabled"} : {}) %>
<%= custom_field.name %> <%= custom_field.name %>
<% end %></p> </label>
<% end %>
</fieldset>
<% end %> <% end %>
<!--[eoform:project]--> <!--[eoform:project]-->
</div>
<% content_for :header_tags do %> <% content_for :header_tags do %>
<%= javascript_include_tag 'calendar/calendar' %> <%= javascript_include_tag 'calendar/calendar' %>
+4 -2
View File
@@ -4,9 +4,9 @@
<h3><%= day_name(day.cwday) %> <%= day.day %></h3> <h3><%= day_name(day.cwday) %> <%= day.day %></h3>
<ul> <ul>
<% @events_by_day[day].sort {|x,y| y.event_datetime <=> x.event_datetime }.each do |e| %> <% @events_by_day[day].sort {|x,y| y.event_datetime <=> x.event_datetime }.each do |e| %>
<li><p><%= e.event_datetime.strftime("%H:%M") %> <%= link_to truncate(e.event_title, 100), e.event_url %><br /> <li><p><%= format_time(e.event_datetime, false) %> <%= link_to truncate(e.event_title, 100), e.event_url %><br />
<% unless e.event_description.blank? %><em><%= truncate(e.event_description, 500) %></em><br /><% end %> <% unless e.event_description.blank? %><em><%= truncate(e.event_description, 500) %></em><br /><% end %>
<span class="author"><%= e.event_author if e.respond_to?(:author) %></span></p></li> <span class="author"><%= e.event_author if e.respond_to?(:event_author) %></span></p></li>
<% end %> <% end %>
</ul> </ul>
<% end %> <% end %>
@@ -39,3 +39,5 @@
<p><%= submit_tag l(:button_apply), :class => 'button-small' %></p> <p><%= submit_tag l(:button_apply), :class => 'button-small' %></p>
<% end %> <% end %>
<% end %> <% end %>
<% set_html_title l(:label_activity) -%>
+6 -6
View File
@@ -3,13 +3,13 @@
<% labelled_tabular_form_for :project, @project, :url => { :action => "add" } do |f| %> <% labelled_tabular_form_for :project, @project, :url => { :action => "add" } do |f| %>
<%= render :partial => 'form', :locals => { :f => f } %> <%= render :partial => 'form', :locals => { :f => f } %>
<div class="box"> <fieldset class="box"><legend><%= l(:label_module_plural) %></legend>
<p><label><%= l(:label_module_plural) %></label>
<% Redmine::AccessControl.available_project_modules.each do |m| %> <% Redmine::AccessControl.available_project_modules.each do |m| %>
<%= check_box_tag 'enabled_modules[]', m, @project.module_enabled?(m) %> <%= m.to_s.humanize %> <label class="floating">
<% end %></p> <%= check_box_tag 'enabled_modules[]', m, @project.module_enabled?(m) %> <%= m.to_s.humanize %>
</div> </label>
<% end %>
</fieldset>
<%= submit_tag l(:button_save) %> <%= submit_tag l(:button_save) %>
<% end %> <% end %>

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