Compare commits

...
140 Commits
Author SHA1 Message Date
Jean-Philippe Lang 3c9a5f1e23 tagged version 0.5.0
git-svn-id: http://redmine.rubyforge.org/svn/tags/0.5.0@444 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-04-11 15:46:37 +00:00
Jean-Philippe Lang 6a8ddc954f Doc files updated for 0.5.0 release
git-svn-id: http://redmine.rubyforge.org/svn/trunk@443 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-04-11 15:33:27 +00:00
Jean-Philippe Lang 8f1c0eb2dd Closed issues no longer displayed on "my page" -> "issues assigned to me".
git-svn-id: http://redmine.rubyforge.org/svn/trunk@442 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-04-11 15:18:36 +00:00
Jean-Philippe Lang 8c01275ee9 Japanese lang file updated (Satoru Kurashiki)
git-svn-id: http://redmine.rubyforge.org/svn/trunk@441 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-04-11 15:14:44 +00:00
Jean-Philippe Lang d6f4208a15 Fixed 10031 revision links and auto linking URLs conflict
git-svn-id: http://redmine.rubyforge.org/svn/trunk@440 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-04-11 15:10:02 +00:00
Jean-Philippe Lang 2e78bc1bc4 Fixed pagination links on admin project listing
git-svn-id: http://redmine.rubyforge.org/svn/trunk@439 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-04-10 17:08:59 +00:00
Jean-Philippe Lang 20feb44564 Changesets comments truncated at 500 characters on activity view.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@438 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-04-09 18:18:23 +00:00
Jean-Philippe Lang 1506d3ddab Form to add an attachment on document view is now togglable.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@437 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-04-09 17:59:31 +00:00
Jean-Philippe Lang 5cb64fff91 Replaced deprecated content_for_layout in the layout.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@436 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-04-08 20:43:57 +00:00
Jean-Philippe Lang 1adf56664d Replaced hard coded help link.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@435 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-04-08 18:16:45 +00:00
Jean-Philippe Lang 183ede84fd Various changes on views. On project summary, members are now grouped by role and subprojects are listed inline.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@434 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-04-08 17:44:54 +00:00
Jean-Philippe Lang bd9a44c225 Added permissions report (link available on roles list view).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@433 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-04-07 20:27:19 +00:00
Jean-Philippe Lang 30734ba8e8 Added toggle_link helper.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@432 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-04-07 18:56:12 +00:00
Jean-Philippe Lang cf4651b6bb Added link_to_issue helper.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@431 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-04-07 18:30:01 +00:00
Jean-Philippe Lang 14314b77ee Added a warning message on tracker list for trackers that don't have any workflow defined.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@430 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-04-07 17:33:07 +00:00
Jean-Philippe Lang ef71edd331 Added versions due dates on calendar.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@429 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-04-07 12:16:09 +00:00
Jean-Philippe Lang 708c3c9ec6 Added versions due dates on gantt chart.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@428 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-04-07 12:09:01 +00:00
Jean-Philippe Lang c1a5c5b9f8 Removed repository creation callback
git-svn-id: http://redmine.rubyforge.org/svn/trunk@427 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-04-07 09:49:36 +00:00
Jean-Philippe Lang 1cc7006555 Modified the change_status view to use the form builder.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@426 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-04-05 18:02:56 +00:00
Jean-Philippe Lang 3f87f3c47a Feature 9867 Allow file upload in comment and add to issue history
git-svn-id: http://redmine.rubyforge.org/svn/trunk@425 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-04-05 17:20:04 +00:00
Jean-Philippe Lang f3a1b4e38c Added the ability to easily rollback to a previous version of a wiki page.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@424 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-04-05 14:45:44 +00:00
Jean-Philippe Lang 7eb4abae80 Removed RedCloth :hard_break option that broke textile rendering.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@423 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-04-05 14:34:11 +00:00
Jean-Philippe Lang 0ea1b712be "Author" column replaced by "Assigned to" in issue list.
"Assigned to" column added in the csv export.

git-svn-id: http://redmine.rubyforge.org/svn/trunk@422 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-04-05 07:33:51 +00:00
Jean-Philippe Lang 9cf96580bf RedCloth attribute :hard_break set to true to keep line breaks
git-svn-id: http://redmine.rubyforge.org/svn/trunk@421 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-04-04 22:16:17 +00:00
Jean-Philippe Lang 46d67cb349 Fixed a regression (crash when an admin with no role on the project try to create an issue)
git-svn-id: http://redmine.rubyforge.org/svn/trunk@420 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-04-04 21:52:53 +00:00
Jean-Philippe Lang f7d5141251 Added svn:eol-style native property for various files
git-svn-id: http://redmine.rubyforge.org/svn/trunk@419 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-04-04 18:20:45 +00:00
Jean-Philippe Lang 8e11dac375 Added gantt and calendar url in robots file, to avoid recursive indexing by bots.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@418 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-04-04 18:09:28 +00:00
Jean-Philippe Lang adee6de140 Added a unit test on User#role_for_project
git-svn-id: http://redmine.rubyforge.org/svn/trunk@417 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-04-04 17:52:07 +00:00
Jean-Philippe Lang 4ed89dc2d3 Fixed User role_for_project method
git-svn-id: http://redmine.rubyforge.org/svn/trunk@416 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-04-04 17:46:51 +00:00
Jean-Philippe Lang 8e6d575d4e Modified logged_in_user_membership controller method so that it returns a role
git-svn-id: http://redmine.rubyforge.org/svn/trunk@415 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-04-04 17:32:23 +00:00
Jean-Philippe Lang e227b92972 Various code cleaning, mainly on User, Permission and IssueStatus models.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@414 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-04-04 17:26:05 +00:00
Jean-Philippe Lang a9a082f05c Patch 9725 Email templates of chinese simplified (Andy Wu)
git-svn-id: http://redmine.rubyforge.org/svn/trunk@413 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-04-04 16:22:00 +00:00
Jean-Philippe Lang be54cb9531 Feature #9808 Allow user to "change" issue status to the same status
git-svn-id: http://redmine.rubyforge.org/svn/trunk@412 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-04-04 16:14:26 +00:00
Jean-Philippe Lang 18a6b8e101 Fixed 9793 Preview fail for first wiki page of a project
git-svn-id: http://redmine.rubyforge.org/svn/trunk@411 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-04-03 22:24:59 +00:00
Jean-Philippe Lang 52ced19e2e Added subprojects issue count on project "Reports" page
git-svn-id: http://redmine.rubyforge.org/svn/trunk@410 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-04-03 19:48:49 +00:00
Jean-Philippe Lang 1c0971b12c Fixed pagination on users/list (status filter lost)
git-svn-id: http://redmine.rubyforge.org/svn/trunk@409 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-04-03 19:22:56 +00:00
Jean-Philippe Lang 2db9bdf7a2 Slight modifications on revisions table
git-svn-id: http://redmine.rubyforge.org/svn/trunk@408 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-04-03 19:16:09 +00:00
Jean-Philippe Lang a09b2a23b9 Feature 9784 Set status when raising issue.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@407 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-04-03 18:34:22 +00:00
Jean-Philippe Lang 6d2ecefca0 Quick fix for:
* 9750 sort field is lost, when displaying next part of issues list
* 9782 show issues in page is wrong

Still some problems, eg. status criteria is lost when paginating or sorting on users/list

git-svn-id: http://redmine.rubyforge.org/svn/trunk@406 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-04-03 17:46:05 +00:00
Jean-Philippe Lang 568c22016a Colored square added on "my page" issue list
git-svn-id: http://redmine.rubyforge.org/svn/trunk@405 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-04-02 20:24:02 +00:00
Jean-Philippe Lang 6e1b9326e4 Added parent project name (if it exists) on project list and project overview.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@404 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-04-02 20:17:02 +00:00
Jean-Philippe Lang 3c8a38212e Applied the same column width in different similar views.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@403 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-04-02 19:27:05 +00:00
Jean-Philippe Lang eba4af7a9f added svn-executable property on perl scripts
git-svn-id: http://redmine.rubyforge.org/svn/trunk@402 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-04-02 19:01:31 +00:00
Jean-Philippe Lang f8a5725214 Renamed the repository management script and removed hard coded parameters.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@401 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-04-02 19:00:22 +00:00
Jean-Philippe Lang 791077c240 Added some attributes length validations.
Also added some information about attribute length constraints on forms.

git-svn-id: http://redmine.rubyforge.org/svn/trunk@400 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-04-02 18:44:35 +00:00
Jean-Philippe Lang bd8de9a6f8 Added the ability to copy an existing workflow when creating a new tracker.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@399 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-04-02 17:45:21 +00:00
Jean-Philippe Lang 0a82489ddc Added the ability to include subprojects issues on calendar & gantt (options box)
git-svn-id: http://redmine.rubyforge.org/svn/trunk@398 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-04-02 17:12:02 +00:00
Jean-Philippe Lang 006b8716ed Migration to add identifier attribute on project.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@397 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-04-02 06:29:09 +00:00
Jean-Philippe Lang 4ff8386e3d Initial commit for svn repository management and access control:
* Identifier attribute added on Project model. Used as the unix group name for the project
* Web services (disabled by default) and scripts for repository management on a remote svn host

git-svn-id: http://redmine.rubyforge.org/svn/trunk@396 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-04-01 19:43:59 +00:00
Jean-Philippe Lang cb6c8bee47 Patch #9705 Tracker filtering on Main Calendar (Jeffrey Jones)
Slightly edited.

git-svn-id: http://redmine.rubyforge.org/svn/trunk@395 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-31 17:15:30 +00:00
Jean-Philippe Lang 3a35883250 Added calendar-zh.js for chinese translation (Andy Wu)
git-svn-id: http://redmine.rubyforge.org/svn/trunk@394 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-31 15:53:56 +00:00
Jean-Philippe Lang 2a00565bc4 Added chinese simplified translation (Andy Wu)
Fixed rfpdf chinese.rb

git-svn-id: http://redmine.rubyforge.org/svn/trunk@393 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-31 15:51:44 +00:00
Jean-Philippe Lang 5e0e951867 Japanese lang file updated (Satoru KURASHIKI)
git-svn-id: http://redmine.rubyforge.org/svn/trunk@392 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-30 23:02:35 +00:00
Jean-Philippe Lang 4c75bd404b removed hard coded strings in views
git-svn-id: http://redmine.rubyforge.org/svn/trunk@391 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-29 19:02:48 +00:00
Jean-Philippe Lang 2b70760594 patch #9627 Add Side by Side in Diff view (Cyril Mougel)
git-svn-id: http://redmine.rubyforge.org/svn/trunk@390 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-29 18:14:59 +00:00
Jean-Philippe Lang d8ce97f766 german lang file updated (Thomas Löber)
git-svn-id: http://redmine.rubyforge.org/svn/trunk@389 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-28 17:52:20 +00:00
Jean-Philippe Lang cf221c8f75 removed filter_html on RedCloth textile conversions
git-svn-id: http://redmine.rubyforge.org/svn/trunk@388 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-27 17:49:52 +00:00
Jean-Philippe Lang b938c60396 slight svg rendering modifications
git-svn-id: http://redmine.rubyforge.org/svn/trunk@387 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-26 18:13:42 +00:00
Jean-Philippe Lang 3a28fa01b0 added info about textile availabality on admin/info
git-svn-id: http://redmine.rubyforge.org/svn/trunk@386 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-26 18:03:25 +00:00
Jean-Philippe Lang 8a84884d50 added changes counts on the "commits per author" svn stat graph
git-svn-id: http://redmine.rubyforge.org/svn/trunk@385 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-26 17:14:06 +00:00
Jean-Philippe Lang b103052ff0 replaced an hard coded label
git-svn-id: http://redmine.rubyforge.org/svn/trunk@384 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-26 16:47:35 +00:00
Jean-Philippe Lang 71b6dd232e fixed #9542 sqlite3 exception in db:migrate on AddChangesetCommitDate
git-svn-id: http://redmine.rubyforge.org/svn/trunk@383 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-26 16:41:54 +00:00
Jean-Philippe Lang 1f7863e323 fixed bug 9537 fetch_changesets broken
git-svn-id: http://redmine.rubyforge.org/svn/trunk@382 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-25 22:33:40 +00:00
Jean-Philippe Lang e951209d68 added pagination on revisions list
git-svn-id: http://redmine.rubyforge.org/svn/trunk@381 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-25 18:18:29 +00:00
Jean-Philippe Lang 0ef114e006 added simple svn statistics graphs, rendered using SVG::Graph
git-svn-id: http://redmine.rubyforge.org/svn/trunk@380 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-25 17:11:46 +00:00
Jean-Philippe Lang 887f11435b Patch #9530 Progress bars for roadmap versions (Nick Read)
Slightly edited and links to issues list added

git-svn-id: http://redmine.rubyforge.org/svn/trunk@379 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-25 12:59:48 +00:00
Jean-Philippe Lang d7f1b873c8 show anonymous on activity view when a commit has no author
git-svn-id: http://redmine.rubyforge.org/svn/trunk@378 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-25 12:24:07 +00:00
Jean-Philippe Lang 16f9f50f50 SVN commits are now stored in the database, and added to the activity view and the search engine.
New commits are automatically retrieved and stored when consulting the repository in the app. This behaviour can be disabled by unchecking 'Autofecth commits' in configuration settings.
Commits can be fetched offline by running (recommanded at least for the initial import of past commits): 
ruby script/runner "Repository.fetch_changesets"
It will load commits for all of the repositories.

git-svn-id: http://redmine.rubyforge.org/svn/trunk@377 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-25 12:12:15 +00:00
Jean-Philippe Lang 6f1dcdc08f updated INSTALL doc with rake db:migrate instead of the deprecated rake migrate
git-svn-id: http://redmine.rubyforge.org/svn/trunk@376 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-24 20:09:07 +00:00
Jean-Philippe Lang 3cd50da3b0 text formatting setting cached at controller level
git-svn-id: http://redmine.rubyforge.org/svn/trunk@375 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-24 09:16:33 +00:00
Jean-Philippe Lang 0125ec3b48 removed td padding
git-svn-id: http://redmine.rubyforge.org/svn/trunk@374 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-23 23:50:12 +00:00
Jean-Philippe Lang 7611f52faf added some parenthesis
git-svn-id: http://redmine.rubyforge.org/svn/trunk@373 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-23 19:03:22 +00:00
Jean-Philippe Lang 669b2342ff added some parenthesis in views
git-svn-id: http://redmine.rubyforge.org/svn/trunk@372 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-23 19:00:48 +00:00
Jean-Philippe Lang 1493c2a9b4 added roadmap and activity calls in projects functional tests
git-svn-id: http://redmine.rubyforge.org/svn/trunk@371 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-23 15:58:53 +00:00
Jean-Philippe Lang 724ca74f51 patch #9429 Display Wiki edits in activity log (Nick Read)
git-svn-id: http://redmine.rubyforge.org/svn/trunk@370 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-23 15:58:02 +00:00
Jean-Philippe Lang 76d4ac17a2 added 999 as a maximum for time entry hours
git-svn-id: http://redmine.rubyforge.org/svn/trunk@369 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-23 13:11:47 +00:00
Jean-Philippe Lang 8d54d97007 Simple time tracking functionality added. Time can be logged at issue or project level.
There's no aggregation reports for now, it's just possible to see all time entries for a project or an issue.
A new "activities" enumeration is added.
Permission for a role to log time must be set (new "Time tracking" section in role permissions screen).

git-svn-id: http://redmine.rubyforge.org/svn/trunk@368 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-23 12:22:31 +00:00
Jean-Philippe Lang 7cf2d889d8 native eol property set on lang files
git-svn-id: http://redmine.rubyforge.org/svn/trunk@363 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-23 08:15:22 +00:00
Jean-Philippe Lang 59a2d1c974 closed issues are now striked on roadmap
git-svn-id: http://redmine.rubyforge.org/svn/trunk@362 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-23 08:13:59 +00:00
Jean-Philippe Lang 5108a9c6fe german translation improved (thanks to Daniel Weinand)
git-svn-id: http://redmine.rubyforge.org/svn/trunk@361 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-23 08:13:33 +00:00
Jean-Philippe Lang 6e98df0e28 fixed #9397 Roadmap - Distinguishing open and closed issues
(closed) is added at the end of closed issue subjects

git-svn-id: http://redmine.rubyforge.org/svn/trunk@360 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-21 18:56:47 +00:00
Jean-Philippe Lang f3f75f557a added some diagnostic information on admin/info
git-svn-id: http://redmine.rubyforge.org/svn/trunk@359 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-20 20:03:27 +00:00
Jean-Philippe Lang 8bf5759d26 fixed #9398 RSS feed on custom reports not displaying subproject changes
git-svn-id: http://redmine.rubyforge.org/svn/trunk@358 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-20 18:55:33 +00:00
Jean-Philippe Lang cbf797ed95 removed a non matching closing select tag
git-svn-id: http://redmine.rubyforge.org/svn/trunk@357 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-20 18:37:53 +00:00
Jean-Philippe Lang 17c4d733d0 fixed #9403 Include "Fixed version" when creating a new issue
git-svn-id: http://redmine.rubyforge.org/svn/trunk@356 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-20 18:37:21 +00:00
Jean-Philippe Lang dd52fb7e2e accidentally committed a database name change...
git-svn-id: http://redmine.rubyforge.org/svn/trunk@355 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-20 18:34:07 +00:00
Jean-Philippe Lang b1f5103c75 fixed #9401 New Wiki, cannot create new page (good one)
git-svn-id: http://redmine.rubyforge.org/svn/trunk@354 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-20 18:14:23 +00:00
Jean-Philippe Lang f19e8af33b fixed #9404 Issue and revision textile links in issue notes
git-svn-id: http://redmine.rubyforge.org/svn/trunk@353 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-20 18:09:14 +00:00
Jean-Philippe Lang a559c0cfc2 fixed #9401 New Wiki, cannot create new page.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@352 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-20 17:41:04 +00:00
Jean-Philippe Lang 5132252daa added "return false;" for onclick event on unclickable menu items
git-svn-id: http://redmine.rubyforge.org/svn/trunk@351 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-20 17:38:34 +00:00
Jean-Philippe Lang dfb81dd89c added back "subproject" filter on issue list
git-svn-id: http://redmine.rubyforge.org/svn/trunk@350 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-19 20:31:02 +00:00
Jean-Philippe Lang 59e3802c74 fixed migration scripts to work with mysql 5 running in strict mode
git-svn-id: http://redmine.rubyforge.org/svn/trunk@349 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-19 18:12:35 +00:00
Jean-Philippe Lang a1d2acd574 fixed #9374 Edit issue doesn't have blank category
git-svn-id: http://redmine.rubyforge.org/svn/trunk@348 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-19 17:46:02 +00:00
Jean-Philippe Lang 8bb60b6474 added a status filter on users/list
git-svn-id: http://redmine.rubyforge.org/svn/trunk@347 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-18 20:44:33 +00:00
Jean-Philippe Lang 143be7ee02 added a setting option to set the feeds content limit
git-svn-id: http://redmine.rubyforge.org/svn/trunk@346 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-18 18:32:59 +00:00
Jean-Philippe Lang d967bb4a54 added back the anchor for each page on the wiki full export
git-svn-id: http://redmine.rubyforge.org/svn/trunk@345 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-18 16:52:25 +00:00
Jean-Philippe Lang fd37f37ee4 fixed problems when svn path doesn't point to the root directory of the repository.
a root_url property has been added on the repository. it's automatically retrieved the first time the repository is browsed

git-svn-id: http://redmine.rubyforge.org/svn/trunk@344 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-18 15:48:05 +00:00
Jean-Philippe Lang db3e8616c8 better rendering time for projects/calendar
git-svn-id: http://redmine.rubyforge.org/svn/trunk@343 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-18 10:17:59 +00:00
Jean-Philippe Lang 8fdef1f820 admin menu modification
git-svn-id: http://redmine.rubyforge.org/svn/trunk@342 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-18 10:17:21 +00:00
Jean-Philippe Lang b481e76ee2 members are now sorted according to roles order on projects/show
removed the useless "members" item in the project menu

git-svn-id: http://redmine.rubyforge.org/svn/trunk@341 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-17 17:44:29 +00:00
Jean-Philippe Lang 3477f9bfe0 added feed icon
git-svn-id: http://redmine.rubyforge.org/svn/trunk@340 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-17 15:51:26 +00:00
Jean-Philippe Lang 10cf1ccc1a added rss/atom feeds at project levels for:
* news
* new issues reported
* details of issue changes

issue cutom queries can be used as feeds

git-svn-id: http://redmine.rubyforge.org/svn/trunk@339 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-17 15:18:50 +00:00
Jean-Philippe Lang 22b42dc90a fixed self.down in AddSearchPermission migration
git-svn-id: http://redmine.rubyforge.org/svn/trunk@338 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-17 14:50:08 +00:00
Jean-Philippe Lang e6f58e4dcc fixed #9308 table_name pre/suffix support
git-svn-id: http://redmine.rubyforge.org/svn/trunk@337 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-15 22:11:02 +00:00
Jean-Philippe Lang 192b9008bd fixed: error on feeds/news when no news exist
git-svn-id: http://redmine.rubyforge.org/svn/trunk@336 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-15 19:20:49 +00:00
Jean-Philippe Lang 56d0af472d removed project_id presence validation on wiki model which was failing when creating a project with a wiki
git-svn-id: http://redmine.rubyforge.org/svn/trunk@335 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-14 17:23:38 +00:00
Jean-Philippe Lang 85f32b956f added italian translation [Alessio Spadaro]
git-svn-id: http://redmine.rubyforge.org/svn/trunk@334 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-12 18:34:25 +00:00
Jean-Philippe Lang 027bf93849 added svn:eol-style native property on /app files
git-svn-id: http://redmine.rubyforge.org/svn/trunk@333 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-12 17:59:02 +00:00
Jean-Philippe Lang 071f8e18d0 fixed #9207 Can't Save Changes to Project
git-svn-id: http://redmine.rubyforge.org/svn/trunk@332 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-12 17:33:20 +00:00
Jean-Philippe Lang 19d962c3aa added svn:executable property on the scripts
git-svn-id: http://redmine.rubyforge.org/svn/trunk@331 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-12 17:29:04 +00:00
Jean-Philippe Lang dae2d4686e width of all wiki content textareas set to 99.5%
git-svn-id: http://redmine.rubyforge.org/svn/trunk@329 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-11 14:22:53 +00:00
Jean-Philippe Lang fc3ee67b1f send a 404 when trying to access an unexisting repository
git-svn-id: http://redmine.rubyforge.org/svn/trunk@328 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-11 14:06:05 +00:00
Jean-Philippe Lang a54a15523b wiki:
* added "Heading" and "Inline quote" buttons to the toolbar
* changed "Link" button behaviour (now produces [[Link]])
* added a simple help on text formatting

git-svn-id: http://redmine.rubyforge.org/svn/trunk@327 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-11 14:00:15 +00:00
Jean-Philippe Lang 5b135c07d0 fixed the "preview" link on wiki/edit
git-svn-id: http://redmine.rubyforge.org/svn/trunk@326 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-10 15:46:34 +00:00
Jean-Philippe Lang dd83445fd7 extended the search to wiki content
git-svn-id: http://redmine.rubyforge.org/svn/trunk@325 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-10 15:34:38 +00:00
Jean-Philippe Lang 290485b144 added missing calendar-ja.js, partially translated
git-svn-id: http://redmine.rubyforge.org/svn/trunk@324 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-10 15:20:37 +00:00
Jean-Philippe Lang c514316a2e wiki branch merged into trunk
git-svn-id: http://redmine.rubyforge.org/svn/trunk@323 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-10 15:09:49 +00:00
Jean-Philippe Lang 8b98ceb92c improved search engine
* it's now possible to search for multiple words ("all words" or "one of the words" options)
* added a fixed limit for result count

git-svn-id: http://redmine.rubyforge.org/svn/trunk@321 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-10 13:32:04 +00:00
Jean-Philippe Lang 7a20a4d32b feature #9137 Password-protected SVN repositories
* added login and password attributes on repositories
* fixed svn calls (svn waiting for user input)

git-svn-id: http://redmine.rubyforge.org/svn/trunk@319 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-09 18:03:31 +00:00
Jean-Philippe Lang 8cbb78bb1d patch #9104 Disable cleartext logging of passwords in log files
git-svn-id: http://redmine.rubyforge.org/svn/trunk@310 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-07 17:59:41 +00:00
Jean-Philippe Lang ef84339003 fixed #9097: Minor rendering problem within Safari at admin/mail_options and roles permissions
git-svn-id: http://redmine.rubyforge.org/svn/trunk@308 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-06 22:46:58 +00:00
Jean-Philippe Lang 909954ac34 fixed dotted hr for IE6
git-svn-id: http://redmine.rubyforge.org/svn/trunk@305 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-06 18:45:08 +00:00
Jean-Philippe Lang d996643540 adjusted the attachment date column width on pdf issue export to avoid overlapping in japanese
git-svn-id: http://redmine.rubyforge.org/svn/trunk@302 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-05 19:00:16 +00:00
Jean-Philippe Lang 074a4a6e0e fixed #9005: administration-menu doesn't work with prefix-url
git-svn-id: http://redmine.rubyforge.org/svn/trunk@289 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-03 17:35:11 +00:00
Jean-Philippe Lang a6248cba19 added missing field_is_in_roadmap key in lang files
git-svn-id: http://redmine.rubyforge.org/svn/trunk@288 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-03 10:22:38 +00:00
Jean-Philippe Lang 6a875eb691 fix for #8973: Export feature(to csv/pdf) doesn't work in Japanese
csv and pdf encoding are know defined for each language (general_csv_encoding and general_pdf_encoding keys in lang files)
added SJIS font for japanese pdf exports

git-svn-id: http://redmine.rubyforge.org/svn/trunk@287 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-03-01 19:49:04 +00:00
Jean-Philippe Lang 83c96d4f3e search engine improvements and simple test
git-svn-id: http://redmine.rubyforge.org/svn/trunk@285 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-02-28 22:35:22 +00:00
Jean-Philippe Lang 1fcc9e977f updated supported language count in test (now 5)
git-svn-id: http://redmine.rubyforge.org/svn/trunk@284 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-02-28 22:32:45 +00:00
Jean-Philippe Lang c56b95ed68 patch #8936: Properties changes in a diff are showing as modifications to files
git-svn-id: http://redmine.rubyforge.org/svn/trunk@283 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-02-28 19:33:07 +00:00
Jean-Philippe Lang bb3a5bcfd7 patch #8937: Show filename above diff to aid reading multi-file diffs
git-svn-id: http://redmine.rubyforge.org/svn/trunk@282 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-02-28 19:26:53 +00:00
Jean-Philippe Lang 35678a1c09 brackets removed around "View all changes" on issues/show
git-svn-id: http://redmine.rubyforge.org/svn/trunk@281 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-02-27 22:07:42 +00:00
Jean-Philippe Lang c622ad0b50 #8911 Textile formatting for issue notes
git-svn-id: http://redmine.rubyforge.org/svn/trunk@280 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-02-27 22:07:01 +00:00
Jean-Philippe Lang 75324e4859 added a simple search engine. left to do:
- full content search (only subject/titles for now)
- pagination
- results presentation improvement

git-svn-id: http://redmine.rubyforge.org/svn/trunk@279 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-02-27 21:03:15 +00:00
Jean-Philippe Lang 93f07c78d6 japanese translation added (thanks to Satoru Kurashiki)
git-svn-id: http://redmine.rubyforge.org/svn/trunk@278 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-02-27 17:58:59 +00:00
Jean-Philippe Lang 59a619ebbb fixed #8915: crash when adding a user with a wrong length password
git-svn-id: http://redmine.rubyforge.org/svn/trunk@277 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-02-27 17:26:25 +00:00
Jean-Philippe Lang 77938cc11e fixed: no diff display with one line files [#8883]
git-svn-id: http://redmine.rubyforge.org/svn/trunk@276 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-02-26 21:32:16 +00:00
Jean-Philippe Lang ae998984a0 fixed: unable to update repository url
git-svn-id: http://redmine.rubyforge.org/svn/trunk@275 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-02-26 19:20:18 +00:00
Jean-Philippe Lang 2e31311e3c "#id" patterns are turned into links to issues in descriptions and commit messages
git-svn-id: http://redmine.rubyforge.org/svn/trunk@274 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-02-25 19:40:57 +00:00
Jean-Philippe Lang 32782a7f9f added back database adapter name on admin/info
git-svn-id: http://redmine.rubyforge.org/svn/trunk@262 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-02-20 20:17:34 +00:00
Jean-Philippe Lang 4f2d74e25f fixed: error when clicking "add" with no block selected on my/page_layout
git-svn-id: http://redmine.rubyforge.org/svn/trunk@261 e93f8b46-1217-0410-a6f0-8f06a7374b81
2007-02-18 20:20:17 +00:00
363 changed files with 19946 additions and 9850 deletions
+25
View File
@@ -0,0 +1,25 @@
# 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 SysApi < ActionWebService::API::Base
api_method :projects,
:expects => [],
:returns => [[Project]]
api_method :repository_created,
:expects => [:int, :string],
:returns => [:int]
end
+130 -130
View File
@@ -1,131 +1,131 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang # Copyright (C) 2006-2007 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# 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.
class AccountController < ApplicationController class AccountController < ApplicationController
layout 'base' layout 'base'
helper :custom_fields helper :custom_fields
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]
before_filter :require_login, :only => :logout before_filter :require_login, :only => :logout
# Show user's account # Show user's account
def show def show
@user = User.find(params[:id]) @user = User.find(params[:id])
@custom_values = @user.custom_values.find(:all, :include => :custom_field) @custom_values = @user.custom_values.find(:all, :include => :custom_field)
rescue ActiveRecord::RecordNotFound rescue ActiveRecord::RecordNotFound
render_404 render_404
end end
# Login request and validation # Login request and validation
def login def login
if request.get? if request.get?
# Logout user # Logout user
self.logged_in_user = nil self.logged_in_user = nil
else else
# Authenticate user # Authenticate user
user = User.try_to_login(params[:login], params[:password]) user = User.try_to_login(params[:login], params[:password])
if user if user
self.logged_in_user = user self.logged_in_user = user
redirect_back_or_default :controller => 'my', :action => 'page' redirect_back_or_default :controller => 'my', :action => 'page'
else else
flash.now[:notice] = l(:notice_account_invalid_creditentials) flash.now[:notice] = l(:notice_account_invalid_creditentials)
end end
end end
end end
# Log out current user and redirect to welcome page # Log out current user and redirect to welcome page
def logout def logout
self.logged_in_user = nil self.logged_in_user = nil
redirect_to :controller => 'welcome' redirect_to :controller => 'welcome'
end end
# Enable user to choose a new password # Enable user to choose a new password
def lost_password def lost_password
redirect_to :controller => 'welcome' and return unless Setting.lost_password? redirect_to :controller => 'welcome' and return unless Setting.lost_password?
if params[:token] if params[:token]
@token = Token.find_by_action_and_value("recovery", params[:token]) @token = Token.find_by_action_and_value("recovery", params[:token])
redirect_to :controller => 'welcome' and return unless @token and !@token.expired? redirect_to :controller => 'welcome' and return unless @token and !@token.expired?
@user = @token.user @user = @token.user
if request.post? if request.post?
@user.password, @user.password_confirmation = params[:new_password], params[:new_password_confirmation] @user.password, @user.password_confirmation = params[:new_password], params[:new_password_confirmation]
if @user.save if @user.save
@token.destroy @token.destroy
flash[:notice] = l(:notice_account_password_updated) flash[:notice] = l(:notice_account_password_updated)
redirect_to :action => 'login' redirect_to :action => 'login'
return return
end end
end end
render :template => "account/password_recovery" render :template => "account/password_recovery"
return return
else else
if request.post? if request.post?
user = User.find_by_mail(params[:mail]) user = User.find_by_mail(params[:mail])
# user not found in db # user not found in db
flash.now[:notice] = l(:notice_account_unknown_email) and return unless user flash.now[:notice] = l(:notice_account_unknown_email) and return unless user
# user uses an external authentification # user uses an external authentification
flash.now[:notice] = l(:notice_can_t_change_password) and return if user.auth_source_id flash.now[:notice] = l(:notice_can_t_change_password) and return if user.auth_source_id
# create a new token for password recovery # create a new token for password recovery
token = Token.new(:user => user, :action => "recovery") token = Token.new(:user => user, :action => "recovery")
if token.save if token.save
Mailer.deliver_lost_password(token) Mailer.deliver_lost_password(token)
flash[:notice] = l(:notice_account_lost_email_sent) flash[:notice] = l(:notice_account_lost_email_sent)
redirect_to :action => 'login' redirect_to :action => 'login'
return return
end end
end end
end end
end end
# User self-registration # User self-registration
def register def register
redirect_to :controller => 'welcome' and return unless Setting.self_registration? redirect_to :controller => 'welcome' and return unless Setting.self_registration?
if params[:token] if params[:token]
token = Token.find_by_action_and_value("register", params[:token]) token = Token.find_by_action_and_value("register", params[:token])
redirect_to :controller => 'welcome' and return unless token and !token.expired? redirect_to :controller => 'welcome' and return unless token and !token.expired?
user = token.user user = token.user
redirect_to :controller => 'welcome' and return unless user.status == User::STATUS_REGISTERED redirect_to :controller => 'welcome' and return unless user.status == User::STATUS_REGISTERED
user.status = User::STATUS_ACTIVE user.status = User::STATUS_ACTIVE
if user.save if user.save
token.destroy token.destroy
flash[:notice] = l(:notice_account_activated) flash[:notice] = l(:notice_account_activated)
redirect_to :action => 'login' redirect_to :action => 'login'
return return
end end
else else
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).collect { |x| CustomValue.new(:custom_field => x, :customized => @user) }
else else
@user = User.new(params[:user]) @user = User.new(params[:user])
@user.admin = false @user.admin = false
@user.login = params[:user][:login] @user.login = params[:user][:login]
@user.status = User::STATUS_REGISTERED @user.status = User::STATUS_REGISTERED
@user.password, @user.password_confirmation = params[:password], params[:password_confirmation] @user.password, @user.password_confirmation = params[:password], params[:password_confirmation]
@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).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
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 => 'welcome' and return redirect_to :controller => 'welcome' and return
end end
end end
end end
end end
end end
+59 -54
View File
@@ -1,55 +1,60 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang # Copyright (C) 2006 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# 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.
class AdminController < ApplicationController class AdminController < ApplicationController
layout 'base' layout 'base'
before_filter :require_admin before_filter :require_admin
helper :sort helper :sort
include SortHelper include SortHelper
def index def index
end end
def projects def projects
sort_init 'name', 'asc' sort_init 'name', 'asc'
sort_update sort_update
@project_count = Project.count @project_count = Project.count
@project_pages = Paginator.new self, @project_count, @project_pages = Paginator.new self, @project_count,
15, 15,
params['page'] params['page']
@projects = Project.find :all, :order => sort_clause, @projects = Project.find :all, :order => sort_clause,
:limit => @project_pages.items_per_page, :limit => @project_pages.items_per_page,
:offset => @project_pages.current.offset :offset => @project_pages.current.offset
render :action => "projects", :layout => false if request.xhr? render :action => "projects", :layout => false if request.xhr?
end end
def mail_options def mail_options
@actions = Permission.find(:all, :conditions => ["mail_option=?", true]) || [] @actions = Permission.find(:all, :conditions => ["mail_option=?", true]) || []
if request.post? if request.post?
@actions.each { |a| @actions.each { |a|
a.mail_enabled = (params[:action_ids] || []).include? a.id.to_s a.mail_enabled = (params[:action_ids] || []).include? a.id.to_s
a.save a.save
} }
flash.now[:notice] = l(:notice_successful_update) flash.now[:notice] = l(:notice_successful_update)
end end
end end
def info def info
end @db_adapter_name = ActiveRecord::Base.connection.adapter_name
@flags = Hash.new
@flags[:default_admin_changed] = User.find(:first, :conditions => ["login=? and hashed_password=?", 'admin', User.hash_password('admin')]).nil?
@flags[:file_repository_writable] = File.writable?(Attachment.storage_path)
@flags[:textile_available] = ActionView::Helpers::TextHelper.method_defined? "textilize"
end
end end
+132 -116
View File
@@ -1,94 +1,110 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang # Copyright (C) 2006-2007 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# 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.
class ApplicationController < ActionController::Base class ApplicationController < ActionController::Base
before_filter :check_if_login_required, :set_localization before_filter :check_if_login_required, :set_localization
filter_parameter_logging :password
def logged_in_user=(user)
@logged_in_user = user def logged_in_user=(user)
session[:user_id] = (user ? user.id : nil) @logged_in_user = user
end session[:user_id] = (user ? user.id : nil)
end
def logged_in_user
if session[:user_id] def logged_in_user
@logged_in_user ||= User.find(session[:user_id]) if session[:user_id]
else @logged_in_user ||= User.find(session[:user_id])
nil else
end nil
end end
end
# check if login is globally required to access the application
def check_if_login_required # Returns the role that the logged in user has on the current project
require_login if Setting.login_required? # or nil if current user is not a member of the project
end def logged_in_user_membership
@user_membership ||= logged_in_user.role_for_project(@project)
def set_localization end
lang = begin
if self.logged_in_user and self.logged_in_user.language and !self.logged_in_user.language.empty? and GLoc.valid_languages.include? self.logged_in_user.language.to_sym # check if login is globally required to access the application
self.logged_in_user.language def check_if_login_required
elsif request.env['HTTP_ACCEPT_LANGUAGE'] require_login if Setting.login_required?
accept_lang = parse_qvalues(request.env['HTTP_ACCEPT_LANGUAGE']).first.split('-').first end
if accept_lang and !accept_lang.empty? and GLoc.valid_languages.include? accept_lang.to_sym
accept_lang def set_localization
end lang = begin
end if self.logged_in_user and self.logged_in_user.language and !self.logged_in_user.language.empty? and GLoc.valid_languages.include? self.logged_in_user.language.to_sym
rescue self.logged_in_user.language
nil elsif request.env['HTTP_ACCEPT_LANGUAGE']
end || Setting.default_language accept_lang = parse_qvalues(request.env['HTTP_ACCEPT_LANGUAGE']).first.split('-').first
set_language_if_valid(lang) if accept_lang and !accept_lang.empty? and GLoc.valid_languages.include? accept_lang.to_sym
end accept_lang
end
def require_login end
unless self.logged_in_user rescue
store_location nil
redirect_to :controller => "account", :action => "login" end || Setting.default_language
return false set_language_if_valid(lang)
end end
true
end def require_login
unless self.logged_in_user
def require_admin store_location
return unless require_login redirect_to :controller => "account", :action => "login"
unless self.logged_in_user.admin? return false
render :nothing => true, :status => 403 end
return false true
end end
true
end def require_admin
return unless require_login
# authorizes the user for the requested action. unless self.logged_in_user.admin?
def authorize(ctrl = params[:controller], action = params[:action]) render :nothing => true, :status => 403
# check if action is allowed on public projects return false
if @project.is_public? and Permission.allowed_to_public "%s/%s" % [ ctrl, action ] end
return true true
end end
# if action is not public, force login
return unless require_login # authorizes the user for the requested action.
# admin is always authorized def authorize(ctrl = params[:controller], action = params[:action])
return true if self.logged_in_user.admin? # check if action is allowed on public projects
# if not admin, check membership permission if @project.is_public? and Permission.allowed_to_public "%s/%s" % [ ctrl, action ]
@user_membership ||= Member.find(:first, :conditions => ["user_id=? and project_id=?", self.logged_in_user.id, @project.id]) return true
if @user_membership and Permission.allowed_to_role( "%s/%s" % [ ctrl, action ], @user_membership.role_id ) end
return true # if action is not public, force login
end return unless require_login
render :nothing => true, :status => 403 # admin is always authorized
false return true if self.logged_in_user.admin?
end # if not admin, check membership permission
if logged_in_user_membership and Permission.allowed_to_role( "%s/%s" % [ ctrl, action ], logged_in_user_membership )
return true
end
render :nothing => true, :status => 403
false
end
# make sure that the user is a member of the project (or admin) if project is private
# used as a before_filter for actions that do not require any particular permission on the project
def check_project_privacy
return true if @project.is_public?
return false unless logged_in_user
return true if logged_in_user.admin? || logged_in_user_membership
render :nothing => true, :status => 403
false
end
# store current uri in session. # store current uri in session.
# return to this location by calling redirect_back_or_default # return to this location by calling redirect_back_or_default
def store_location def store_location
@@ -103,30 +119,30 @@ class ApplicationController < ActionController::Base
redirect_to session[:return_to_params] redirect_to session[:return_to_params]
session[:return_to_params] = nil session[:return_to_params] = nil
end end
end
def render_404
@html_title = "404"
render :template => "common/404", :layout => true, :status => 404
return false
end end
# qvalues http header parser def render_404
# code taken from webrick @html_title = "404"
def parse_qvalues(value) render :template => "common/404", :layout => true, :status => 404
tmp = [] return false
if value end
parts = value.split(/,\s*/)
parts.each {|part| # qvalues http header parser
if m = %r{^([^\s,]+?)(?:;\s*q=(\d+(?:\.\d+)?))?$}.match(part) # code taken from webrick
val = m[1] def parse_qvalues(value)
q = (m[2] or 1).to_f tmp = []
tmp.push([val, q]) if value
end parts = value.split(/,\s*/)
} parts.each {|part|
tmp = tmp.sort_by{|val, q| -q} if m = %r{^([^\s,]+?)(?:;\s*q=(\d+(?:\.\d+)?))?$}.match(part)
tmp.collect!{|val, q| val} val = m[1]
end q = (m[2] or 1).to_f
return tmp tmp.push([val, q])
end end
}
tmp = tmp.sort_by{|val, q| -q}
tmp.collect!{|val, q| val}
end
return tmp
end
end end
+59 -59
View File
@@ -1,23 +1,23 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang # Copyright (C) 2006 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# 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.
class CustomFieldsController < ApplicationController class CustomFieldsController < ApplicationController
layout 'base' layout 'base'
before_filter :require_admin before_filter :require_admin
def index def index
list list
@@ -25,48 +25,48 @@ class CustomFieldsController < ApplicationController
end end
def list def list
@custom_fields_by_type = CustomField.find(:all).group_by {|f| f.type.to_s } @custom_fields_by_type = CustomField.find(:all).group_by {|f| f.type.to_s }
@tab = params[:tab] || 'IssueCustomField' @tab = params[:tab] || 'IssueCustomField'
render :action => "list", :layout => false if request.xhr? render :action => "list", :layout => false if request.xhr?
end end
def new def new
case params[:type] case params[:type]
when "IssueCustomField" when "IssueCustomField"
@custom_field = IssueCustomField.new(params[:custom_field]) @custom_field = IssueCustomField.new(params[:custom_field])
@custom_field.trackers = Tracker.find(params[:tracker_ids]) if params[:tracker_ids] @custom_field.trackers = Tracker.find(params[:tracker_ids]) if params[:tracker_ids]
when "UserCustomField" when "UserCustomField"
@custom_field = UserCustomField.new(params[:custom_field]) @custom_field = UserCustomField.new(params[:custom_field])
when "ProjectCustomField" when "ProjectCustomField"
@custom_field = ProjectCustomField.new(params[:custom_field]) @custom_field = ProjectCustomField.new(params[:custom_field])
else else
redirect_to :action => 'list' redirect_to :action => 'list'
return return
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.type
end end
@trackers = Tracker.find(:all, :order => 'position') @trackers = Tracker.find(:all, :order => 'position')
end end
def edit def edit
@custom_field = CustomField.find(params[:id]) @custom_field = CustomField.find(params[:id])
if request.post? and @custom_field.update_attributes(params[:custom_field]) if request.post? and @custom_field.update_attributes(params[:custom_field])
if @custom_field.is_a? IssueCustomField if @custom_field.is_a? IssueCustomField
@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.type
end end
@trackers = Tracker.find(:all, :order => 'position') @trackers = Tracker.find(:all, :order => 'position')
end 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.type
rescue rescue
flash[:notice] = "Unable to delete custom field" flash[:notice] = "Unable to delete custom field"
redirect_to :action => 'list' redirect_to :action => 'list'
end end
end end
+55 -55
View File
@@ -1,71 +1,71 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang # Copyright (C) 2006-2007 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# 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.
class DocumentsController < ApplicationController
layout 'base'
before_filter :find_project, :authorize
def show class DocumentsController < ApplicationController
layout 'base'
before_filter :find_project, :authorize
def show
@attachments = @document.attachments.find(:all, :order => "created_on DESC") @attachments = @document.attachments.find(:all, :order => "created_on DESC")
end end
def edit def edit
@categories = Enumeration::get_values('DCAT') @categories = Enumeration::get_values('DCAT')
if request.post? and @document.update_attributes(params[:document]) if request.post? and @document.update_attributes(params[:document])
flash[:notice] = l(:notice_successful_update) flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'show', :id => @document redirect_to :action => 'show', :id => @document
end end
end end
def destroy def destroy
@document.destroy @document.destroy
redirect_to :controller => 'projects', :action => 'list_documents', :id => @project redirect_to :controller => 'projects', :action => 'list_documents', :id => @project
end
def download
@attachment = @document.attachments.find(params[:attachment_id])
@attachment.increment_download
send_file @attachment.diskfile, :filename => @attachment.filename
rescue
render_404
end
def add_attachment
# Save the attachments
@attachments = []
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_add(@attachments) if !@attachments.empty? and Permission.find_by_controller_and_action(params[:controller], params[:action]).mail_enabled?
redirect_to :action => 'show', :id => @document
end
def destroy_attachment
@document.attachments.find(params[:attachment_id]).destroy
redirect_to :action => 'show', :id => @document
end end
private def download
def find_project @attachment = @document.attachments.find(params[:attachment_id])
@document = Document.find(params[:id]) @attachment.increment_download
@project = @document.project send_file @attachment.diskfile, :filename => @attachment.filename
rescue ActiveRecord::RecordNotFound rescue
render_404 render_404
end
def add_attachment
# Save the attachments
@attachments = []
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_add(@attachments) if !@attachments.empty? and Permission.find_by_controller_and_action(params[:controller], params[:action]).mail_enabled?
redirect_to :action => 'show', :id => @document
end
def destroy_attachment
@document.attachments.find(params[:attachment_id]).destroy
redirect_to :action => 'show', :id => @document
end
private
def find_project
@document = Document.find(params[:id])
@project = @document.project
rescue ActiveRecord::RecordNotFound
render_404
end end
end end
+24 -24
View File
@@ -1,23 +1,23 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang # Copyright (C) 2006 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# 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.
class EnumerationsController < ApplicationController class EnumerationsController < ApplicationController
layout 'base' layout 'base'
before_filter :require_admin before_filter :require_admin
def index def index
list list
@@ -60,11 +60,11 @@ class EnumerationsController < ApplicationController
end end
def destroy def destroy
Enumeration.find(params[:id]).destroy Enumeration.find(params[:id]).destroy
flash[:notice] = l(:notice_successful_delete) flash[:notice] = l(:notice_successful_delete)
redirect_to :action => 'list' redirect_to :action => 'list'
rescue rescue
flash[:notice] = "Unable to delete enumeration" flash[:notice] = "Unable to delete enumeration"
redirect_to :action => 'list' redirect_to :action => 'list'
end end
end end
+74 -3
View File
@@ -1,5 +1,5 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang # Copyright (C) 2006-2007 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
@@ -16,10 +16,81 @@
# 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 FeedsController < ApplicationController class FeedsController < ApplicationController
before_filter :find_scope
session :off session :off
helper :issues
include IssuesHelper
helper :custom_fields
include CustomFieldsHelper
# news feeds
def news def news
@news = News.find :all, :order => 'news.created_on DESC', :limit => 10, :include => [ :author, :project ] 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" 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])
# 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])
# 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_reported_issues))
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}
return true
end end
end end
+42 -45
View File
@@ -1,47 +1,44 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang # Copyright (C) 2006 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# 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.
class HelpController < ApplicationController class HelpController < ApplicationController
skip_before_filter :check_if_login_required skip_before_filter :check_if_login_required
before_filter :load_help_config before_filter :load_help_config
# displays help page for the requested controller/action # displays help page for the requested controller/action
def index def index
# select help page to display # select help page to display
if params[:ctrl] and @help_config['pages'][params[:ctrl]] if params[:ctrl] and @help_config['pages'][params[:ctrl]]
if params[:page] and @help_config['pages'][params[:ctrl]][params[:page]] if params[:page] and @help_config['pages'][params[:ctrl]][params[:page]]
template = @help_config['pages'][params[:ctrl]][params[:page]] template = @help_config['pages'][params[:ctrl]][params[:page]]
else else
template = @help_config['pages'][params[:ctrl]]['index'] template = @help_config['pages'][params[:ctrl]]['index']
end end
end end
# choose language according to available help translations # choose language according to available help translations
lang = (@help_config['langs'].include? current_language.to_s) ? current_language.to_s : @help_config['langs'].first lang = (@help_config['langs'].include? current_language.to_s) ? current_language.to_s : @help_config['langs'].first
if template url = "/manual/#{lang}/" + (template || "index.html")
redirect_to "/manual/#{lang}/#{template}" redirect_to(request.relative_url_root + url)
else end
redirect_to "/manual/#{lang}/index.html"
end private
end def load_help_config
@help_config = YAML::load(File.open("#{RAILS_ROOT}/config/help.yml"))
private
def load_help_config
@help_config = YAML::load(File.open("#{RAILS_ROOT}/config/help.yml"))
end end
end end
+31 -31
View File
@@ -1,24 +1,24 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang # Copyright (C) 2006 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# 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.
class IssueCategoriesController < ApplicationController class IssueCategoriesController < ApplicationController
layout 'base' layout 'base'
before_filter :find_project, :authorize before_filter :find_project, :authorize
def edit def edit
if request.post? and @category.update_attributes(params[:category]) if request.post? and @category.update_attributes(params[:category])
flash[:notice] = l(:notice_successful_update) flash[:notice] = l(:notice_successful_update)
@@ -28,17 +28,17 @@ class IssueCategoriesController < ApplicationController
def destroy def destroy
@category.destroy @category.destroy
redirect_to :controller => 'projects', :action => 'settings', :tab => 'categories', :id => @project redirect_to :controller => 'projects', :action => 'settings', :tab => 'categories', :id => @project
rescue rescue
flash[:notice] = "Categorie can't be deleted" flash[:notice] = "Categorie can't be deleted"
redirect_to :controller => 'projects', :action => 'settings', :tab => 'categories', :id => @project redirect_to :controller => 'projects', :action => 'settings', :tab => 'categories', :id => @project
end end
private private
def find_project def find_project
@category = IssueCategory.find(params[:id]) @category = IssueCategory.find(params[:id])
@project = @category.project @project = @category.project
rescue ActiveRecord::RecordNotFound rescue ActiveRecord::RecordNotFound
render_404 render_404
end end
end end
+42 -42
View File
@@ -1,34 +1,34 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang # Copyright (C) 2006 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# 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.
class IssueStatusesController < ApplicationController class IssueStatusesController < ApplicationController
layout 'base' layout 'base'
before_filter :require_admin before_filter :require_admin
verify :method => :post, :only => [ :destroy, :create, :update, :move ], verify :method => :post, :only => [ :destroy, :create, :update, :move ],
:redirect_to => { :action => :list } :redirect_to => { :action => :list }
def index def index
list list
render :action => 'list' unless request.xhr? render :action => 'list' unless request.xhr?
end end
def list def list
@issue_status_pages, @issue_statuses = paginate :issue_statuses, :per_page => 25, :order => "position" @issue_status_pages, @issue_statuses = paginate :issue_statuses, :per_page => 25, :order => "position"
render :action => "list", :layout => false if request.xhr? render :action => "list", :layout => false if request.xhr?
end end
@@ -58,28 +58,28 @@ class IssueStatusesController < ApplicationController
else else
render :action => 'edit' render :action => 'edit'
end end
end end
def move def move
@issue_status = IssueStatus.find(params[:id]) @issue_status = IssueStatus.find(params[:id])
case params[:position] case params[:position]
when 'highest' when 'highest'
@issue_status.move_to_top @issue_status.move_to_top
when 'higher' when 'higher'
@issue_status.move_higher @issue_status.move_higher
when 'lower' when 'lower'
@issue_status.move_lower @issue_status.move_lower
when 'lowest' when 'lowest'
@issue_status.move_to_bottom @issue_status.move_to_bottom
end if params[:position] end if params[:position]
redirect_to :action => 'list' redirect_to :action => 'list'
end end
def destroy def destroy
IssueStatus.find(params[:id]).destroy IssueStatus.find(params[:id]).destroy
redirect_to :action => 'list' redirect_to :action => 'list'
rescue rescue
flash[:notice] = "Unable to delete issue status" flash[:notice] = "Unable to delete issue status"
redirect_to :action => 'list' redirect_to :action => 'list'
end end
end end
+145 -135
View File
@@ -1,149 +1,159 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang # Copyright (C) 2006-2007 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# 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.
class IssuesController < ApplicationController class IssuesController < ApplicationController
layout 'base', :except => :export_pdf layout 'base', :except => :export_pdf
before_filter :find_project, :authorize before_filter :find_project, :authorize
helper :custom_fields helper :custom_fields
include CustomFieldsHelper include CustomFieldsHelper
helper :ifpdf helper :ifpdf
include IfpdfHelper include IfpdfHelper
def show def show
@status_options = @issue.status.workflows.find(:all, :order => 'position', :include => :new_status, :conditions => ["role_id=? and tracker_id=?", self.logged_in_user.role_for_project(@project.id), @issue.tracker.id]).collect{ |w| w.new_status } if self.logged_in_user @status_options = @issue.status.find_new_statuses_allowed_to(logged_in_user.role_for_project(@project), @issue.tracker) if logged_in_user
@custom_values = @issue.custom_values.find(:all, :include => :custom_field) @custom_values = @issue.custom_values.find(:all, :include => :custom_field)
@journals_count = @issue.journals.count @journals_count = @issue.journals.count
@journals = @issue.journals.find(:all, :include => [:user, :details], :limit => 15, :order => "journals.created_on desc") @journals = @issue.journals.find(:all, :include => [:user, :details], :limit => 15, :order => "#{Journal.table_name}.created_on desc")
end end
def history def history
@journals = @issue.journals.find(:all, :include => [:user, :details], :order => "journals.created_on desc") @journals = @issue.journals.find(:all, :include => [:user, :details], :order => "#{Journal.table_name}.created_on desc")
@journals_count = @journals.length @journals_count = @journals.length
end end
def export_pdf def export_pdf
@custom_values = @issue.custom_values.find(:all, :include => :custom_field) @custom_values = @issue.custom_values.find(:all, :include => :custom_field)
@options_for_rfpdf ||= {} @options_for_rfpdf ||= {}
@options_for_rfpdf[:file_name] = "#{@project.name}_#{@issue.long_id}.pdf" @options_for_rfpdf[:file_name] = "#{@project.name}_#{@issue.long_id}.pdf"
end end
def edit def edit
@priorities = Enumeration::get_values('IPRI') @priorities = Enumeration::get_values('IPRI')
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(self.logged_in_user)
# Retrieve custom fields and values # Retrieve custom fields and values
@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
@issue.attributes = params[:issue] @issue.attributes = params[:issue]
if @issue.save if @issue.save
flash[:notice] = l(:notice_successful_update) flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'show', :id => @issue redirect_to :action => 'show', :id => @issue
end end
rescue ActiveRecord::StaleObjectError rescue ActiveRecord::StaleObjectError
# Optimistic locking exception # Optimistic locking exception
flash[:notice] = l(:notice_locking_conflict) flash[:notice] = l(:notice_locking_conflict)
end end
end end
end end
def add_note def add_note
unless params[:notes].empty? unless params[:notes].empty?
journal = @issue.init_journal(self.logged_in_user, params[:notes]) journal = @issue.init_journal(self.logged_in_user, params[:notes])
#@history = @issue.histories.build(params[:history]) if @issue.save
#@history.author_id = self.logged_in_user.id if self.logged_in_user flash[:notice] = l(:notice_successful_update)
#@history.status = @issue.status Mailer.deliver_issue_edit(journal) if Permission.find_by_controller_and_action(params[:controller], params[:action]).mail_enabled?
if @issue.save redirect_to :action => 'show', :id => @issue
flash[:notice] = l(:notice_successful_update) return
Mailer.deliver_issue_edit(journal) if Permission.find_by_controller_and_action(params[:controller], params[:action]).mail_enabled? end
redirect_to :action => 'show', :id => @issue end
return show
end render :action => 'show'
end end
show
render :action => 'show' def change_status
end @status_options = @issue.status.find_new_statuses_allowed_to(logged_in_user.role_for_project(@project), @issue.tracker) if logged_in_user
@new_status = IssueStatus.find(params[:new_status_id])
def change_status if params[:confirm]
#@history = @issue.histories.build(params[:history]) begin
@status_options = @issue.status.workflows.find(:all, :order => 'position', :include => :new_status, :conditions => ["role_id=? and tracker_id=?", self.logged_in_user.role_for_project(@project.id), @issue.tracker.id]).collect{ |w| w.new_status } if self.logged_in_user journal = @issue.init_journal(self.logged_in_user, params[:notes])
@new_status = IssueStatus.find(params[:new_status_id]) @issue.status = @new_status
if params[:confirm] if @issue.update_attributes(params[:issue])
begin # Save attachments
#@history.author_id = self.logged_in_user.id if self.logged_in_user params[:attachments].each { |file|
#@issue.status = @history.status next unless file.size > 0
#@issue.fixed_version_id = (params[:issue][:fixed_version_id]) a = Attachment.create(:container => @issue, :file => file, :author => logged_in_user)
#@issue.assigned_to_id = (params[:issue][:assigned_to_id]) journal.details << JournalDetail.new(:property => 'attachment',
#@issue.done_ratio = (params[:issue][:done_ratio]) :prop_key => a.id,
#@issue.lock_version = (params[:issue][:lock_version]) :value => a.filename) unless a.new_record?
journal = @issue.init_journal(self.logged_in_user, params[:notes]) } if params[:attachments] and params[:attachments].is_a? Array
@issue.status = @new_status
if @issue.update_attributes(params[:issue]) flash[:notice] = l(:notice_successful_update)
flash[:notice] = l(:notice_successful_update) Mailer.deliver_issue_edit(journal) if Permission.find_by_controller_and_action(params[:controller], params[:action]).mail_enabled?
Mailer.deliver_issue_edit(journal) if Permission.find_by_controller_and_action(params[:controller], params[:action]).mail_enabled? redirect_to :action => 'show', :id => @issue
redirect_to :action => 'show', :id => @issue end
end rescue ActiveRecord::StaleObjectError
rescue ActiveRecord::StaleObjectError # Optimistic locking exception
# Optimistic locking exception flash[:notice] = l(:notice_locking_conflict)
flash[:notice] = l(:notice_locking_conflict) end
end end
end @assignable_to = @project.members.find(:all, :include => :user).collect{ |m| m.user }
@assignable_to = @project.members.find(:all, :include => :user).collect{ |m| m.user }
end end
def destroy def destroy
@issue.destroy @issue.destroy
redirect_to :controller => 'projects', :action => 'list_issues', :id => @project redirect_to :controller => 'projects', :action => 'list_issues', :id => @project
end end
def add_attachment def add_attachment
# Save the attachments # Save the attachments
@attachments = [] @attachments = []
params[:attachments].each { |file| journal = @issue.init_journal(self.logged_in_user)
next unless file.size > 0 params[:attachments].each { |file|
a = Attachment.create(:container => @issue, :file => file, :author => logged_in_user) next unless file.size > 0
@attachments << a unless a.new_record? a = Attachment.create(:container => @issue, :file => file, :author => logged_in_user)
} if params[:attachments] and params[:attachments].is_a? Array @attachments << a unless a.new_record?
Mailer.deliver_attachments_add(@attachments) if !@attachments.empty? and Permission.find_by_controller_and_action(params[:controller], params[:action]).mail_enabled? journal.details << JournalDetail.new(:property => 'attachment',
redirect_to :action => 'show', :id => @issue :prop_key => a.id,
end :value => a.filename) unless a.new_record?
} if params[:attachments] and params[:attachments].is_a? Array
def destroy_attachment journal.save if journal.details.any?
@issue.attachments.find(params[:attachment_id]).destroy Mailer.deliver_attachments_add(@attachments) if !@attachments.empty? and Permission.find_by_controller_and_action(params[:controller], params[:action]).mail_enabled?
redirect_to :action => 'show', :id => @issue redirect_to :action => 'show', :id => @issue
end end
# Send the file in stream mode def destroy_attachment
def download a = @issue.attachments.find(params[:attachment_id])
@attachment = @issue.attachments.find(params[:attachment_id]) a.destroy
send_file @attachment.diskfile, :filename => @attachment.filename journal = @issue.init_journal(self.logged_in_user)
rescue journal.details << JournalDetail.new(:property => 'attachment',
render_404 :prop_key => a.id,
end :old_value => a.filename)
journal.save
private redirect_to :action => 'show', :id => @issue
def find_project end
@issue = Issue.find(params[:id], :include => [:project, :tracker, :status, :author, :priority, :category])
@project = @issue.project # Send the file in stream mode
@html_title = "#{@project.name} - #{@issue.tracker.name} ##{@issue.id}" def download
rescue ActiveRecord::RecordNotFound @attachment = @issue.attachments.find(params[:attachment_id])
render_404 send_file @attachment.diskfile, :filename => @attachment.filename
rescue
render_404
end
private
def find_project
@issue = Issue.find(params[:id], :include => [:project, :tracker, :status, :author, :priority, :category])
@project = @issue.project
@html_title = "#{@project.name} - #{@issue.tracker.name} ##{@issue.id}"
rescue ActiveRecord::RecordNotFound
render_404
end end
end end
+31 -31
View File
@@ -1,42 +1,42 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang # Copyright (C) 2006 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# 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.
class MembersController < ApplicationController class MembersController < ApplicationController
layout 'base' layout 'base'
before_filter :find_project, :authorize before_filter :find_project, :authorize
def edit def edit
if request.post? and @member.update_attributes(params[:member]) if request.post? and @member.update_attributes(params[:member])
flash[:notice] = l(:notice_successful_update) flash[:notice] = l(:notice_successful_update)
redirect_to :controller => 'projects', :action => 'settings', :tab => 'members', :id => @project redirect_to :controller => 'projects', :action => 'settings', :tab => 'members', :id => @project
end end
end end
def destroy def destroy
@member.destroy @member.destroy
flash[:notice] = l(:notice_successful_delete) flash[:notice] = l(:notice_successful_delete)
redirect_to :controller => 'projects', :action => 'settings', :tab => 'members', :id => @project redirect_to :controller => 'projects', :action => 'settings', :tab => 'members', :id => @project
end end
private private
def find_project def find_project
@member = Member.find(params[:id]) @member = Member.find(params[:id])
@project = @member.project @project = @member.project
rescue ActiveRecord::RecordNotFound rescue ActiveRecord::RecordNotFound
render_404 render_404
end end
end end
+2 -1
View File
@@ -90,8 +90,9 @@ class MyController < ApplicationController
# The block is added on top of the page # The block is added on top of the page
# params[:block] : id of the block to add # params[:block] : id of the block to add
def add_block def add_block
@user = self.logged_in_user
block = params[:block] block = params[:block]
render(:nothing => true) and return unless block && (BLOCKS.keys.include? block)
@user = self.logged_in_user
# 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
+44 -44
View File
@@ -1,23 +1,23 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang # Copyright (C) 2006 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# 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.
class NewsController < ApplicationController class NewsController < ApplicationController
layout 'base' layout 'base'
before_filter :find_project, :authorize before_filter :find_project, :authorize
def show def show
end end
@@ -27,34 +27,34 @@ class NewsController < ApplicationController
flash[:notice] = l(:notice_successful_update) flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'show', :id => @news redirect_to :action => 'show', :id => @news
end end
end
def add_comment
@comment = Comment.new(params[:comment])
@comment.author = logged_in_user
if @news.comments << @comment
flash[:notice] = l(:label_comment_added)
redirect_to :action => 'show', :id => @news
else
render :action => 'show'
end
end end
def destroy_comment def add_comment
@news.comments.find(params[:comment_id]).destroy @comment = Comment.new(params[:comment])
redirect_to :action => 'show', :id => @news @comment.author = logged_in_user
end if @news.comments << @comment
flash[:notice] = l(:label_comment_added)
redirect_to :action => 'show', :id => @news
else
render :action => 'show'
end
end
def destroy_comment
@news.comments.find(params[:comment_id]).destroy
redirect_to :action => 'show', :id => @news
end
def destroy def destroy
@news.destroy @news.destroy
redirect_to :controller => 'projects', :action => 'list_news', :id => @project redirect_to :controller => 'projects', :action => 'list_news', :id => @project
end end
private private
def find_project def find_project
@news = News.find(params[:id]) @news = News.find(params[:id])
@project = @news.project @project = @news.project
rescue ActiveRecord::RecordNotFound rescue ActiveRecord::RecordNotFound
render_404 render_404
end end
end end
File diff suppressed because it is too large Load Diff
+189 -165
View File
@@ -1,167 +1,191 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang # Copyright (C) 2006 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# 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.
class ReportsController < ApplicationController class ReportsController < ApplicationController
layout 'base' layout 'base'
before_filter :find_project, :authorize before_filter :find_project, :authorize
def issue_report def issue_report
@statuses = IssueStatus.find(:all, :order => 'position') @statuses = IssueStatus.find(:all, :order => 'position')
case params[:detail] case params[:detail]
when "tracker" when "tracker"
@field = "tracker_id" @field = "tracker_id"
@rows = Tracker.find :all, :order => 'position' @rows = Tracker.find :all, :order => 'position'
@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"
when "priority" when "priority"
@field = "priority_id" @field = "priority_id"
@rows = Enumeration::get_values('IPRI') @rows = Enumeration::get_values('IPRI')
@data = issues_by_priority @data = issues_by_priority
@report_title = l(:field_priority) @report_title = l(:field_priority)
render :template => "reports/issue_report_details" render :template => "reports/issue_report_details"
when "category" when "category"
@field = "category_id" @field = "category_id"
@rows = @project.issue_categories @rows = @project.issue_categories
@data = issues_by_category @data = issues_by_category
@report_title = l(:field_category) @report_title = l(:field_category)
render :template => "reports/issue_report_details" render :template => "reports/issue_report_details"
when "author" when "author"
@field = "author_id" @field = "author_id"
@rows = @project.members.collect { |m| m.user } @rows = @project.members.collect { |m| m.user }
@data = issues_by_author @data = issues_by_author
@report_title = l(:field_author) @report_title = l(:field_author)
render :template => "reports/issue_report_details" render :template => "reports/issue_report_details"
else when "subproject"
@queries = @project.queries.find :all, :conditions => ["is_public=? or user_id=?", true, (logged_in_user ? logged_in_user.id : 0)] @field = "project_id"
@trackers = Tracker.find(:all, :order => 'position') @rows = @project.children
@priorities = Enumeration::get_values('IPRI') @data = issues_by_subproject
@categories = @project.issue_categories @report_title = l(:field_subproject)
@authors = @project.members.collect { |m| m.user } render :template => "reports/issue_report_details"
issues_by_tracker else
issues_by_priority @queries = @project.queries.find :all, :conditions => ["is_public=? or user_id=?", true, (logged_in_user ? logged_in_user.id : 0)]
issues_by_category @trackers = Tracker.find(:all, :order => 'position')
issues_by_author @priorities = Enumeration::get_values('IPRI')
render :template => "reports/issue_report" @categories = @project.issue_categories
end @authors = @project.members.collect { |m| m.user }
end @subprojects = @project.children
issues_by_tracker
def delays issues_by_priority
@trackers = Tracker.find(:all) issues_by_category
if request.get? issues_by_author
@selected_tracker_ids = @trackers.collect {|t| t.id.to_s } issues_by_subproject
else @total_hours = @project.time_entries.sum(:hours)
@selected_tracker_ids = params[:tracker_ids].collect { |id| id.to_i.to_s } if params[:tracker_ids] and params[:tracker_ids].is_a? Array render :template => "reports/issue_report"
end end
@selected_tracker_ids ||= [] end
@raw =
ActiveRecord::Base.connection.select_all("SELECT datediff( a.created_on, b.created_on ) as delay, count(a.id) as total def delays
FROM issue_histories a, issue_histories b, issues i @trackers = Tracker.find(:all)
WHERE a.status_id =5 if request.get?
AND a.issue_id = b.issue_id @selected_tracker_ids = @trackers.collect {|t| t.id.to_s }
AND a.issue_id = i.id else
AND i.tracker_id in (#{@selected_tracker_ids.join(',')}) @selected_tracker_ids = params[:tracker_ids].collect { |id| id.to_i.to_s } if params[:tracker_ids] and params[:tracker_ids].is_a? Array
AND b.id = ( end
SELECT min( c.id ) @selected_tracker_ids ||= []
FROM issue_histories c @raw =
WHERE b.issue_id = c.issue_id ) ActiveRecord::Base.connection.select_all("SELECT datediff( a.created_on, b.created_on ) as delay, count(a.id) as total
GROUP BY delay") unless @selected_tracker_ids.empty? FROM issue_histories a, issue_histories b, issues i
@raw ||=[] WHERE a.status_id =5
AND a.issue_id = b.issue_id
@x_from = 0 AND a.issue_id = i.id
@x_to = 0 AND i.tracker_id in (#{@selected_tracker_ids.join(',')})
@y_from = 0 AND b.id = (
@y_to = 0 SELECT min( c.id )
@sum_total = 0 FROM issue_histories c
@sum_delay = 0 WHERE b.issue_id = c.issue_id )
@raw.each do |r| GROUP BY delay") unless @selected_tracker_ids.empty?
@x_to = [r['delay'].to_i, @x_to].max @raw ||=[]
@y_to = [r['total'].to_i, @y_to].max
@sum_total = @sum_total + r['total'].to_i @x_from = 0
@sum_delay = @sum_delay + r['total'].to_i * r['delay'].to_i @x_to = 0
end @y_from = 0
end @y_to = 0
@sum_total = 0
private @sum_delay = 0
# Find project of id params[:id] @raw.each do |r|
def find_project @x_to = [r['delay'].to_i, @x_to].max
@project = Project.find(params[:id]) @y_to = [r['total'].to_i, @y_to].max
rescue ActiveRecord::RecordNotFound @sum_total = @sum_total + r['total'].to_i
render_404 @sum_delay = @sum_delay + r['total'].to_i * r['delay'].to_i
end end
end
def issues_by_tracker
@issues_by_tracker ||= private
ActiveRecord::Base.connection.select_all("select s.id as status_id, # Find project of id params[:id]
s.is_closed as closed, def find_project
t.id as tracker_id, @project = Project.find(params[:id])
count(i.id) as total rescue ActiveRecord::RecordNotFound
from render_404
issues i, issue_statuses s, trackers t end
where
i.status_id=s.id def issues_by_tracker
and i.tracker_id=t.id @issues_by_tracker ||=
and i.project_id=#{@project.id} ActiveRecord::Base.connection.select_all("select s.id as status_id,
group by s.id, s.is_closed, t.id") s.is_closed as closed,
end t.id as tracker_id,
count(i.id) as total
def issues_by_priority from
@issues_by_priority ||= #{Issue.table_name} i, #{IssueStatus.table_name} s, #{Tracker.table_name} t
ActiveRecord::Base.connection.select_all("select s.id as status_id, where
s.is_closed as closed, i.status_id=s.id
p.id as priority_id, and i.tracker_id=t.id
count(i.id) as total and i.project_id=#{@project.id}
from group by s.id, s.is_closed, t.id")
issues i, issue_statuses s, enumerations p end
where
i.status_id=s.id def issues_by_priority
and i.priority_id=p.id @issues_by_priority ||=
and i.project_id=#{@project.id} ActiveRecord::Base.connection.select_all("select s.id as status_id,
group by s.id, s.is_closed, p.id") s.is_closed as closed,
end p.id as priority_id,
count(i.id) as total
def issues_by_category from
@issues_by_category ||= #{Issue.table_name} i, #{IssueStatus.table_name} s, #{Enumeration.table_name} p
ActiveRecord::Base.connection.select_all("select s.id as status_id, where
s.is_closed as closed, i.status_id=s.id
c.id as category_id, and i.priority_id=p.id
count(i.id) as total and i.project_id=#{@project.id}
from group by s.id, s.is_closed, p.id")
issues i, issue_statuses s, issue_categories c end
where
i.status_id=s.id def issues_by_category
and i.category_id=c.id @issues_by_category ||=
and i.project_id=#{@project.id} ActiveRecord::Base.connection.select_all("select s.id as status_id,
group by s.id, s.is_closed, c.id") s.is_closed as closed,
end c.id as category_id,
count(i.id) as total
def issues_by_author from
@issues_by_author ||= #{Issue.table_name} i, #{IssueStatus.table_name} s, #{IssueCategory.table_name} c
ActiveRecord::Base.connection.select_all("select s.id as status_id, where
s.is_closed as closed, i.status_id=s.id
a.id as author_id, and i.category_id=c.id
count(i.id) as total and i.project_id=#{@project.id}
from group by s.id, s.is_closed, c.id")
issues i, issue_statuses s, users a end
where
i.status_id=s.id def issues_by_author
and i.author_id=a.id @issues_by_author ||=
and i.project_id=#{@project.id} ActiveRecord::Base.connection.select_all("select s.id as status_id,
group by s.id, s.is_closed, a.id") s.is_closed as closed,
a.id as author_id,
count(i.id) as total
from
#{Issue.table_name} i, #{IssueStatus.table_name} s, #{User.table_name} a
where
i.status_id=s.id
and i.author_id=a.id
and i.project_id=#{@project.id}
group by s.id, s.is_closed, a.id")
end
def issues_by_subproject
@issues_by_subproject ||=
ActiveRecord::Base.connection.select_all("select s.id as status_id,
s.is_closed as closed,
i.project_id as project_id,
count(i.id) as total
from
#{Issue.table_name} i, #{IssueStatus.table_name} s
where
i.status_id=s.id
and i.project_id IN (#{@project.children.collect{|p| p.id}.join(',')})
group by s.id, s.is_closed, i.project_id") if @project.children.any?
@issues_by_subproject ||= []
end end
end end
+145 -11
View File
@@ -1,5 +1,5 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang # Copyright (C) 2006-2007 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
@@ -15,14 +15,26 @@
# 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 'SVG/Graph/Bar'
require 'SVG/Graph/BarHorizontal'
class RepositoriesController < ApplicationController class RepositoriesController < ApplicationController
layout 'base' layout 'base'
before_filter :find_project, :authorize before_filter :find_project
before_filter :authorize, :except => [:stats, :graph]
before_filter :check_project_privacy, :only => [:stats, :graph]
def show def show
# get entries for the browse frame
@entries = @repository.scm.entries('') @entries = @repository.scm.entries('')
show_error and return unless @entries show_error and return unless @entries
@latest_revision = @entries.revisions.latest # check if new revisions have been committed in the repository
scm_latestrev = @entries.revisions.latest
if Setting.autofetch_changesets? && scm_latestrev && ((@repository.latest_changeset.nil?) || (@repository.latest_changeset.revision < scm_latestrev.identifier.to_i))
@repository.fetch_changesets
@repository.reload
end
@changesets = @repository.changesets.find(:all, :limit => 5, :order => "committed_on DESC")
end end
def browse def browse
@@ -31,9 +43,20 @@ class RepositoriesController < ApplicationController
end end
def revisions def revisions
@entry = @repository.scm.entry(@path, @rev) unless @path == ''
@revisions = @repository.scm.revisions(@path, @rev) @entry = @repository.scm.entry(@path, @rev)
show_error and return unless @entry && @revisions show_error and return unless @entry
end
@repository.changesets_with_path @path do
@changeset_count = @repository.changesets.count
@changeset_pages = Paginator.new self, @changeset_count,
25,
params['page']
@changesets = @repository.changesets.find(:all,
:limit => @changeset_pages.items_per_page,
:offset => @changeset_pages.current.offset)
end
render :action => "revisions", :layout => false if request.xhr?
end end
def entry def entry
@@ -45,21 +68,41 @@ class RepositoriesController < ApplicationController
end end
def revision def revision
@revisions = @repository.scm.revisions '', @rev, @rev, :with_paths => true @changeset = @repository.changesets.find_by_revision(@rev)
show_error and return unless @revisions show_error and return unless @changeset
@revision = @revisions.first
end end
def diff def diff
@rev_to = params[:rev_to] || (@rev-1) @rev_to = params[:rev_to] || (@rev-1)
@diff = @repository.scm.diff(params[:path], @rev, @rev_to) type = params[:type] || 'inline'
@diff = @repository.scm.diff(params[:path], @rev, @rev_to, type)
show_error and return unless @diff show_error and return unless @diff
end end
def stats
end
def graph
data = nil
case params[:graph]
when "commits_per_month"
data = graph_commits_per_month(@repository)
when "commits_per_author"
data = graph_commits_per_author(@repository)
end
if data
headers["Content-Type"] = "image/svg+xml"
send_data(data, :type => "image/svg+xml", :disposition => "inline")
else
render_404
end
end
private private
def find_project def find_project
@project = Project.find(params[:id]) @project = Project.find(params[:id])
@repository = @project.repository @repository = @project.repository
render_404 and return false unless @repository
@path = params[:path].squeeze('/').gsub(/^\//, '') if params[:path] @path = params[:path].squeeze('/').gsub(/^\//, '') if params[:path]
@path ||= '' @path ||= ''
@rev = params[:rev].to_i if params[:rev] and params[:rev].to_i > 0 @rev = params[:rev].to_i if params[:rev] and params[:rev].to_i > 0
@@ -71,4 +114,95 @@ private
flash.now[:notice] = l(:notice_scm_error) flash.now[:notice] = l(:notice_scm_error)
render :nothing => true, :layout => true render :nothing => true, :layout => true
end end
def graph_commits_per_month(repository)
@date_to = Date.today
@date_from = @date_to << 12
commits_by_day = repository.changesets.count(:all, :group => :commit_date, :conditions => ["commit_date BETWEEN ? AND ?", @date_from, @date_to])
commits_by_month = [0] * 12
commits_by_day.each {|c| commits_by_month[c.first.to_date.months_ago] += c.last }
changes_by_day = repository.changes.count(:all, :group => :commit_date)
changes_by_month = [0] * 12
changes_by_day.each {|c| changes_by_month[c.first.to_date.months_ago] += c.last }
fields = []
month_names = l(:actionview_datehelper_select_month_names_abbr).split(',')
12.times {|m| fields << month_names[((Date.today.month - 1 - m) % 12)]}
graph = SVG::Graph::Bar.new(
:height => 300,
:width => 500,
:fields => fields.reverse,
:stack => :side,
:scale_integers => true,
:step_x_labels => 2,
:show_data_values => false,
:graph_title => l(:label_commits_per_month),
:show_graph_title => true
)
graph.add_data(
:data => commits_by_month[0..11].reverse,
:title => l(:label_revision_plural)
)
graph.add_data(
:data => changes_by_month[0..11].reverse,
:title => l(:label_change_plural)
)
graph.burn
end
def graph_commits_per_author(repository)
commits_by_author = repository.changesets.count(:all, :group => :committer)
commits_by_author.sort! {|x, y| x.last <=> y.last}
changes_by_author = repository.changes.count(:all, :group => :committer)
h = changes_by_author.inject({}) {|o, i| o[i.first] = i.last; o}
fields = commits_by_author.collect {|r| r.first}
commits_data = commits_by_author.collect {|r| r.last}
changes_data = commits_by_author.collect {|r| h[r.first] || 0}
fields = fields + [""]*(10 - fields.length) if fields.length<10
commits_data = commits_data + [0]*(10 - commits_data.length) if commits_data.length<10
changes_data = changes_data + [0]*(10 - changes_data.length) if changes_data.length<10
graph = SVG::Graph::BarHorizontal.new(
:height => 300,
:width => 500,
:fields => fields,
:stack => :side,
:scale_integers => true,
:show_data_values => false,
:rotate_y_labels => false,
:graph_title => l(:label_commits_per_author),
:show_graph_title => true
)
graph.add_data(
:data => commits_data,
:title => l(:label_revision_plural)
)
graph.add_data(
:data => changes_data,
:title => l(:label_change_plural)
)
graph.burn
end
end
class Date
def months_ago(date = Date.today)
(date.year - self.year)*12 + (date.month - self.month)
end
def weeks_ago(date = Date.today)
(date.year - self.year)*52 + (date.cweek - self.cweek)
end
end end
+81 -68
View File
@@ -1,26 +1,26 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang # Copyright (C) 2006 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# 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.
class RolesController < ApplicationController class RolesController < ApplicationController
layout 'base' layout 'base'
before_filter :require_admin before_filter :require_admin
verify :method => :post, :only => [ :destroy, :move ], verify :method => :post, :only => [ :destroy, :move ],
:redirect_to => { :action => :list } :redirect_to => { :action => :list }
def index def index
list list
@@ -28,26 +28,26 @@ class RolesController < ApplicationController
end end
def list def list
@role_pages, @roles = paginate :roles, :per_page => 10, :order => "position" @role_pages, @roles = paginate :roles, :per_page => 25, :order => "position"
render :action => "list", :layout => false if request.xhr? render :action => "list", :layout => false if request.xhr?
end end
def new def new
@role = Role.new(params[:role]) @role = Role.new(params[:role])
if request.post? if request.post?
@role.permissions = Permission.find(params[:permission_ids]) if params[:permission_ids] @role.permissions = Permission.find(params[:permission_ids]) if params[:permission_ids]
if @role.save if @role.save
flash[:notice] = l(:notice_successful_create) flash[:notice] = l(:notice_successful_create)
redirect_to :action => 'list' redirect_to :action => 'list'
end end
end end
@permissions = Permission.find(:all, :conditions => ["is_public=?", false], :order => 'sort ASC') @permissions = Permission.find(:all, :conditions => ["is_public=?", false], :order => 'sort ASC')
end end
def edit def edit
@role = Role.find(params[:id]) @role = Role.find(params[:id])
if request.post? and @role.update_attributes(params[:role]) if request.post? and @role.update_attributes(params[:role])
@role.permissions = Permission.find(params[:permission_ids] || []) @role.permissions = Permission.find(params[:permission_ids] || [])
Permission.allowed_to_role_expired Permission.allowed_to_role_expired
flash[:notice] = l(:notice_successful_update) flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'list' redirect_to :action => 'list'
@@ -56,47 +56,60 @@ class RolesController < ApplicationController
end end
def destroy def destroy
@role = Role.find(params[:id]) @role = Role.find(params[:id])
unless @role.members.empty? unless @role.members.empty?
flash[:notice] = 'Some members have this role. Can\'t delete it.' flash[:notice] = 'Some members have this role. Can\'t delete it.'
else else
@role.destroy @role.destroy
end end
redirect_to :action => 'list' redirect_to :action => 'list'
end end
def move def move
@role = Role.find(params[:id]) @role = Role.find(params[:id])
case params[:position] case params[:position]
when 'highest' when 'highest'
@role.move_to_top @role.move_to_top
when 'higher' when 'higher'
@role.move_higher @role.move_higher
when 'lower' when 'lower'
@role.move_lower @role.move_lower
when 'lowest' when 'lowest'
@role.move_to_bottom @role.move_to_bottom
end if params[:position] end if params[:position]
redirect_to :action => 'list' redirect_to :action => 'list'
end end
def workflow def workflow
@role = Role.find_by_id(params[:role_id]) @role = Role.find_by_id(params[:role_id])
@tracker = Tracker.find_by_id(params[:tracker_id]) @tracker = Tracker.find_by_id(params[:tracker_id])
if request.post? if request.post?
Workflow.destroy_all( ["role_id=? and tracker_id=?", @role.id, @tracker.id]) Workflow.destroy_all( ["role_id=? and tracker_id=?", @role.id, @tracker.id])
(params[:issue_status] || []).each { |old, news| (params[:issue_status] || []).each { |old, news|
news.each { |new| news.each { |new|
@role.workflows.build(:tracker_id => @tracker.id, :old_status_id => old, :new_status_id => new) @role.workflows.build(:tracker_id => @tracker.id, :old_status_id => old, :new_status_id => new)
} }
} }
if @role.save if @role.save
flash[:notice] = l(:notice_successful_update) flash[:notice] = l(:notice_successful_update)
end end
end end
@roles = Role.find(:all, :order => 'position') @roles = Role.find(:all, :order => '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, :include => :workflows, :order => 'position')
end
def report
@roles = Role.find(:all, :order => 'position')
@permissions = Permission.find :all, :conditions => ["is_public=?", false], :order => 'sort'
if request.post?
@roles.each do |role|
role.permissions = Permission.find(params[:permission_ids] ? (params[:permission_ids][role.id.to_s] || []) : [] )
end
Permission.allowed_to_role_expired
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'list'
end
end end
end end
+44
View File
@@ -0,0 +1,44 @@
# 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 SysController < ActionController::Base
wsdl_service_name 'Sys'
web_service_api SysApi
web_service_scaffold :invoke
before_invocation :check_enabled
def projects
Project.find(:all, :include => :repository)
end
def repository_created(project_id, url)
project = Project.find_by_id(project_id)
return 0 unless project && project.repository.nil?
logger.debug "Repository for #{project.name} created"
repository = Repository.new(:project => project, :url => url)
repository.root_url = url
repository.save
repository.id
end
protected
def check_enabled(name, args)
Setting.sys_api_enabled?
end
end
+80
View File
@@ -0,0 +1,80 @@
class TimelogController < ApplicationController
layout 'base'
before_filter :find_project
before_filter :authorize, :only => :edit
before_filter :check_project_privacy, :only => :details
helper :sort
include SortHelper
def details
sort_init 'spent_on', 'desc'
sort_update
@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 }
@owner_id = logged_in_user ? logged_in_user.id : 0
send_csv and return if 'csv' == params[:export]
render :action => 'details', :layout => false if request.xhr?
end
def edit
render_404 and return if @time_entry && @time_entry.user != logged_in_user
@time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :user => logged_in_user, :spent_on => Date.today)
@time_entry.attributes = params[:time_entry]
if request.post? and @time_entry.save
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'details', :project_id => @time_entry.project, :issue_id => @time_entry.issue
return
end
@activities = Enumeration::get_values('ACTI')
end
private
def find_project
if params[:id]
@time_entry = TimeEntry.find(params[:id])
@project = @time_entry.project
elsif params[:issue_id]
@issue = Issue.find(params[:issue_id])
@project = @issue.project
elsif params[:project_id]
@project = Project.find(params[:project_id])
else
render_404
return false
end
end
def send_csv
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_spent_on),
l(:field_user),
l(:field_activity),
l(:field_issue),
l(:field_hours),
l(:field_comment)
]
csv << headers.collect {|c| ic.iconv(c) }
# csv lines
@entries.each do |entry|
fields = [l_date(entry.spent_on),
entry.user.name,
entry.activity.name,
(entry.issue ? entry.issue.id : nil),
entry.hours,
entry.comment
]
csv << fields.collect {|c| ic.iconv(c.to_s) }
end
end
export.rewind
send_data(export.read, :type => 'text/csv; header=present', :filename => 'export.csv')
end
end
+48 -41
View File
@@ -1,23 +1,23 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang # Copyright (C) 2006 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# 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.
class TrackersController < ApplicationController class TrackersController < ApplicationController
layout 'base' layout 'base'
before_filter :require_admin before_filter :require_admin
def index def index
list list
@@ -28,16 +28,23 @@ class TrackersController < ApplicationController
verify :method => :post, :only => [ :destroy, :move ], :redirect_to => { :action => :list } verify :method => :post, :only => [ :destroy, :move ], :redirect_to => { :action => :list }
def list def list
@tracker_pages, @trackers = paginate :trackers, :per_page => 10, :order => 'position' @tracker_pages, @trackers = paginate :trackers, :per_page => 10, :order => 'position'
render :action => "list", :layout => false if request.xhr? render :action => "list", :layout => false if request.xhr?
end end
def new def new
@tracker = Tracker.new(params[:tracker]) @tracker = Tracker.new(params[:tracker])
if request.post? and @tracker.save if request.post? and @tracker.save
# workflow copy
if params[:copy_workflow_from] && (copy_from = Tracker.find_by_id(params[:copy_workflow_from]))
copy_from.workflows.each do |w|
@tracker.workflows << w.clone
end
end
flash[:notice] = l(:notice_successful_create) flash[:notice] = l(:notice_successful_create)
redirect_to :action => 'list' redirect_to :action => 'list'
end end
@trackers = Tracker.find :all
end end
def edit def edit
@@ -47,28 +54,28 @@ class TrackersController < ApplicationController
redirect_to :action => 'list' redirect_to :action => 'list'
end end
end end
def move def move
@tracker = Tracker.find(params[:id]) @tracker = Tracker.find(params[:id])
case params[:position] case params[:position]
when 'highest' when 'highest'
@tracker.move_to_top @tracker.move_to_top
when 'higher' when 'higher'
@tracker.move_higher @tracker.move_higher
when 'lower' when 'lower'
@tracker.move_lower @tracker.move_lower
when 'lowest' when 'lowest'
@tracker.move_to_bottom @tracker.move_to_bottom
end if params[:position] end if params[:position]
redirect_to :action => 'list' redirect_to :action => 'list'
end end
def destroy def destroy
@tracker = Tracker.find(params[:id]) @tracker = Tracker.find(params[:id])
unless @tracker.issues.empty? unless @tracker.issues.empty?
flash[:notice] = "This tracker contains issues and can\'t be deleted." flash[:notice] = "This tracker contains issues and can\'t be deleted."
else else
@tracker.destroy @tracker.destroy
end end
redirect_to :action => 'list' redirect_to :action => 'list'
end end
+90 -84
View File
@@ -1,113 +1,119 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang # Copyright (C) 2006-2007 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# 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.
class UsersController < ApplicationController class UsersController < ApplicationController
layout 'base' layout 'base'
before_filter :require_admin before_filter :require_admin
helper :sort helper :sort
include SortHelper include SortHelper
helper :custom_fields helper :custom_fields
include CustomFieldsHelper include CustomFieldsHelper
def index def index
list list
render :action => 'list' unless request.xhr? render :action => 'list' unless request.xhr?
end end
def list def list
sort_init 'login', 'asc' sort_init 'login', 'asc'
sort_update sort_update
@user_count = User.count
@user_pages = Paginator.new self, @user_count, @status = params[:status] ? params[:status].to_i : 1
15, conditions = nil
params['page'] conditions = ["status=?", @status] unless @status == 0
@users = User.find :all,:order => sort_clause,
:limit => @user_pages.items_per_page, @user_count = User.count(:conditions => conditions)
:offset => @user_pages.current.offset @user_pages = Paginator.new self, @user_count,
15,
params['page']
@users = User.find :all,:order => sort_clause,
:conditions => conditions,
:limit => @user_pages.items_per_page,
:offset => @user_pages.current.offset
render :action => "list", :layout => false if request.xhr? render :action => "list", :layout => false if request.xhr?
end end
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).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"][x.id.to_s]) } @custom_values = UserCustomField.find(:all).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
if @user.save if @user.save
flash[:notice] = l(:notice_successful_create) flash[:notice] = l(:notice_successful_create)
redirect_to :action => 'list' redirect_to :action => 'list'
end end
end end
@auth_sources = AuthSource.find(:all) @auth_sources = AuthSource.find(:all)
end end
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).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).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])
flash[:notice] = l(:notice_successful_update) flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'list' redirect_to :action => 'list'
end end
end end
@auth_sources = AuthSource.find(:all) @auth_sources = AuthSource.find(:all)
@roles = Role.find(:all, :order => 'position') @roles = Role.find(:all, :order => 'position')
@projects = Project.find(:all) - @user.projects @projects = Project.find(:all) - @user.projects
@membership ||= Member.new @membership ||= Member.new
end end
def edit_membership def edit_membership
@user = User.find(params[:id]) @user = User.find(params[:id])
@membership = params[:membership_id] ? Member.find(params[:membership_id]) : Member.new(:user => @user) @membership = params[:membership_id] ? Member.find(params[:membership_id]) : Member.new(:user => @user)
@membership.attributes = params[:membership] @membership.attributes = params[:membership]
if request.post? and @membership.save if request.post? and @membership.save
flash[:notice] = l(:notice_successful_update) flash[:notice] = l(:notice_successful_update)
end end
redirect_to :action => 'edit', :id => @user and return redirect_to :action => 'edit', :id => @user and return
end end
def destroy_membership def destroy_membership
@user = User.find(params[:id]) @user = User.find(params[:id])
if request.post? and Member.find(params[:membership_id]).destroy if request.post? and Member.find(params[:membership_id]).destroy
flash[:notice] = l(:notice_successful_update) flash[:notice] = l(:notice_successful_update)
end end
redirect_to :action => 'edit', :id => @user and return redirect_to :action => 'edit', :id => @user and return
end end
def destroy def destroy
User.find(params[:id]).destroy User.find(params[:id]).destroy
redirect_to :action => 'list'
rescue
flash[:notice] = "Unable to delete user"
redirect_to :action => 'list' redirect_to :action => 'list'
end rescue
flash[:notice] = "Unable to delete user"
redirect_to :action => 'list'
end
end end
+47 -47
View File
@@ -1,23 +1,23 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang # Copyright (C) 2006 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# 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.
class VersionsController < ApplicationController class VersionsController < ApplicationController
layout 'base' layout 'base'
before_filter :find_project, :authorize before_filter :find_project, :authorize
def edit def edit
if request.post? and @version.update_attributes(params[:version]) if request.post? and @version.update_attributes(params[:version])
@@ -26,33 +26,33 @@ class VersionsController < ApplicationController
end end
end end
def destroy def destroy
@version.destroy @version.destroy
redirect_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project redirect_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
rescue rescue
flash[:notice] = "Unable to delete version" flash[:notice] = "Unable to delete version"
redirect_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project redirect_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
end end
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
rescue rescue
render_404 render_404
end end
def destroy_file def destroy_file
@version.attachments.find(params[:attachment_id]).destroy @version.attachments.find(params[:attachment_id]).destroy
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
private private
def find_project def find_project
@version = Version.find(params[:id]) @version = Version.find(params[:id])
@project = @version.project @project = @version.project
rescue ActiveRecord::RecordNotFound rescue ActiveRecord::RecordNotFound
render_404 render_404
end end
end end
+23 -23
View File
@@ -1,25 +1,25 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang # Copyright (C) 2006 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# 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.
class WelcomeController < ApplicationController class WelcomeController < ApplicationController
layout 'base' layout 'base'
def index def index
@news = News.latest logged_in_user @news = News.latest logged_in_user
@projects = Project.latest logged_in_user @projects = Project.latest logged_in_user
end end
end end
+112
View File
@@ -0,0 +1,112 @@
# 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 WikiController < ApplicationController
layout 'base'
before_filter :find_wiki, :check_project_privacy, :except => [:preview]
# display a page (in editing mode if it doesn't exist)
def index
page_title = params[:page]
@page = @wiki.find_or_new_page(page_title)
if @page.new_record?
edit
render :action => 'edit' and return
end
@content = @page.content_for_version(params[:version])
if params[:export] == 'html'
export = render_to_string :action => 'export', :layout => false
send_data(export, :type => 'text/html', :filename => "#{@page.title}.html")
return
elsif params[:export] == 'txt'
send_data(@content.text, :type => 'text/plain', :filename => "#{@page.title}.txt")
return
end
render :action => 'show'
end
# edit an existing page or a new one
def edit
@page = @wiki.find_or_new_page(params[:page])
@page.content = WikiContent.new(:page => @page) if @page.new_record?
@content = @page.content_for_version(params[:version])
@content.text = "h1. #{@page.pretty_title}" if @content.text.blank?
# don't keep previous comment
@content.comment = nil
if request.post?
if @content.text == params[:content][:text]
# don't save if text wasn't changed
redirect_to :action => 'index', :id => @project, :page => @page.title
return
end
@content.text = params[:content][:text]
@content.comment = params[:content][:comment]
@content.author = logged_in_user
# 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)
redirect_to :action => 'index', :id => @project, :page => @page.title
end
end
end
# show page history
def history
@page = @wiki.find_page(params[:page])
# don't load text
@versions = @page.content.versions.find :all,
:select => "id, author_id, comment, updated_on, version",
:order => 'version DESC'
end
# display special pages
def special
page_title = params[:page].downcase
case page_title
# show pages index, sorted by title
when 'page_index'
# eager load information about last updates, without loading text
@pages = @wiki.pages.find :all, :select => "#{WikiPage.table_name}.*, #{WikiContent.table_name}.updated_on",
:joins => "LEFT JOIN #{WikiContent.table_name} ON #{WikiContent.table_name}.page_id = #{WikiPage.table_name}.id",
:order => 'title'
# export wiki to a single html file
when 'export'
@pages = @wiki.pages.find :all, :order => 'title'
export = render_to_string :action => 'export_multiple', :layout => false
send_data(export, :type => 'text/html', :filename => "wiki.html")
return
else
# requested special page doesn't exist, redirect to default page
redirect_to :action => 'index', :id => @project, :page => nil and return
end
render :action => "special_#{page_title}"
end
def preview
@text = params[:content][:text]
render :partial => 'preview'
end
private
def find_wiki
@project = Project.find(params[:id])
@wiki = @project.wiki
rescue ActiveRecord::RecordNotFound
render_404
end
end
+17 -17
View File
@@ -1,19 +1,19 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang # Copyright (C) 2006 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# 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.
module AccountHelper module AccountHelper
end end
+17 -17
View File
@@ -1,19 +1,19 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang # Copyright (C) 2006 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# 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.
module AdminHelper module AdminHelper
end end
+240 -192
View File
@@ -1,193 +1,241 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang # Copyright (C) 2006-2007 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# 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.
module ApplicationHelper module ApplicationHelper
# Return current logged in user or nil # Return current logged in user or nil
def loggedin? def loggedin?
@logged_in_user @logged_in_user
end end
# Return true if user is logged in and is admin, otherwise false # Return true if user is logged in and is admin, otherwise false
def admin_loggedin? def admin_loggedin?
@logged_in_user and @logged_in_user.admin? @logged_in_user and @logged_in_user.admin?
end end
# Return true if user is authorized for controller/action, otherwise false # Return true if user is authorized for controller/action, otherwise false
def authorize_for(controller, action) def authorize_for(controller, action)
# check if action is allowed on public projects # check if action is allowed on public projects
if @project.is_public? and Permission.allowed_to_public "%s/%s" % [ controller, action ] if @project.is_public? and Permission.allowed_to_public "%s/%s" % [ controller, action ]
return true return true
end end
# check if user is authorized # check if user is authorized
if @logged_in_user and (@logged_in_user.admin? or Permission.allowed_to_role( "%s/%s" % [ controller, action ], @logged_in_user.role_for_project(@project.id) ) ) if @logged_in_user and (@logged_in_user.admin? or Permission.allowed_to_role( "%s/%s" % [ controller, action ], @logged_in_user.role_for_project(@project) ) )
return true return true
end end
return false return false
end end
# Display a link if user is authorized # Display a link if user is authorized
def link_to_if_authorized(name, options = {}, html_options = nil, *parameters_for_method_reference) def link_to_if_authorized(name, options = {}, html_options = nil, *parameters_for_method_reference)
link_to(name, options, html_options, *parameters_for_method_reference) if authorize_for(options[:controller], options[:action]) link_to(name, options, html_options, *parameters_for_method_reference) if authorize_for(options[:controller], options[:action])
end end
# 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.display_name, :controller => 'account', :action => 'show', :id => user link_to user.display_name, :controller => 'account', :action => 'show', :id => user
end end
def image_to_function(name, function, html_options = {}) def link_to_issue(issue)
html_options.symbolize_keys! link_to "#{issue.tracker.name} ##{issue.id}", :controller => "issues", :action => "show", :id => issue
tag(:input, html_options.merge({ end
:type => "image", :src => image_path(name),
:onclick => (html_options[:onclick] ? "#{html_options[:onclick]}; " : "") + "#{function};" def toggle_link(name, id, options={})
})) onclick = "Element.toggle('#{id}'); "
end onclick << (options[:focus] ? "Form.Element.focus('#{options[:focus]}'); " : "this.blur(); ")
onclick << "return false;"
def format_date(date) link_to(name, "#", :onclick => onclick)
l_date(date) if date end
end
def image_to_function(name, function, html_options = {})
def format_time(time) html_options.symbolize_keys!
l_datetime(time) if time tag(:input, html_options.merge({
end :type => "image", :src => image_path(name),
:onclick => (html_options[:onclick] ? "#{html_options[:onclick]}; " : "") + "#{function};"
def day_name(day) }))
l(:general_day_names).split(',')[day-1] end
end
def format_date(date)
def month_name(month) l_date(date) if date
l(:actionview_datehelper_select_month_names).split(',')[month-1] end
end
def format_time(time)
def pagination_links_full(paginator, options={}, html_options={}) l_datetime((time.is_a? String) ? time.to_time : time) if time
html = '' end
html << link_to_remote(('&#171; ' + l(:label_previous)),
{:update => "content", :url => { :page => paginator.current.previous }}, def day_name(day)
{:href => url_for(:action => 'list', :params => params.merge({:page => paginator.current.previous}))}) + ' ' if paginator.current.previous l(:general_day_names).split(',')[day-1]
end
html << (pagination_links_each(paginator, options) do |n|
link_to_remote(n.to_s, def month_name(month)
{:url => {:action => 'list', :params => params.merge({:page => n})}, :update => 'content'}, l(:actionview_datehelper_select_month_names).split(',')[month-1]
{:href => url_for(:action => 'list', :params => params.merge({:page => n}))}) end
end || '')
def pagination_links_full(paginator, options={}, html_options={})
html << ' ' + link_to_remote((l(:label_next) + ' &#187;'), html = ''
{:update => "content", :url => { :page => paginator.current.next }}, html << link_to_remote(('&#171; ' + l(:label_previous)),
{:href => url_for(:action => 'list', :params => params.merge({:page => paginator.current.next}))}) if paginator.current.next {:update => "content", :url => options.merge(:page => paginator.current.previous)},
html {:href => url_for(:params => options.merge(:page => paginator.current.previous))}) + ' ' if paginator.current.previous
end
html << (pagination_links_each(paginator, options) do |n|
def textilizable(text) link_to_remote(n.to_s,
(Setting.text_formatting == 'textile') && (ActionView::Helpers::TextHelper.method_defined? "textilize") ? RedCloth.new(h(text)).to_html : simple_format(auto_link(h(text))) {:url => {:params => options.merge(:page => n)}, :update => 'content'},
end {:href => url_for(:params => options.merge(:page => n))})
end || '')
def error_messages_for(object_name, options = {})
options = options.symbolize_keys html << ' ' + link_to_remote((l(:label_next) + ' &#187;'),
object = instance_variable_get("@#{object_name}") {:update => "content", :url => options.merge(:page => paginator.current.next)},
if object && !object.errors.empty? {:href => url_for(:params => options.merge(:page => paginator.current.next))}) if paginator.current.next
# build full_messages here with controller current language html
full_messages = [] end
object.errors.each do |attr, msg|
next if msg.nil? # textilize text according to system settings and RedCloth availability
if attr == "base" def textilizable(text, options = {})
full_messages << l(msg) # different methods for formatting wiki links
else case options[:wiki_links]
full_messages << "&#171; " + (l_has_string?("field_" + attr) ? l("field_" + attr) : object.class.human_attribute_name(attr)) + " &#187; " + l(msg) unless attr == "custom_values" when :local
end # used for local links to html files
end format_wiki_link = Proc.new {|title| "#{title}.html" }
# retrieve custom values error messages when :anchor
if object.errors[:custom_values] # used for single-file wiki export
object.custom_values.each do |v| format_wiki_link = Proc.new {|title| "##{title}" }
v.errors.each do |attr, msg| else
next if msg.nil? if @project
full_messages << "&#171; " + v.custom_field.name + " &#187; " + l(msg) format_wiki_link = Proc.new {|title| url_for :controller => 'wiki', :action => 'index', :id => @project, :page => title }
end else
end format_wiki_link = Proc.new {|title| title }
end end
content_tag("div", end
content_tag(
options[:header_tag] || "h2", lwr(:gui_validation_error, full_messages.length) + " :" # turn wiki links into textile links:
) + # example:
content_tag("ul", full_messages.collect { |msg| content_tag("li", msg) }), # [[link]] -> "link":link
"id" => options[:id] || "errorExplanation", "class" => options[:class] || "errorExplanation" # [[link|title]] -> "title":link
) text = text.gsub(/\[\[([^\]\|]+)(\|([^\]\|]+))?\]\]/) {|m| "\"#{$3 || $1}\":" + format_wiki_link.call(Wiki.titleize($1)) }
else
"" # turn issue ids to textile links
end # example:
end # #52 -> "#52":/issues/show/52
text = text.gsub(/#(\d+)(?=\b)/) {|m| "\"##{$1}\":" + url_for(:controller => 'issues', :action => 'show', :id => $1) }
def lang_options_for_select(blank=true)
(blank ? [["(auto)", ""]] : []) + # turn revision ids to textile links (@project needed)
(GLoc.valid_languages.sort {|x,y| x.to_s <=> y.to_s }).collect {|lang| [ l_lang_name(lang.to_s, lang), lang.to_s]} # example:
end # r52 -> "r52":/repositories/revision/6?rev=52 (@project.id is 6)
text = text.gsub(/(?=\b)r(\d+)(?=\b)/) {|m| "\"r#{$1}\":" + url_for(:controller => 'repositories', :action => 'revision', :id => @project.id, :rev => $1) } if @project
def label_tag_for(name, option_tags = nil, options = {})
label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "") # finally textilize text
content_tag("label", label_text) @do_textilize ||= (Setting.text_formatting == 'textile') && (ActionView::Helpers::TextHelper.method_defined? "textilize")
end text = @do_textilize ? auto_link(RedCloth.new(text).to_html) : simple_format(auto_link(h(text)))
end
def labelled_tabular_form_for(name, object, options, &proc)
options[:html] ||= {} def error_messages_for(object_name, options = {})
options[:html].store :class, "tabular" options = options.symbolize_keys
form_for(name, object, options.merge({ :builder => TabularFormBuilder, :lang => current_language}), &proc) object = instance_variable_get("@#{object_name}")
end if object && !object.errors.empty?
# build full_messages here with controller current language
def check_all_links(form_name) full_messages = []
link_to_function(l(:button_check_all), "checkAll('#{form_name}', true)") + object.errors.each do |attr, msg|
" | " + next if msg.nil?
link_to_function(l(:button_uncheck_all), "checkAll('#{form_name}', false)") msg = msg.first if msg.is_a? Array
end if attr == "base"
full_messages << l(msg)
def calendar_for(field_id) else
image_tag("calendar.png", {:id => "#{field_id}_trigger",:class => "calendar-trigger"}) + full_messages << "&#171; " + (l_has_string?("field_" + attr) ? l("field_" + attr) : object.class.human_attribute_name(attr)) + " &#187; " + l(msg) unless attr == "custom_values"
javascript_tag("Calendar.setup({inputField : '#{field_id}', ifFormat : '%Y-%m-%d', button : '#{field_id}_trigger' });") end
end end
# retrieve custom values error messages
if object.errors[:custom_values]
object.custom_values.each do |v|
v.errors.each do |attr, msg|
next if msg.nil?
msg = msg.first if msg.is_a? Array
full_messages << "&#171; " + v.custom_field.name + " &#187; " + l(msg)
end
end
end
content_tag("div",
content_tag(
options[:header_tag] || "h2", lwr(:gui_validation_error, full_messages.length) + " :"
) +
content_tag("ul", full_messages.collect { |msg| content_tag("li", msg) }),
"id" => options[:id] || "errorExplanation", "class" => options[:class] || "errorExplanation"
)
else
""
end
end
def lang_options_for_select(blank=true)
(blank ? [["(auto)", ""]] : []) +
(GLoc.valid_languages.sort {|x,y| x.to_s <=> y.to_s }).collect {|lang| [ l_lang_name(lang.to_s, lang), lang.to_s]}
end
def label_tag_for(name, option_tags = nil, options = {})
label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "")
content_tag("label", label_text)
end
def labelled_tabular_form_for(name, object, options, &proc)
options[:html] ||= {}
options[:html].store :class, "tabular"
form_for(name, object, options.merge({ :builder => TabularFormBuilder, :lang => current_language}), &proc)
end
def check_all_links(form_name)
link_to_function(l(:button_check_all), "checkAll('#{form_name}', true)") +
" | " +
link_to_function(l(:button_uncheck_all), "checkAll('#{form_name}', false)")
end
def calendar_for(field_id)
image_tag("calendar.png", {:id => "#{field_id}_trigger",:class => "calendar-trigger"}) +
javascript_tag("Calendar.setup({inputField : '#{field_id}', ifFormat : '%Y-%m-%d', button : '#{field_id}_trigger' });")
end
end end
class TabularFormBuilder < ActionView::Helpers::FormBuilder class TabularFormBuilder < ActionView::Helpers::FormBuilder
include GLoc include GLoc
def initialize(object_name, object, template, options, proc) def initialize(object_name, object, template, options, proc)
set_language_if_valid options.delete(:lang) set_language_if_valid options.delete(:lang)
@object_name, @object, @template, @options, @proc = object_name, object, template, options, proc @object_name, @object, @template, @options, @proc = object_name, object, template, options, proc
end end
(field_helpers - %w(radio_button hidden_field) + %w(date_select)).each do |selector| (field_helpers - %w(radio_button hidden_field) + %w(date_select)).each do |selector|
src = <<-END_SRC src = <<-END_SRC
def #{selector}(field, options = {}) def #{selector}(field, options = {})
return super if options.delete :no_label return super if options.delete :no_label
label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "") label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "")
label = @template.content_tag("label", label_text, label = @template.content_tag("label", label_text,
:class => (@object && @object.errors[field] ? "error" : nil), :class => (@object && @object.errors[field] ? "error" : nil),
:for => (@object_name.to_s + "_" + field.to_s)) :for => (@object_name.to_s + "_" + field.to_s))
label + super label + super
end end
END_SRC END_SRC
class_eval src, __FILE__, __LINE__ class_eval src, __FILE__, __LINE__
end end
def select(field, choices, options = {}, html_options = {}) def select(field, choices, options = {}, html_options = {})
label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "") label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "")
label = @template.content_tag("label", label_text, label = @template.content_tag("label", label_text,
:class => (@object && @object.errors[field] ? "error" : nil), :class => (@object && @object.errors[field] ? "error" : nil),
:for => (@object_name.to_s + "_" + field.to_s)) :for => (@object_name.to_s + "_" + field.to_s))
label + super label + super
end end
end end
+75 -75
View File
@@ -1,77 +1,77 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang # Copyright (C) 2006 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# 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.
module CustomFieldsHelper module CustomFieldsHelper
# Return custom field html tag corresponding to its format # Return custom field html tag corresponding to its format
def custom_field_tag(custom_value) def custom_field_tag(custom_value)
custom_field = custom_value.custom_field custom_field = custom_value.custom_field
field_name = "custom_fields[#{custom_field.id}]" field_name = "custom_fields[#{custom_field.id}]"
field_id = "custom_fields_#{custom_field.id}" field_id = "custom_fields_#{custom_field.id}"
case custom_field.field_format case custom_field.field_format
when "string", "int" when "string", "int"
text_field 'custom_value', 'value', :name => field_name, :id => field_id text_field 'custom_value', 'value', :name => field_name, :id => field_id
when "date" when "date"
text_field('custom_value', 'value', :name => field_name, :id => field_id, :size => 10) + text_field('custom_value', 'value', :name => field_name, :id => field_id, :size => 10) +
calendar_for(field_id) calendar_for(field_id)
when "text" when "text"
text_area 'custom_value', 'value', :name => field_name, :id => field_id, :cols => 60, :rows => 3 text_area 'custom_value', 'value', :name => field_name, :id => field_id, :cols => 60, :rows => 3
when "bool" when "bool"
check_box 'custom_value', 'value', :name => field_name, :id => field_id check_box 'custom_value', 'value', :name => field_name, :id => field_id
when "list" when "list"
select 'custom_value', 'value', custom_field.possible_values, { :include_blank => true }, :name => field_name, :id => field_id select 'custom_value', 'value', custom_field.possible_values, { :include_blank => true }, :name => field_name, :id => field_id
end end
end end
# Return custom field label tag # Return custom field label tag
def custom_field_label_tag(custom_value) def custom_field_label_tag(custom_value)
content_tag "label", custom_value.custom_field.name + content_tag "label", custom_value.custom_field.name +
(custom_value.custom_field.is_required? ? " <span class=\"required\">*</span>" : ""), (custom_value.custom_field.is_required? ? " <span class=\"required\">*</span>" : ""),
:for => "custom_fields_#{custom_value.custom_field.id}", :for => "custom_fields_#{custom_value.custom_field.id}",
:class => (custom_value.errors.empty? ? nil : "error" ) :class => (custom_value.errors.empty? ? nil : "error" )
end end
# Return custom field tag with its label tag # Return custom field tag with its label tag
def custom_field_tag_with_label(custom_value) def custom_field_tag_with_label(custom_value)
custom_field_label_tag(custom_value) + custom_field_tag(custom_value) custom_field_label_tag(custom_value) + custom_field_tag(custom_value)
end end
# Return a string used to display a custom value # Return a string used to display a custom value
def show_value(custom_value) def show_value(custom_value)
return "" unless custom_value return "" unless custom_value
format_value(custom_value.value, custom_value.custom_field.field_format) format_value(custom_value.value, custom_value.custom_field.field_format)
end end
# Return a string used to display a custom value # Return a string used to display a custom value
def format_value(value, field_format) def format_value(value, field_format)
return "" unless value && !value.empty? return "" unless value && !value.empty?
case field_format case field_format
when "date" when "date"
begin; l_date(value.to_date); rescue; value end begin; l_date(value.to_date); rescue; value end
when "bool" when "bool"
l_YesNo(value == "1") l_YesNo(value == "1")
else else
value value
end end
end end
# Return an array of custom field formats which can be used in select_tag # Return an array of custom field formats which can be used in select_tag
def custom_field_formats_for_select def custom_field_formats_for_select
CustomField::FIELD_FORMATS.sort {|a,b| a[1][:order]<=>b[1][:order]}.collect { |k| [ l(k[1][:name]), k[0] ] } CustomField::FIELD_FORMATS.sort {|a,b| a[1][:order]<=>b[1][:order]}.collect { |k| [ l(k[1][:name]), k[0] ] }
end end
end end
+17 -17
View File
@@ -1,19 +1,19 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang # Copyright (C) 2006 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# 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.
module DocumentsHelper module DocumentsHelper
end end
+17 -17
View File
@@ -1,19 +1,19 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang # Copyright (C) 2006 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# 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.
module EnumerationsHelper module EnumerationsHelper
end end
+17 -17
View File
@@ -1,19 +1,19 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang # Copyright (C) 2006 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# 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.
module HelpHelper module HelpHelper
end end
+74 -52
View File
@@ -1,53 +1,75 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang # Copyright (C) 2006 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# 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 'iconv' require 'iconv'
require 'rfpdf/chinese'
module IfpdfHelper
module IfpdfHelper
class IFPDF < FPDF
class IFPDF < FPDF
attr_accessor :footer_date include GLoc
attr_accessor :footer_date
def initialize
super def initialize(lang)
SetCreator("redMine #{Redmine::VERSION}") super()
end set_language_if_valid lang
case current_language
def Cell(w,h=0,txt='',border=0,ln=0,align='',fill=0,link='') when :ja
@ic ||= Iconv.new('ISO-8859-1', 'UTF-8') extend(PDF_Japanese)
txt = begin AddSJISFont()
@ic.iconv(txt) @font_for_content = 'SJIS'
rescue @font_for_footer = 'SJIS'
txt when :zh
end extend(PDF_Chinese)
super w,h,txt,border,ln,align,fill,link AddBig5Font()
end @font_for_content = 'Big5'
@font_for_footer = 'Big5'
def Footer else
SetFont('Helvetica', 'I', 8) @font_for_content = 'Arial'
SetY(-15) @font_for_footer = 'Helvetica'
SetX(15) end
Cell(0, 5, @footer_date, 0, 0, 'L') SetCreator("redMine #{Redmine::VERSION}")
SetY(-15) SetFont(@font_for_content)
SetX(-30) end
Cell(0, 5, PageNo().to_s + '/{nb}', 0, 0, 'C')
end def SetFontStyle(style, size)
SetFont(@font_for_content, style, size)
end
def Cell(w,h=0,txt='',border=0,ln=0,align='',fill=0,link='')
@ic ||= Iconv.new(l(:general_pdf_encoding), 'UTF-8')
txt = begin
@ic.iconv(txt)
rescue
txt
end
super w,h,txt,border,ln,align,fill,link
end
def Footer
SetFont(@font_for_footer, 'I', 8)
SetY(-15)
SetX(15)
Cell(0, 5, @footer_date, 0, 0, 'L')
SetY(-15)
SetX(-30)
Cell(0, 5, PageNo().to_s + '/{nb}', 0, 0, 'C')
end
end end
end end
+17 -17
View File
@@ -1,19 +1,19 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang # Copyright (C) 2006 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# 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.
module IssueCategoriesHelper module IssueCategoriesHelper
end end
+17 -17
View File
@@ -1,19 +1,19 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang # Copyright (C) 2006 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# 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.
module IssueStatusesHelper module IssueStatusesHelper
end end
+84 -72
View File
@@ -1,74 +1,86 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang # Copyright (C) 2006 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# 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.
module IssuesHelper module IssuesHelper
def show_detail(detail, no_html=false) def show_detail(detail, no_html=false)
case detail.property case detail.property
when 'attr' when 'attr'
label = l(("field_" + detail.prop_key.to_s.gsub(/\_id$/, "")).to_sym) label = l(("field_" + detail.prop_key.to_s.gsub(/\_id$/, "")).to_sym)
case detail.prop_key case detail.prop_key
when 'due_date', 'start_date' when 'due_date', 'start_date'
value = format_date(detail.value.to_date) if detail.value value = format_date(detail.value.to_date) if detail.value
old_value = format_date(detail.old_value.to_date) if detail.old_value old_value = format_date(detail.old_value.to_date) if detail.old_value
when 'status_id' when 'status_id'
s = IssueStatus.find_by_id(detail.value) and value = s.name if detail.value s = IssueStatus.find_by_id(detail.value) and value = s.name if detail.value
s = IssueStatus.find_by_id(detail.old_value) and old_value = s.name if detail.old_value s = IssueStatus.find_by_id(detail.old_value) and old_value = s.name if detail.old_value
when 'assigned_to_id' when 'assigned_to_id'
u = User.find_by_id(detail.value) and value = u.name if detail.value u = User.find_by_id(detail.value) and value = u.name if detail.value
u = User.find_by_id(detail.old_value) and old_value = u.name if detail.old_value u = User.find_by_id(detail.old_value) and old_value = u.name if detail.old_value
when 'priority_id' when 'priority_id'
e = Enumeration.find_by_id(detail.value) and value = e.name if detail.value e = Enumeration.find_by_id(detail.value) and value = e.name if detail.value
e = Enumeration.find_by_id(detail.old_value) and old_value = e.name if detail.old_value e = Enumeration.find_by_id(detail.old_value) and old_value = e.name if detail.old_value
when 'category_id' when 'category_id'
c = IssueCategory.find_by_id(detail.value) and value = c.name if detail.value c = IssueCategory.find_by_id(detail.value) and value = c.name if detail.value
c = IssueCategory.find_by_id(detail.old_value) and old_value = c.name if detail.old_value c = IssueCategory.find_by_id(detail.old_value) and old_value = c.name if detail.old_value
when 'fixed_version_id' when 'fixed_version_id'
v = Version.find_by_id(detail.value) and value = v.name if detail.value v = Version.find_by_id(detail.value) and value = v.name if detail.value
v = Version.find_by_id(detail.old_value) and old_value = v.name if detail.old_value v = Version.find_by_id(detail.old_value) and old_value = v.name if detail.old_value
end end
when 'cf' when 'cf'
custom_field = CustomField.find_by_id(detail.prop_key) custom_field = CustomField.find_by_id(detail.prop_key)
if custom_field if custom_field
label = custom_field.name label = custom_field.name
value = format_value(detail.value, custom_field.field_format) if detail.value value = format_value(detail.value, custom_field.field_format) if detail.value
old_value = format_value(detail.old_value, custom_field.field_format) if detail.old_value old_value = format_value(detail.old_value, custom_field.field_format) if detail.old_value
end end
end when 'attachment'
label = l(:label_attachment)
label ||= detail.prop_key end
value ||= detail.value
old_value ||= detail.old_value label ||= detail.prop_key
value ||= detail.value
unless no_html old_value ||= detail.old_value
label = content_tag('strong', label)
old_value = content_tag("i", h(old_value)) if detail.old_value unless no_html
old_value = content_tag("strike", old_value) if detail.old_value and (!detail.value or detail.value.empty?) label = content_tag('strong', label)
value = content_tag("i", h(value)) if value old_value = content_tag("i", h(old_value)) if detail.old_value
end old_value = content_tag("strike", old_value) if detail.old_value and (!detail.value or detail.value.empty?)
value = content_tag("i", h(value)) if value
if detail.value and !detail.value.to_s.empty? end
if old_value
label + " " + l(:text_journal_changed, old_value, value) if detail.value and !detail.value.to_s.empty?
else case detail.property
label + " " + l(:text_journal_set_to, value) when 'attr', 'cf'
end if old_value
else label + " " + l(:text_journal_changed, old_value, value)
label + " " + l(:text_journal_deleted) + " (#{old_value})" else
end label + " " + l(:text_journal_set_to, value)
end
when 'attachment'
"#{label} #{value} #{l(:label_added)}"
end
else
case detail.property
when 'attr', 'cf'
label + " " + l(:text_journal_deleted) + " (#{old_value})"
when 'attachment'
"#{label} #{old_value} #{l(:label_deleted)}"
end
end
end end
end end
+17 -17
View File
@@ -1,19 +1,19 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang # Copyright (C) 2006 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# 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.
module MembersHelper module MembersHelper
end end
+17 -17
View File
@@ -1,19 +1,19 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang # Copyright (C) 2006 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# 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.
module NewsHelper module NewsHelper
end end
+27 -17
View File
@@ -1,19 +1,29 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang # Copyright (C) 2006 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# 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.
module ProjectsHelper module ProjectsHelper
def highlight_tokens(text, tokens)
return text unless tokens && !tokens.empty?
regexp = Regexp.new "(#{tokens.join('|')})", Regexp::IGNORECASE
result = ''
text.split(regexp).each_with_index do |words, i|
result << (i.even? ? (words.length > 100 ? "#{words[0..44]} ... #{words[-45..-1]}" : words) : content_tag('span', words, :class => 'highlight'))
end
result
end
end end
+30 -30
View File
@@ -1,32 +1,32 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang # Copyright (C) 2006 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# 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.
module ReportsHelper module ReportsHelper
def aggregate(data, criteria) def aggregate(data, criteria)
a = 0 a = 0
data.each { |row| data.each { |row|
match = 1 match = 1
criteria.each { |k, v| criteria.each { |k, v|
match = 0 unless row[k].to_s == v.to_s match = 0 unless row[k].to_s == v.to_s
} unless criteria.nil? } unless criteria.nil?
a = a + row["total"].to_i if match == 1 a = a + row["total"].to_i if match == 1
} unless data.nil? } unless data.nil?
a a
end end
end end
+17 -17
View File
@@ -1,19 +1,19 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang # Copyright (C) 2006 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# 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.
module RolesHelper module RolesHelper
end end
+5 -3
View File
@@ -107,10 +107,12 @@ module SortHelper
order = 'desc' # changed for desc order by default order = 'desc' # changed for desc order by default
end end
caption = titleize(Inflector::humanize(column)) unless caption caption = titleize(Inflector::humanize(column)) unless caption
params = {:params => {:sort_key => column, :sort_order => order}}
url = {:sort_key => column, :sort_order => order, :issue_id => params[:issue_id], :project_id => params[:project_id]}
link_to_remote(caption, link_to_remote(caption,
{:update => "content", :url => { :sort_key => column, :sort_order => order}}, {:update => "content", :url => url},
{:href => url_for(:params => { :sort_key => column, :sort_order => order})}) + {:href => url_for(url)}) +
(icon ? nbsp(2) + image_tag(icon) : '') (icon ? nbsp(2) + image_tag(icon) : '')
end end
+2
View File
@@ -0,0 +1,2 @@
module TimelogHelper
end
+17 -17
View File
@@ -1,19 +1,19 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang # Copyright (C) 2006 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# 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.
module TrackersHelper module TrackersHelper
end end
+23 -17
View File
@@ -1,19 +1,25 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang # Copyright (C) 2006 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# 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.
module UsersHelper module UsersHelper
def status_options_for_select(selected)
options_for_select([[l(:label_all), "*"],
[l(:status_active), 1],
[l(:status_registered), 2],
[l(:status_locked), 3]], selected)
end
end end
+17 -17
View File
@@ -1,19 +1,19 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang # Copyright (C) 2006 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# 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.
module VersionsHelper module VersionsHelper
end end
+17 -17
View File
@@ -1,19 +1,19 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang # Copyright (C) 2006 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# 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.
module WelcomeHelper module WelcomeHelper
end end
+19
View File
@@ -0,0 +1,19 @@
# 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.
module WikiHelper
end
+90 -90
View File
@@ -1,92 +1,92 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang # Copyright (C) 2006-2007 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# 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 "digest/md5" require "digest/md5"
class Attachment < ActiveRecord::Base 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
cattr_accessor :storage_path cattr_accessor :storage_path
@@storage_path = "#{RAILS_ROOT}/files" @@storage_path = "#{RAILS_ROOT}/files"
def validate def validate
errors.add_to_base :too_long if self.filesize > Setting.attachment_max_size.to_i.kilobytes errors.add_to_base :too_long if self.filesize > Setting.attachment_max_size.to_i.kilobytes
end end
def file=(incomming_file) def file=(incomming_file)
unless incomming_file.nil? unless incomming_file.nil?
@temp_file = incomming_file @temp_file = incomming_file
if @temp_file.size > 0 if @temp_file.size > 0
self.filename = sanitize_filename(@temp_file.original_filename) self.filename = sanitize_filename(@temp_file.original_filename)
self.disk_filename = DateTime.now.strftime("%y%m%d%H%M%S") + "_" + self.filename self.disk_filename = DateTime.now.strftime("%y%m%d%H%M%S") + "_" + self.filename
self.content_type = @temp_file.content_type self.content_type = @temp_file.content_type
self.filesize = @temp_file.size self.filesize = @temp_file.size
end end
end end
end end
def file def file
nil nil
end end
# Copy temp file to its final location # Copy temp file to its final location
def before_save def before_save
if @temp_file && (@temp_file.size > 0) if @temp_file && (@temp_file.size > 0)
logger.debug("saving '#{self.diskfile}'") logger.debug("saving '#{self.diskfile}'")
File.open(diskfile, "wb") do |f| File.open(diskfile, "wb") do |f|
f.write(@temp_file.read) f.write(@temp_file.read)
end end
self.digest = Digest::MD5.hexdigest(File.read(diskfile)) self.digest = Digest::MD5.hexdigest(File.read(diskfile))
end end
end end
# Deletes file on the disk # Deletes file on the disk
def after_destroy def after_destroy
if self.filename? if self.filename?
File.delete(diskfile) if File.exist?(diskfile) File.delete(diskfile) if File.exist?(diskfile)
end end
end end
# Returns file's location on disk # Returns file's location on disk
def diskfile def diskfile
"#{@@storage_path}/#{self.disk_filename}" "#{@@storage_path}/#{self.disk_filename}"
end end
def increment_download def increment_download
increment!(:downloads) increment!(:downloads)
end end
# returns last created projects # returns last created projects
def self.most_downloaded def self.most_downloaded
find(:all, :limit => 5, :order => "downloads DESC") find(:all, :limit => 5, :order => "downloads DESC")
end end
private private
def sanitize_filename(value) def sanitize_filename(value)
# get only the filename, not the whole path # get only the filename, not the whole path
just_filename = value.gsub(/^.*(\\|\/)/, '') just_filename = value.gsub(/^.*(\\|\/)/, '')
# NOTE: File.basename doesn't work right with Windows paths on Unix # NOTE: File.basename doesn't work right with Windows paths on Unix
# INCORRECT: just_filename = File.basename(value.gsub('\\\\', '/')) # INCORRECT: just_filename = File.basename(value.gsub('\\\\', '/'))
# Finally, replace all non alphanumeric, underscore or periods with underscore # Finally, replace all non alphanumeric, underscore or periods with underscore
@filename = just_filename.gsub(/[^\w\.\-]/,'_') @filename = just_filename.gsub(/[^\w\.\-]/,'_')
end end
end end
+77 -77
View File
@@ -1,79 +1,79 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang # Copyright (C) 2006 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# 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 'net/ldap' require 'net/ldap'
require 'iconv' require 'iconv'
class AuthSourceLdap < AuthSource class AuthSourceLdap < AuthSource
validates_presence_of :host, :port, :attr_login validates_presence_of :host, :port, :attr_login
def after_initialize def after_initialize
self.port = 389 if self.port == 0 self.port = 389 if self.port == 0
end end
def authenticate(login, password) def authenticate(login, password)
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)
login_filter = Net::LDAP::Filter.eq( self.attr_login, login ) login_filter = Net::LDAP::Filter.eq( self.attr_login, login )
object_filter = Net::LDAP::Filter.eq( "objectClass", "*" ) object_filter = Net::LDAP::Filter.eq( "objectClass", "*" )
dn = String.new dn = String.new
ldap_con.search( :base => self.base_dn, ldap_con.search( :base => self.base_dn,
:filter => object_filter & login_filter, :filter => object_filter & login_filter,
:attributes=> ['dn', self.attr_firstname, self.attr_lastname, self.attr_mail]) do |entry| :attributes=> ['dn', self.attr_firstname, self.attr_lastname, self.attr_mail]) do |entry|
dn = entry.dn dn = entry.dn
attrs = [:firstname => AuthSourceLdap.get_attr(entry, self.attr_firstname), attrs = [:firstname => AuthSourceLdap.get_attr(entry, self.attr_firstname),
:lastname => AuthSourceLdap.get_attr(entry, self.attr_lastname), :lastname => AuthSourceLdap.get_attr(entry, self.attr_lastname),
:mail => AuthSourceLdap.get_attr(entry, self.attr_mail), :mail => AuthSourceLdap.get_attr(entry, self.attr_mail),
:auth_source_id => self.id ] :auth_source_id => self.id ]
end end
return nil if dn.empty? return nil if dn.empty?
logger.debug "DN found for #{login}: #{dn}" if logger && logger.debug? logger.debug "DN found for #{login}: #{dn}" if logger && logger.debug?
# authenticate user # authenticate user
ldap_con = initialize_ldap_con(dn, password) ldap_con = initialize_ldap_con(dn, password)
return nil unless ldap_con.bind return nil unless ldap_con.bind
# return user's attributes # return user's attributes
logger.debug "Authentication successful for '#{login}'" if logger && logger.debug? logger.debug "Authentication successful for '#{login}'" if logger && logger.debug?
attrs attrs
rescue Net::LDAP::LdapError => text rescue Net::LDAP::LdapError => text
raise "LdapError: " + text raise "LdapError: " + text
end end
# test the connection to the LDAP # test the connection to the LDAP
def test_connection def test_connection
ldap_con = initialize_ldap_con(self.account, self.account_password) ldap_con = initialize_ldap_con(self.account, self.account_password)
ldap_con.open { } ldap_con.open { }
rescue Net::LDAP::LdapError => text rescue Net::LDAP::LdapError => text
raise "LdapError: " + text raise "LdapError: " + text
end end
def auth_method_name def auth_method_name
"LDAP" "LDAP"
end end
private private
def initialize_ldap_con(ldap_user, ldap_password) def initialize_ldap_con(ldap_user, ldap_password)
Net::LDAP.new( {:host => self.host, Net::LDAP.new( {:host => self.host,
:port => self.port, :port => self.port,
:auth => { :method => :simple, :username => Iconv.new('iso-8859-15', 'utf-8').iconv(ldap_user), :password => Iconv.new('iso-8859-15', 'utf-8').iconv(ldap_password) }} :auth => { :method => :simple, :username => Iconv.new('iso-8859-15', 'utf-8').iconv(ldap_user), :password => Iconv.new('iso-8859-15', 'utf-8').iconv(ldap_password) }}
) )
end end
def self.get_attr(entry, attr_name) def self.get_attr(entry, attr_name)
entry[attr_name].is_a?(Array) ? entry[attr_name].first : entry[attr_name] entry[attr_name].is_a?(Array) ? entry[attr_name].first : entry[attr_name]
end end
end end
+22
View File
@@ -0,0 +1,22 @@
# 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 Change < ActiveRecord::Base
belongs_to :changeset
validates_presence_of :changeset_id, :action, :path
end
+30
View File
@@ -0,0 +1,30 @@
# 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 Changeset < ActiveRecord::Base
belongs_to :repository
has_many :changes, :dependent => :delete_all
validates_presence_of :repository_id, :revision, :committed_on, :commit_date
validates_numericality_of :revision, :only_integer => true
validates_uniqueness_of :revision, :scope => :repository_id
def committed_on=(date)
self.commit_date = date
super
end
end
+59 -59
View File
@@ -1,60 +1,60 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang # Copyright (C) 2006 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# 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.
class CustomField < ActiveRecord::Base class CustomField < ActiveRecord::Base
has_many :custom_values, :dependent => :delete_all has_many :custom_values, :dependent => :delete_all
serialize :possible_values serialize :possible_values
FIELD_FORMATS = { "string" => { :name => :label_string, :order => 1 }, FIELD_FORMATS = { "string" => { :name => :label_string, :order => 1 },
"text" => { :name => :label_text, :order => 2 }, "text" => { :name => :label_text, :order => 2 },
"int" => { :name => :label_integer, :order => 3 }, "int" => { :name => :label_integer, :order => 3 },
"list" => { :name => :label_list, :order => 4 }, "list" => { :name => :label_list, :order => 4 },
"date" => { :name => :label_date, :order => 5 }, "date" => { :name => :label_date, :order => 5 },
"bool" => { :name => :label_boolean, :order => 6 } "bool" => { :name => :label_boolean, :order => 6 }
}.freeze }.freeze
validates_presence_of :name, :field_format validates_presence_of :name, :field_format
validates_uniqueness_of :name validates_uniqueness_of :name
validates_format_of :name, :with => /^[\w\s\'\-]*$/i validates_format_of :name, :with => /^[\w\s\'\-]*$/i
validates_inclusion_of :field_format, :in => FIELD_FORMATS.keys validates_inclusion_of :field_format, :in => FIELD_FORMATS.keys
def initialize(attributes = nil) def initialize(attributes = nil)
super super
self.possible_values ||= [] self.possible_values ||= []
end
def before_validation
# remove empty values
self.possible_values = self.possible_values.collect{|v| v unless v.empty?}.compact
end
def validate
if self.field_format == "list"
errors.add(:possible_values, :activerecord_error_blank) if self.possible_values.nil? || self.possible_values.empty?
errors.add(:possible_values, :activerecord_error_invalid) unless self.possible_values.is_a? Array
end
end
# to move in project_custom_field
def self.for_all
find(:all, :conditions => ["is_for_all=?", true])
end
def type_name
nil
end end
end
def before_validation
# remove empty values
self.possible_values = self.possible_values.collect{|v| v unless v.empty?}.compact
end
def validate
if self.field_format == "list"
errors.add(:possible_values, :activerecord_error_blank) if self.possible_values.nil? || self.possible_values.empty?
errors.add(:possible_values, :activerecord_error_invalid) unless self.possible_values.is_a? Array
end
end
# to move in project_custom_field
def self.for_all
find(:all, :conditions => ["is_for_all=?", true])
end
def type_name
nil
end
end
+37 -37
View File
@@ -1,38 +1,38 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang # Copyright (C) 2006 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# 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.
class CustomValue < ActiveRecord::Base class CustomValue < ActiveRecord::Base
belongs_to :custom_field belongs_to :custom_field
belongs_to :customized, :polymorphic => true belongs_to :customized, :polymorphic => true
protected protected
def validate def validate
errors.add(:value, :activerecord_error_blank) and return if custom_field.is_required? and value.empty? errors.add(:value, :activerecord_error_blank) and return if custom_field.is_required? and value.empty?
errors.add(:value, :activerecord_error_invalid) unless custom_field.regexp.empty? or value =~ Regexp.new(custom_field.regexp) errors.add(:value, :activerecord_error_invalid) unless custom_field.regexp.empty? or value =~ Regexp.new(custom_field.regexp)
errors.add(:value, :activerecord_error_too_short) if custom_field.min_length > 0 and value.length < custom_field.min_length and value.length > 0 errors.add(:value, :activerecord_error_too_short) if custom_field.min_length > 0 and value.length < custom_field.min_length and value.length > 0
errors.add(:value, :activerecord_error_too_long) if custom_field.max_length > 0 and value.length > custom_field.max_length errors.add(:value, :activerecord_error_too_long) if custom_field.max_length > 0 and value.length > custom_field.max_length
case custom_field.field_format case custom_field.field_format
when "int" when "int"
errors.add(:value, :activerecord_error_not_a_number) unless value =~ /^[0-9]*$/ errors.add(:value, :activerecord_error_not_a_number) unless value =~ /^[0-9]*$/
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.empty?
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.empty?
end end
end end
end end
+22 -22
View File
@@ -1,24 +1,24 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang # Copyright (C) 2006 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# 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.
class Document < ActiveRecord::Base class Document < ActiveRecord::Base
belongs_to :project belongs_to :project
belongs_to :category, :class_name => "Enumeration", :foreign_key => "category_id" belongs_to :category, :class_name => "Enumeration", :foreign_key => "category_id"
has_many :attachments, :as => :container, :dependent => :destroy has_many :attachments, :as => :container, :dependent => :destroy
validates_presence_of :project, :title, :category validates_presence_of :project, :title, :category
end end
+48 -45
View File
@@ -1,47 +1,50 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang # Copyright (C) 2006 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# 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.
class Enumeration < ActiveRecord::Base class Enumeration < ActiveRecord::Base
before_destroy :check_integrity before_destroy :check_integrity
validates_presence_of :opt, :name validates_presence_of :opt, :name
validates_uniqueness_of :name, :scope => [:opt] validates_uniqueness_of :name, :scope => [:opt]
validates_format_of :name, :with => /^[\w\s\'\-]*$/i validates_format_of :name, :with => /^[\w\s\'\-]*$/i
OPTIONS = { OPTIONS = {
"IPRI" => :enumeration_issue_priorities, "IPRI" => :enumeration_issue_priorities,
"DCAT" => :enumeration_doc_categories "DCAT" => :enumeration_doc_categories,
}.freeze "ACTI" => :enumeration_activities
}.freeze
def self.get_values(option)
find(:all, :conditions => ['opt=?', option]) def self.get_values(option)
end find(:all, :conditions => ['opt=?', option])
end
def option_name
OPTIONS[self.opt] def option_name
end OPTIONS[self.opt]
end
private
def check_integrity private
case self.opt def check_integrity
when "IPRI" case self.opt
raise "Can't delete enumeration" if Issue.find(:first, :conditions => ["priority_id=?", self.id]) when "IPRI"
when "DCAT" raise "Can't delete enumeration" if Issue.find(:first, :conditions => ["priority_id=?", self.id])
raise "Can't delete enumeration" if Document.find(:first, :conditions => ["category_id=?", self.id]) when "DCAT"
end raise "Can't delete enumeration" if Document.find(:first, :conditions => ["category_id=?", self.id])
when "ACTI"
raise "Can't delete enumeration" if TimeEntry.find(:first, :conditions => ["activity_id=?", self.id])
end
end end
end end
+105 -101
View File
@@ -1,102 +1,106 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang # Copyright (C) 2006 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# 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.
class Issue < ActiveRecord::Base class Issue < ActiveRecord::Base
belongs_to :project belongs_to :project
belongs_to :tracker belongs_to :tracker
belongs_to :status, :class_name => 'IssueStatus', :foreign_key => 'status_id' belongs_to :status, :class_name => 'IssueStatus', :foreign_key => 'status_id'
belongs_to :author, :class_name => 'User', :foreign_key => 'author_id' belongs_to :author, :class_name => 'User', :foreign_key => 'author_id'
belongs_to :assigned_to, :class_name => 'User', :foreign_key => 'assigned_to_id' belongs_to :assigned_to, :class_name => 'User', :foreign_key => 'assigned_to_id'
belongs_to :fixed_version, :class_name => 'Version', :foreign_key => 'fixed_version_id' belongs_to :fixed_version, :class_name => 'Version', :foreign_key => 'fixed_version_id'
belongs_to :priority, :class_name => 'Enumeration', :foreign_key => 'priority_id' belongs_to :priority, :class_name => 'Enumeration', :foreign_key => 'priority_id'
belongs_to :category, :class_name => 'IssueCategory', :foreign_key => 'category_id' belongs_to :category, :class_name => 'IssueCategory', :foreign_key => 'category_id'
has_many :journals, :as => :journalized, :dependent => :destroy has_many :journals, :as => :journalized, :dependent => :destroy
has_many :attachments, :as => :container, :dependent => :destroy has_many :attachments, :as => :container, :dependent => :destroy
has_many :time_entries
has_many :custom_values, :dependent => :delete_all, :as => :customized has_many :custom_values, :dependent => :delete_all, :as => :customized
has_many :custom_fields, :through => :custom_values has_many :custom_fields, :through => :custom_values
validates_presence_of :subject, :description, :priority, :tracker, :author, :status validates_presence_of :subject, :description, :priority, :tracker, :author, :status
validates_inclusion_of :done_ratio, :in => 0..100 validates_inclusion_of :done_ratio, :in => 0..100
validates_associated :custom_values, :on => :update validates_associated :custom_values, :on => :update
# set default status for new issues # set default status for new issues
def before_validation def before_validation
self.status = IssueStatus.default if new_record? self.status = IssueStatus.default if status.nil?
end end
def validate def validate
if self.due_date.nil? && @attributes['due_date'] && !@attributes['due_date'].empty? if self.due_date.nil? && @attributes['due_date'] && !@attributes['due_date'].empty?
errors.add :due_date, :activerecord_error_not_a_date errors.add :due_date, :activerecord_error_not_a_date
end end
if self.due_date and self.start_date and self.due_date < self.start_date if self.due_date and self.start_date and self.due_date < self.start_date
errors.add :due_date, :activerecord_error_greater_than_start_date errors.add :due_date, :activerecord_error_greater_than_start_date
end end
end end
#def before_create #def before_create
# build_history # build_history
#end #end
def before_save def before_save
if @current_journal if @current_journal
# attributes changes # attributes changes
(Issue.column_names - %w(id description)).each {|c| (Issue.column_names - %w(id description)).each {|c|
@current_journal.details << JournalDetail.new(:property => 'attr', @current_journal.details << JournalDetail.new(:property => 'attr',
:prop_key => c, :prop_key => c,
:old_value => @issue_before_change.send(c), :old_value => @issue_before_change.send(c),
:value => send(c)) unless send(c)==@issue_before_change.send(c) :value => send(c)) unless send(c)==@issue_before_change.send(c)
} }
# custom fields changes # custom fields changes
custom_values.each {|c| custom_values.each {|c|
@current_journal.details << JournalDetail.new(:property => 'cf', @current_journal.details << JournalDetail.new(:property => 'cf',
:prop_key => c.custom_field_id, :prop_key => c.custom_field_id,
:old_value => @custom_values_before_change[c.custom_field_id], :old_value => @custom_values_before_change[c.custom_field_id],
:value => c.value) unless @custom_values_before_change[c.custom_field_id]==c.value :value => c.value) unless @custom_values_before_change[c.custom_field_id]==c.value
} }
@current_journal.save unless @current_journal.details.empty? and @current_journal.notes.empty? @current_journal.save unless @current_journal.details.empty? and @current_journal.notes.empty?
end end
end end
def long_id def long_id
"%05d" % self.id "%05d" % self.id
end end
def custom_value_for(custom_field) def custom_value_for(custom_field)
self.custom_values.each {|v| return v if v.custom_field_id == custom_field.id } self.custom_values.each {|v| return v if v.custom_field_id == custom_field.id }
return nil return nil
end end
def init_journal(user, notes = "") def init_journal(user, notes = "")
@current_journal ||= Journal.new(:journalized => self, :user => user, :notes => notes) @current_journal ||= Journal.new(:journalized => self, :user => user, :notes => notes)
@issue_before_change = self.clone @issue_before_change = self.clone
@custom_values_before_change = {} @custom_values_before_change = {}
self.custom_values.each {|c| @custom_values_before_change.store c.custom_field_id, c.value } self.custom_values.each {|c| @custom_values_before_change.store c.custom_field_id, c.value }
@current_journal @current_journal
end end
private def spent_hours
# Creates an history for the issue @spent_hours ||= time_entries.sum(:hours) || 0
#def build_history end
# @history = self.histories.build
# @history.status = self.status private
# @history.author = self.author # Creates an history for the issue
#end #def build_history
# @history = self.histories.build
# @history.status = self.status
# @history.author = self.author
#end
end end
+27 -27
View File
@@ -1,29 +1,29 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang # Copyright (C) 2006 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# 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.
class IssueCategory < ActiveRecord::Base class IssueCategory < ActiveRecord::Base
before_destroy :check_integrity before_destroy :check_integrity
belongs_to :project belongs_to :project
validates_presence_of :name validates_presence_of :name
validates_uniqueness_of :name, :scope => [:project_id] validates_uniqueness_of :name, :scope => [:project_id]
private private
def check_integrity def check_integrity
raise "Can't delete category" if Issue.find(:first, :conditions => ["category_id=?", self.id]) raise "Can't delete category" if Issue.find(:first, :conditions => ["category_id=?", self.id])
end end
end end
+26 -26
View File
@@ -1,27 +1,27 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang # Copyright (C) 2006 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# 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.
class IssueCustomField < CustomField class IssueCustomField < CustomField
has_and_belongs_to_many :projects, :join_table => "custom_fields_projects", :foreign_key => "custom_field_id" has_and_belongs_to_many :projects, :join_table => "#{table_name_prefix}custom_fields_projects#{table_name_suffix}", :foreign_key => "custom_field_id"
has_and_belongs_to_many :trackers, :join_table => "custom_fields_trackers", :foreign_key => "custom_field_id" has_and_belongs_to_many :trackers, :join_table => "#{table_name_prefix}custom_fields_trackers#{table_name_suffix}", :foreign_key => "custom_field_id"
has_many :issues, :through => :issue_custom_values has_many :issues, :through => :issue_custom_values
def type_name def type_name
:label_issue_plural :label_issue_plural
end end
end end
+22 -22
View File
@@ -1,24 +1,24 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang # Copyright (C) 2006 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# 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.
class IssueHistory < ActiveRecord::Base class IssueHistory < ActiveRecord::Base
belongs_to :status, :class_name => 'IssueStatus', :foreign_key => 'status_id' belongs_to :status, :class_name => 'IssueStatus', :foreign_key => 'status_id'
belongs_to :author, :class_name => 'User', :foreign_key => 'author_id' belongs_to :author, :class_name => 'User', :foreign_key => 'author_id'
belongs_to :issue belongs_to :issue
validates_presence_of :status validates_presence_of :status
end end
+58 -49
View File
@@ -1,51 +1,60 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang # Copyright (C) 2006 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# 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.
class IssueStatus < ActiveRecord::Base class IssueStatus < ActiveRecord::Base
before_destroy :check_integrity before_destroy :check_integrity
has_many :workflows, :foreign_key => "old_status_id" has_many :workflows, :foreign_key => "old_status_id"
acts_as_list acts_as_list
validates_presence_of :name validates_presence_of :name
validates_uniqueness_of :name validates_uniqueness_of :name
validates_format_of :name, :with => /^[\w\s\'\-]*$/i validates_format_of :name, :with => /^[\w\s\'\-]*$/i
validates_length_of :html_color, :is => 6 validates_length_of :html_color, :is => 6
validates_format_of :html_color, :with => /^[a-f0-9]*$/i validates_format_of :html_color, :with => /^[a-f0-9]*$/i
def before_save def before_save
IssueStatus.update_all "is_default=#{connection.quoted_false}" if self.is_default? IssueStatus.update_all "is_default=#{connection.quoted_false}" if self.is_default?
end end
# Returns the default status for new issues # Returns the default status for new issues
def self.default def self.default
find(:first, :conditions =>["is_default=?", true]) find(:first, :conditions =>["is_default=?", true])
end end
# Returns an array of all statuses the given role can switch to # Returns an array of all statuses the given role can switch to
def new_statuses_allowed_to(role, tracker) # Uses association cache when called more than one time
statuses = [] def new_statuses_allowed_to(role, tracker)
for workflow in self.workflows new_statuses = [self]
statuses << workflow.new_status if workflow.role_id == role.id and workflow.tracker_id == tracker.id new_statuses += workflows.select {|w| w.role_id == role.id && w.tracker_id == tracker.id}.collect{|w| w.new_status} if role && tracker
end unless role.nil? or tracker.nil? new_statuses.sort{|x, y| x.position <=> y.position }
statuses end
end
# Same thing as above but uses a database query
private # More efficient than the previous method if called just once
def check_integrity def find_new_statuses_allowed_to(role, tracker)
raise "Can't delete status" if Issue.find(:first, :conditions => ["status_id=?", self.id]) new_statuses = [self]
new_statuses += workflows.find(:all,
:include => :new_status,
:conditions => ["role_id=? and tracker_id=?", role.id, tracker.id]).collect{ |w| w.new_status } if role && tracker
new_statuses.sort{|x, y| x.position <=> y.position }
end
private
def check_integrity
raise "Can't delete status" if Issue.find(:first, :conditions => ["status_id=?", self.id])
end end
end end
+4
View File
@@ -17,6 +17,10 @@
class Journal < ActiveRecord::Base class Journal < ActiveRecord::Base
belongs_to :journalized, :polymorphic => true belongs_to :journalized, :polymorphic => true
# added as a quick fix to allow eager loading of the polymorphic association
# since always associated to an issue, for now
belongs_to :issue, :foreign_key => :journalized_id
belongs_to :user belongs_to :user
has_many :details, :class_name => "JournalDetail", :dependent => :delete_all has_many :details, :class_name => "JournalDetail", :dependent => :delete_all
end end
+79 -79
View File
@@ -1,88 +1,88 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang # Copyright (C) 2006-2007 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# 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.
class Mailer < ActionMailer::Base class Mailer < ActionMailer::Base
helper IssuesHelper helper IssuesHelper
def issue_add(issue) def issue_add(issue)
set_language_if_valid(Setting.default_language) set_language_if_valid(Setting.default_language)
# Sends to all project members # Sends to all project members
@recipients = issue.project.members.collect { |m| m.user.mail if m.user.mail_notification }.compact @recipients = issue.project.members.collect { |m| m.user.mail if m.user.mail_notification }.compact
@from = Setting.mail_from @from = Setting.mail_from
@subject = "[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}] #{issue.status.name} - #{issue.subject}" @subject = "[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}] #{issue.status.name} - #{issue.subject}"
@body['issue'] = issue @body['issue'] = issue
end end
def issue_edit(journal) def issue_edit(journal)
set_language_if_valid(Setting.default_language) set_language_if_valid(Setting.default_language)
# Sends to all project members # Sends to all project members
issue = journal.journalized issue = journal.journalized
@recipients = issue.project.members.collect { |m| m.user.mail if m.user.mail_notification }.compact @recipients = issue.project.members.collect { |m| m.user.mail if m.user.mail_notification }.compact
@from = Setting.mail_from @from = Setting.mail_from
@subject = "[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}] #{issue.status.name} - #{issue.subject}" @subject = "[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}] #{issue.status.name} - #{issue.subject}"
@body['issue'] = issue @body['issue'] = issue
@body['journal']= journal @body['journal']= journal
end end
def document_add(document) def document_add(document)
set_language_if_valid(Setting.default_language) set_language_if_valid(Setting.default_language)
@recipients = document.project.users.collect { |u| u.mail if u.mail_notification }.compact @recipients = document.project.users.collect { |u| u.mail if u.mail_notification }.compact
@from = Setting.mail_from @from = Setting.mail_from
@subject = "[#{document.project.name}] #{l(:label_document_new)}: #{document.title}" @subject = "[#{document.project.name}] #{l(:label_document_new)}: #{document.title}"
@body['document'] = document @body['document'] = document
end end
def attachments_add(attachments) def attachments_add(attachments)
set_language_if_valid(Setting.default_language) set_language_if_valid(Setting.default_language)
container = attachments.first.container container = attachments.first.container
url = "http://#{Setting.host_name}/" url = "http://#{Setting.host_name}/"
added_to = "" added_to = ""
case container.class.to_s case container.class.to_s
when 'Version' when 'Version'
url << "projects/list_files/#{container.project_id}" url << "projects/list_files/#{container.project_id}"
added_to = "#{l(:label_version)}: #{container.name}" added_to = "#{l(:label_version)}: #{container.name}"
when 'Document' when 'Document'
url << "documents/show/#{container.id}" url << "documents/show/#{container.id}"
added_to = "#{l(:label_document)}: #{container.title}" added_to = "#{l(:label_document)}: #{container.title}"
when 'Issue' when 'Issue'
url << "issues/show/#{container.id}" url << "issues/show/#{container.id}"
added_to = "#{container.tracker.name} ##{container.id}: #{container.subject}" added_to = "#{container.tracker.name} ##{container.id}: #{container.subject}"
end end
@recipients = container.project.users.collect { |u| u.mail if u.mail_notification }.compact @recipients = container.project.users.collect { |u| u.mail if u.mail_notification }.compact
@from = Setting.mail_from @from = Setting.mail_from
@subject = "[#{container.project.name}] #{l(:label_attachment_new)}" @subject = "[#{container.project.name}] #{l(:label_attachment_new)}"
@body['attachments'] = attachments @body['attachments'] = attachments
@body['url'] = url @body['url'] = url
@body['added_to'] = added_to @body['added_to'] = added_to
end end
def lost_password(token) def lost_password(token)
set_language_if_valid(token.user.language) set_language_if_valid(token.user.language)
@recipients = token.user.mail @recipients = token.user.mail
@from = Setting.mail_from @from = Setting.mail_from
@subject = l(:mail_subject_lost_password) @subject = l(:mail_subject_lost_password)
@body['token'] = token @body['token'] = token
end end
def register(token) def register(token)
set_language_if_valid(token.user.language) set_language_if_valid(token.user.language)
@recipients = token.user.mail @recipients = token.user.mail
@from = Setting.mail_from @from = Setting.mail_from
@subject = l(:mail_subject_register) @subject = l(:mail_subject_register)
@body['token'] = token @body['token'] = token
end end
end end
+27 -27
View File
@@ -1,29 +1,29 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang # Copyright (C) 2006 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# 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.
class Member < ActiveRecord::Base class Member < ActiveRecord::Base
belongs_to :user belongs_to :user
belongs_to :role belongs_to :role
belongs_to :project belongs_to :project
validates_presence_of :role, :user, :project validates_presence_of :role, :user, :project
validates_uniqueness_of :user_id, :scope => :project_id validates_uniqueness_of :user_id, :scope => :project_id
def name def name
self.user.display_name self.user.display_name
end end
end end
+27 -27
View File
@@ -1,29 +1,29 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang # Copyright (C) 2006 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# 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.
class News < ActiveRecord::Base class News < ActiveRecord::Base
belongs_to :project belongs_to :project
belongs_to :author, :class_name => 'User', :foreign_key => 'author_id' belongs_to :author, :class_name => 'User', :foreign_key => 'author_id'
has_many :comments, :as => :commented, :dependent => :delete_all, :order => "created_on" has_many :comments, :as => :commented, :dependent => :delete_all, :order => "created_on"
validates_presence_of :title, :description validates_presence_of :title, :description
# returns latest news for projects visible by user # returns latest news for projects visible by user
def self.latest(user=nil, count=5) def self.latest(user=nil, count=5)
find(:all, :limit => count, :conditions => Project.visible_by(user), :include => [ :author, :project ], :order => "news.created_on DESC") find(:all, :limit => count, :conditions => Project.visible_by(user), :include => [ :author, :project ], :order => "#{News.table_name}.created_on DESC")
end end
end end
+64 -63
View File
@@ -1,65 +1,66 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang # Copyright (C) 2006 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# 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.
class Permission < ActiveRecord::Base class Permission < ActiveRecord::Base
has_and_belongs_to_many :roles has_and_belongs_to_many :roles
validates_presence_of :controller, :action, :description validates_presence_of :controller, :action, :description
GROUPS = { GROUPS = {
100 => :label_project, 100 => :label_project,
200 => :label_member_plural, 200 => :label_member_plural,
300 => :label_version_plural, 300 => :label_version_plural,
400 => :label_issue_category_plural, 400 => :label_issue_category_plural,
600 => :label_query_plural, 600 => :label_query_plural,
1000 => :label_issue_plural, 1000 => :label_issue_plural,
1100 => :label_news_plural, 1100 => :label_news_plural,
1200 => :label_document_plural, 1200 => :label_document_plural,
1300 => :label_attachment_plural, 1300 => :label_attachment_plural,
1400 => :label_repository 1400 => :label_repository,
}.freeze 1500 => :label_time_tracking
}.freeze
@@cached_perms_for_public = nil
@@cached_perms_for_roles = nil @@cached_perms_for_public = nil
@@cached_perms_for_roles = nil
def name
self.controller + "/" + self.action def name
end self.controller + "/" + self.action
end
def group_id
(self.sort / 100)*100 def group_id
end (self.sort / 100)*100
end
def self.allowed_to_public(action)
@@cached_perms_for_public ||= find(:all, :conditions => ["is_public=?", true]).collect {|p| "#{p.controller}/#{p.action}"} def self.allowed_to_public(action)
@@cached_perms_for_public.include? action @@cached_perms_for_public ||= find(:all, :conditions => ["is_public=?", true]).collect {|p| "#{p.controller}/#{p.action}"}
end @@cached_perms_for_public.include? action
end
def self.allowed_to_role(action, role)
@@cached_perms_for_roles ||= def self.allowed_to_role(action, role)
begin @@cached_perms_for_roles ||=
perms = {} begin
find(:all, :include => :roles).each {|p| perms.store "#{p.controller}/#{p.action}", p.roles.collect {|r| r.id } } perms = {}
perms find(:all, :include => :roles).each {|p| perms.store "#{p.controller}/#{p.action}", p.roles.collect {|r| r.id } }
end perms
allowed_to_public(action) or (@@cached_perms_for_roles[action] and @@cached_perms_for_roles[action].include? role) end
end allowed_to_public(action) or (role && @@cached_perms_for_roles[action] && @@cached_perms_for_roles[action].include?(role.id))
end
def self.allowed_to_role_expired
@@cached_perms_for_roles = nil def self.allowed_to_role_expired
@@cached_perms_for_roles = nil
end end
end end
+92 -68
View File
@@ -1,70 +1,94 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang # Copyright (C) 2006 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# 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.
class Project < ActiveRecord::Base class Project < ActiveRecord::Base
has_many :versions, :dependent => :destroy, :order => "versions.effective_date DESC, versions.name DESC" has_many :versions, :dependent => :destroy, :order => "#{Version.table_name}.effective_date DESC, #{Version.table_name}.name DESC"
has_many :members, :dependent => :delete_all, :include => :user, :conditions => "users.status=#{User::STATUS_ACTIVE}" has_many :members, :dependent => :delete_all, :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 :issues, :dependent => :destroy, :order => "issues.created_on DESC", :include => [:status, :tracker] has_many :issues, :dependent => :destroy, :order => "#{Issue.table_name}.created_on DESC", :include => [:status, :tracker]
has_many :queries, :dependent => :delete_all has_many :time_entries, :dependent => :delete_all
has_many :documents, :dependent => :destroy has_many :queries, :dependent => :delete_all
has_many :news, :dependent => :delete_all, :include => :author has_many :documents, :dependent => :destroy
has_many :issue_categories, :dependent => :delete_all, :order => "issue_categories.name" has_many :news, :dependent => :delete_all, :include => :author
has_one :repository, :dependent => :destroy has_many :issue_categories, :dependent => :delete_all, :order => "#{IssueCategory.table_name}.name"
has_and_belongs_to_many :custom_fields, :class_name => 'IssueCustomField', :join_table => 'custom_fields_projects', :association_foreign_key => 'custom_field_id' has_one :repository, :dependent => :destroy
acts_as_tree :order => "name", :counter_cache => true 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'
validates_presence_of :name, :description acts_as_tree :order => "name", :counter_cache => true
validates_uniqueness_of :name
validates_associated :custom_values, :on => :update validates_presence_of :name, :description, :identifier
validates_associated :repository validates_uniqueness_of :name, :identifier
validates_format_of :name, :with => /^[\w\s\'\-]*$/i validates_associated :custom_values, :on => :update
validates_associated :repository, :wiki
# returns latest created projects validates_length_of :name, :maximum => 30
# non public projects will be returned only if user is a member of those validates_format_of :name, :with => /^[\w\s\'\-]*$/i
def self.latest(user=nil, count=5) validates_length_of :description, :maximum => 255
find(:all, :limit => count, :conditions => visible_by(user), :order => "projects.created_on DESC") validates_length_of :identifier, :in => 3..12
end validates_format_of :identifier, :with => /^[a-z0-9\-]*$/
def self.visible_by(user=nil) def identifier=(identifier)
if user && !user.memberships.empty? super unless identifier_frozen?
return ["projects.is_public = ? or projects.id IN (#{user.memberships.collect{|m| m.project_id}.join(',')})", true] end
else
return ["projects.is_public = ?", true] def identifier_frozen?
end errors[:identifier].nil? && !(new_record? || identifier.blank?)
end end
# Returns an array of all custom fields enabled for project issues def issues_with_subprojects(include_subprojects=false)
# (explictly associated custom fields and custom fields enabled for all projects) conditions = nil
def custom_fields_for_issues(tracker) if include_subprojects && children.size > 0
tracker.custom_fields.find(:all, :include => :projects, ids = [id] + children.collect {|c| c.id}
:conditions => ["is_for_all=? or project_id=?", true, self.id]) conditions = ["#{Issue.table_name}.project_id IN (#{ids.join(',')})"]
#(CustomField.for_all + custom_fields).uniq else
end conditions = ["#{Issue.table_name}.project_id = ?", id]
end
def all_custom_fields Issue.with_scope :find => { :conditions => conditions } do
@all_custom_fields ||= IssueCustomField.find(:all, :include => :projects, yield
:conditions => ["is_for_all=? or project_id=?", true, self.id]) end
end end
protected # returns latest created projects
def validate # non public projects will be returned only if user is a member of those
errors.add(parent_id, " must be a root project") if parent and parent.parent def self.latest(user=nil, count=5)
errors.add_to_base("A project with subprojects can't be a subproject") if parent and children.size > 0 find(:all, :limit => count, :conditions => visible_by(user), :order => "created_on DESC")
end
def self.visible_by(user=nil)
if user && !user.memberships.empty?
return ["#{Project.table_name}.is_public = ? or #{Project.table_name}.id IN (#{user.memberships.collect{|m| m.project_id}.join(',')})", true]
else
return ["#{Project.table_name}.is_public = ?", true]
end
end
# Returns an array of all custom fields enabled for project issues
# (explictly associated custom fields and custom fields enabled for all projects)
def custom_fields_for_issues(tracker)
all_custom_fields.select {|c| tracker.custom_fields.include? c }
end
def all_custom_fields
@all_custom_fields ||= (IssueCustomField.for_all + custom_fields).uniq
end
protected
def validate
errors.add(parent_id, " must be a root project") if parent and parent.parent
errors.add_to_base("A project with subprojects can't be a subproject") if parent and children.size > 0
end end
end end
+22 -22
View File
@@ -1,22 +1,22 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang # Copyright (C) 2006 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# 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.
class ProjectCustomField < CustomField class ProjectCustomField < CustomField
def type_name def type_name
:label_project_plural :label_project_plural
end end
end end
+32 -17
View File
@@ -45,6 +45,7 @@ class Query < ActiveRecord::Base
@@operators_by_filter_type = { :list => [ "=", "!" ], @@operators_by_filter_type = { :list => [ "=", "!" ],
:list_status => [ "o", "=", "!", "c", "*" ], :list_status => [ "o", "=", "!", "c", "*" ],
:list_optional => [ "=", "!", "!*", "*" ], :list_optional => [ "=", "!", "!*", "*" ],
:list_one_or_more => [ "*", "=" ],
:date => [ "<t+", ">t+", "t+", "t", ">t-", "<t-", "t-" ], :date => [ "<t+", ">t+", "t+", "t", ">t-", "<t-", "t-" ],
:date_past => [ ">t-", "<t-", "t-", "t" ], :date_past => [ ">t-", "<t-", "t-", "t" ],
:text => [ "~", "!~" ] } :text => [ "~", "!~" ] }
@@ -83,6 +84,9 @@ class Query < ActiveRecord::Base
@available_filters["author_id"] = { :type => :list, :order => 5, :values => @project.users.collect{|s| [s.name, s.id.to_s] } } @available_filters["author_id"] = { :type => :list, :order => 5, :values => @project.users.collect{|s| [s.name, s.id.to_s] } }
@available_filters["category_id"] = { :type => :list_optional, :order => 6, :values => @project.issue_categories.collect{|s| [s.name, s.id.to_s] } } @available_filters["category_id"] = { :type => :list_optional, :order => 6, :values => @project.issue_categories.collect{|s| [s.name, s.id.to_s] } }
@available_filters["fixed_version_id"] = { :type => :list_optional, :order => 7, :values => @project.versions.collect{|s| [s.name, s.id.to_s] } } @available_filters["fixed_version_id"] = { :type => :list_optional, :order => 7, :values => @project.versions.collect{|s| [s.name, s.id.to_s] } }
unless @project.children.empty?
@available_filters["subproject_id"] = { :type => :list_one_or_more, :order => 13, :values => @project.children.collect{|s| [s.name, s.id.to_s] } }
end
# remove category filter if no category defined # remove category filter if no category defined
@available_filters.delete "category_id" if @available_filters["category_id"][:values].empty? @available_filters.delete "category_id" if @available_filters["category_id"][:values].empty?
end end
@@ -123,43 +127,54 @@ class Query < ActiveRecord::Base
end end
def statement def statement
sql = "1=1" sql = "1=1"
sql << " AND issues.project_id=%d" % project.id if project if has_filter?("subproject_id")
subproject_ids = []
if operator_for("subproject_id") == "="
subproject_ids = values_for("subproject_id").each(&:to_i)
else
subproject_ids = project.children.collect{|p| p.id}
end
sql << " AND #{Issue.table_name}.project_id IN (%d,%s)" % [project.id, subproject_ids.join(",")] if project
else
sql << " AND #{Issue.table_name}.project_id=%d" % project.id if project
end
filters.each_key do |field| filters.each_key do |field|
next if field == "subproject_id"
v = values_for field v = values_for field
next unless v and !v.empty? next unless v and !v.empty?
sql = sql + " AND " unless sql.empty? sql = sql + " AND " unless sql.empty?
case operator_for field case operator_for field
when "=" when "="
sql = sql + "issues.#{field} IN (" + v.each(&:to_i).join(",") + ")" sql = sql + "#{Issue.table_name}.#{field} IN (" + v.each(&:to_i).join(",") + ")"
when "!" when "!"
sql = sql + "issues.#{field} NOT IN (" + v.each(&:to_i).join(",") + ")" sql = sql + "#{Issue.table_name}.#{field} NOT IN (" + v.each(&:to_i).join(",") + ")"
when "!*" when "!*"
sql = sql + "issues.#{field} IS NULL" sql = sql + "#{Issue.table_name}.#{field} IS NULL"
when "*" when "*"
sql = sql + "issues.#{field} IS NOT NULL" sql = sql + "#{Issue.table_name}.#{field} IS NOT NULL"
when "o" when "o"
sql = sql + "issue_statuses.is_closed=#{connection.quoted_false}" if field == "status_id" sql = sql + "#{IssueStatus.table_name}.is_closed=#{connection.quoted_false}" if field == "status_id"
when "c" when "c"
sql = sql + "issue_statuses.is_closed=#{connection.quoted_true}" if field == "status_id" sql = sql + "#{IssueStatus.table_name}.is_closed=#{connection.quoted_true}" if field == "status_id"
when ">t-" when ">t-"
sql = sql + "issues.#{field} >= '%s'" % connection.quoted_date(Date.today - v.first.to_i) sql = sql + "#{Issue.table_name}.#{field} >= '%s'" % connection.quoted_date(Date.today - v.first.to_i)
when "<t-" when "<t-"
sql = sql + "issues.#{field} <= '" + (Date.today - v.first.to_i).strftime("%Y-%m-%d") + "'" sql = sql + "#{Issue.table_name}.#{field} <= '" + (Date.today - v.first.to_i).strftime("%Y-%m-%d") + "'"
when "t-" when "t-"
sql = sql + "issues.#{field} = '" + (Date.today - v.first.to_i).strftime("%Y-%m-%d") + "'" sql = sql + "#{Issue.table_name}.#{field} = '" + (Date.today - v.first.to_i).strftime("%Y-%m-%d") + "'"
when ">t+" when ">t+"
sql = sql + "issues.#{field} >= '" + (Date.today + v.first.to_i).strftime("%Y-%m-%d") + "'" sql = sql + "#{Issue.table_name}.#{field} >= '" + (Date.today + v.first.to_i).strftime("%Y-%m-%d") + "'"
when "<t+" when "<t+"
sql = sql + "issues.#{field} <= '" + (Date.today + v.first.to_i).strftime("%Y-%m-%d") + "'" sql = sql + "#{Issue.table_name}.#{field} <= '" + (Date.today + v.first.to_i).strftime("%Y-%m-%d") + "'"
when "t+" when "t+"
sql = sql + "issues.#{field} = '" + (Date.today + v.first.to_i).strftime("%Y-%m-%d") + "'" sql = sql + "#{Issue.table_name}.#{field} = '" + (Date.today + v.first.to_i).strftime("%Y-%m-%d") + "'"
when "t" when "t"
sql = sql + "issues.#{field} = '%s'" % connection.quoted_date(Date.today) sql = sql + "#{Issue.table_name}.#{field} = '%s'" % connection.quoted_date(Date.today)
when "~" when "~"
sql = sql + "issues.#{field} LIKE '%#{connection.quote_string(v.first)}%'" sql = sql + "#{Issue.table_name}.#{field} LIKE '%#{connection.quote_string(v.first)}%'"
when "!~" when "!~"
sql = sql + "issues.#{field} NOT LIKE '%#{connection.quote_string(v.first)}%'" sql = sql + "#{Issue.table_name}.#{field} NOT LIKE '%#{connection.quote_string(v.first)}%'"
end end
end if filters and valid? end if filters and valid?
sql sql
+64 -4
View File
@@ -1,5 +1,5 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang # Copyright (C) 2006-2007 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
@@ -17,12 +17,72 @@
class Repository < ActiveRecord::Base class Repository < ActiveRecord::Base
belongs_to :project belongs_to :project
has_many :changesets, :dependent => :destroy, :order => 'revision DESC'
has_many :changes, :through => :changesets
has_one :latest_changeset, :class_name => 'Changeset', :foreign_key => :repository_id, :order => 'revision DESC'
attr_protected :root_url
validates_presence_of :url validates_presence_of :url
validates_format_of :url, :with => /^(http|https|svn|file):\/\/.+/i validates_format_of :url, :with => /^(http|https|svn|file):\/\/.+/i
@scm = nil
def scm def scm
@scm ||= SvnRepos::Base.new url @scm ||= SvnRepos::Base.new url, root_url, login, password
update_attribute(:root_url, @scm.root_url) if root_url.blank?
@scm
end
def url=(str)
super if root_url.blank?
end
def changesets_with_path(path="")
path = "/#{path}%"
path = url.gsub(/^#{root_url}/, '') + path if root_url && root_url != url
path.squeeze!("/")
Changeset.with_scope(:find => { :include => :changes, :conditions => ["#{Change.table_name}.path LIKE ?", path] }) do
yield
end
end
def fetch_changesets
scm_info = scm.info
if scm_info
lastrev_identifier = scm_info.lastrev.identifier.to_i
if latest_changeset.nil? || latest_changeset.revision < lastrev_identifier
logger.debug "Fetching changesets for repository #{url}" if logger && logger.debug?
identifier_from = latest_changeset ? latest_changeset.revision + 1 : 1
while (identifier_from <= lastrev_identifier)
# loads changesets by batches of 200
identifier_to = [identifier_from + 199, lastrev_identifier].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,
:comment => revision.message)
revision.paths.each do |change|
Change.create(:changeset => changeset,
:action => change[:action],
:path => change[:path],
:from_path => change[:from_path],
:from_revision => change[:from_revision])
end
end
end unless revisions.nil?
identifier_from = identifier_to + 1
end
end
end
end
# fetch new changesets for all repositories
# can be called periodically by an external script
# eg. ruby script/runner "Repository.fetch_changesets"
def self.fetch_changesets
find(:all).each(&:fetch_changesets)
end end
end end
+35 -31
View File
@@ -1,33 +1,37 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang # Copyright (C) 2006 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# 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.
class Role < ActiveRecord::Base class Role < ActiveRecord::Base
before_destroy :check_integrity before_destroy :check_integrity
has_and_belongs_to_many :permissions has_and_belongs_to_many :permissions
has_many :workflows, :dependent => :delete_all has_many :workflows, :dependent => :delete_all
has_many :members has_many :members
acts_as_list acts_as_list
validates_presence_of :name validates_presence_of :name
validates_uniqueness_of :name validates_uniqueness_of :name
validates_format_of :name, :with => /^[\w\s\'\-]*$/i validates_format_of :name, :with => /^[\w\s\'\-]*$/i
private def <=>(role)
def check_integrity position <=> role.position
raise "Can't delete role" if Member.find(:first, :conditions =>["role_id=?", self.id]) end
private
def check_integrity
raise "Can't delete role" if Member.find(:first, :conditions =>["role_id=?", self.id])
end end
end end
+430 -213
View File
@@ -1,214 +1,431 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang # Copyright (C) 2006-2007 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# 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 'rexml/document' require 'rexml/document'
require 'cgi'
module SvnRepos
module SvnRepos
class CommandFailed < StandardError #:nodoc:
end class CommandFailed < StandardError #:nodoc:
end
class Base
@url = nil class Base
@login = nil
@password = nil def initialize(url, root_url=nil, login=nil, password=nil)
@url = url
def initialize(url, login=nil, password=nil) @login = login if login && !login.empty?
@url = url @password = (password || "") if @login
@login = login if login && !login.empty? @root_url = root_url.blank? ? retrieve_root_url : root_url
@password = (password || "") if @login end
end
def root_url
# Returns the entry identified by path and revision identifier @root_url
# or nil if entry doesn't exist in the repository end
def entry(path=nil, identifier=nil)
e = entries(path, identifier) def url
e ? e.first : nil @url
end end
# Returns an Entries collection # get info about the svn repository
# or nil if the given path doesn't exist in the repository def info
def entries(path=nil, identifier=nil) cmd = "svn info --xml #{target('')}"
path ||= '' cmd << " --username #{@login} --password #{@password}" if @login
identifier = 'HEAD' unless identifier and identifier > 0 info = nil
entries = Entries.new shellout(cmd) do |io|
cmd = "svn list --xml #{target(path)}@#{identifier}" begin
shellout(cmd) do |io| doc = REXML::Document.new(io)
begin #root_url = doc.elements["info/entry/repository/root"].text
doc = REXML::Document.new(io) info = Info.new({:root_url => doc.elements["info/entry/repository/root"].text,
doc.elements.each("lists/list/entry") do |entry| :lastrev => Revision.new({
entries << Entry.new({:name => entry.elements['name'].text, :identifier => doc.elements["info/entry/commit"].attributes['revision'],
:path => ((path.empty? ? "" : "#{path}/") + entry.elements['name'].text), :time => Time.parse(doc.elements["info/entry/commit/date"].text),
:kind => entry.attributes['kind'], :author => (doc.elements["info/entry/commit/author"] ? doc.elements["info/entry/commit/author"].text : "")
:size => (entry.elements['size'] and entry.elements['size'].text).to_i, })
:lastrev => Revision.new({ })
:identifier => entry.elements['commit'].attributes['revision'], rescue
:time => Time.parse(entry.elements['commit'].elements['date'].text), end
:author => (entry.elements['commit'].elements['author'] ? entry.elements['commit'].elements['author'].text : "anonymous") end
}) return nil if $? && $?.exitstatus != 0
}) info
end rescue Errno::ENOENT => e
rescue return nil
end end
end
return nil if $? && $?.exitstatus != 0 # Returns the entry identified by path and revision identifier
entries.sort_by_name # or nil if entry doesn't exist in the repository
rescue Errno::ENOENT => e def entry(path=nil, identifier=nil)
raise CommandFailed e = entries(path, identifier)
end e ? e.first : nil
end
def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={})
path ||= '' # Returns an Entries collection
identifier_from = 'HEAD' unless identifier_from and identifier_from.to_i > 0 # or nil if the given path doesn't exist in the repository
identifier_to = 1 unless identifier_to and identifier_to.to_i > 0 def entries(path=nil, identifier=nil)
revisions = Revisions.new path ||= ''
cmd = "svn log --xml -r #{identifier_from}:#{identifier_to} " identifier = 'HEAD' unless identifier and identifier > 0
cmd << "--verbose " if options[:with_paths] entries = Entries.new
cmd << target(path) cmd = "svn list --xml #{target(path)}@#{identifier}"
shellout(cmd) do |io| cmd << " --username #{@login} --password #{@password}" if @login
begin shellout(cmd) do |io|
doc = REXML::Document.new(io) begin
doc.elements.each("log/logentry") do |logentry| doc = REXML::Document.new(io)
paths = [] doc.elements.each("lists/list/entry") do |entry|
logentry.elements.each("paths/path") do |path| entries << Entry.new({:name => entry.elements['name'].text,
paths << {:action => path.attributes['action'], :path => ((path.empty? ? "" : "#{path}/") + entry.elements['name'].text),
:path => path.text :kind => entry.attributes['kind'],
} :size => (entry.elements['size'] and entry.elements['size'].text).to_i,
end :lastrev => Revision.new({
paths.sort! { |x,y| x[:path] <=> y[:path] } :identifier => entry.elements['commit'].attributes['revision'],
:time => Time.parse(entry.elements['commit'].elements['date'].text),
revisions << Revision.new({:identifier => logentry.attributes['revision'], :author => (entry.elements['commit'].elements['author'] ? entry.elements['commit'].elements['author'].text : "")
:author => (logentry.elements['author'] ? logentry.elements['author'].text : "anonymous"), })
:time => Time.parse(logentry.elements['date'].text), })
:message => logentry.elements['msg'].text, end
:paths => paths rescue
}) end
end end
rescue return nil if $? && $?.exitstatus != 0
end entries.sort_by_name
end rescue Errno::ENOENT => e
return nil if $? && $?.exitstatus != 0 raise CommandFailed
revisions end
rescue Errno::ENOENT => e
raise CommandFailed def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={})
end path ||= ''
identifier_from = 'HEAD' unless identifier_from and identifier_from.to_i > 0
def diff(path, identifier_from, identifier_to=nil) identifier_to = 1 unless identifier_to and identifier_to.to_i > 0
path ||= '' revisions = Revisions.new
if identifier_to and identifier_to.to_i > 0 cmd = "svn log --xml -r #{identifier_from}:#{identifier_to}"
identifier_to = identifier_to.to_i cmd << " --username #{@login} --password #{@password}" if @login
else cmd << " --verbose " if options[:with_paths]
identifier_to = identifier_from.to_i - 1 cmd << target(path)
end shellout(cmd) do |io|
cmd = "svn diff -r " begin
cmd << "#{identifier_to}:" doc = REXML::Document.new(io)
cmd << "#{identifier_from}" doc.elements.each("log/logentry") do |logentry|
cmd << "#{target(path)}@#{identifier_from}" paths = []
diff = [] logentry.elements.each("paths/path") do |path|
shellout(cmd) do |io| paths << {:action => path.attributes['action'],
io.each_line do |line| :path => path.text,
diff << line :from_path => path.attributes['copyfrom-path'],
end :from_revision => path.attributes['copyfrom-rev']
end }
return nil if $? && $?.exitstatus != 0 end
diff paths.sort! { |x,y| x[:path] <=> y[:path] }
rescue Errno::ENOENT => e
raise CommandFailed revisions << Revision.new({:identifier => logentry.attributes['revision'],
end :author => (logentry.elements['author'] ? logentry.elements['author'].text : ""),
:time => Time.parse(logentry.elements['date'].text),
def cat(path, identifier=nil) :message => logentry.elements['msg'].text,
identifier = (identifier and identifier.to_i > 0) ? identifier.to_i : "HEAD" :paths => paths
cmd = "svn cat #{target(path)}@#{identifier}" })
cat = nil end
shellout(cmd) do |io| rescue
cat = io.read end
end end
return nil if $? && $?.exitstatus != 0 return nil if $? && $?.exitstatus != 0
cat revisions
rescue Errno::ENOENT => e rescue Errno::ENOENT => e
raise CommandFailed raise CommandFailed
end end
private def diff(path, identifier_from, identifier_to=nil, type="inline")
def target(path) path ||= ''
" \"" << "#{@url}/#{path}".gsub(/["'?<>\*]/, '') << "\"" if identifier_to and identifier_to.to_i > 0
end identifier_to = identifier_to.to_i
else
def logger identifier_to = identifier_from.to_i - 1
RAILS_DEFAULT_LOGGER end
end cmd = "svn diff -r "
cmd << "#{identifier_to}:"
def shellout(cmd, &block) cmd << "#{identifier_from}"
logger.debug "Shelling out: #{cmd}" if logger && logger.debug? cmd << "#{target(path)}@#{identifier_from}"
IO.popen(cmd) do |io| cmd << " --username #{@login} --password #{@password}" if @login
block.call(io) if block_given? diff = []
end shellout(cmd) do |io|
end io.each_line do |line|
end diff << line
end
class Entries < Array end
def sort_by_name return nil if $? && $?.exitstatus != 0
sort {|x,y| DiffTableList.new diff, type
if x.kind == y.kind
x.name <=> y.name rescue Errno::ENOENT => e
else raise CommandFailed
x.kind <=> y.kind end
end
} def cat(path, identifier=nil)
end identifier = (identifier and identifier.to_i > 0) ? identifier.to_i : "HEAD"
cmd = "svn cat #{target(path)}@#{identifier}"
def revisions cmd << " --username #{@login} --password #{@password}" if @login
revisions ||= Revisions.new(collect{|entry| entry.lastrev}) cat = nil
end shellout(cmd) do |io|
end cat = io.read
end
class Entry return nil if $? && $?.exitstatus != 0
attr_accessor :name, :path, :kind, :size, :lastrev cat
def initialize(attributes={}) rescue Errno::ENOENT => e
self.name = attributes[:name] if attributes[:name] raise CommandFailed
self.path = attributes[:path] if attributes[:path] end
self.kind = attributes[:kind] if attributes[:kind]
self.size = attributes[:size].to_i if attributes[:size] private
self.lastrev = attributes[:lastrev] def retrieve_root_url
end info = self.info
info ? info.root_url : nil
def is_file? end
'file' == self.kind
end def target(path)
path ||= ""
def is_dir? base = path.match(/^\//) ? root_url : url
'dir' == self.kind " \"" << "#{base}/#{path}".gsub(/["'?<>\*]/, '') << "\""
end end
end
def logger
class Revisions < Array RAILS_DEFAULT_LOGGER
def latest end
sort {|x,y| x.time <=> y.time}.last
end def shellout(cmd, &block)
end logger.debug "Shelling out: #{cmd}" if logger && logger.debug?
IO.popen(cmd, "r+") do |io|
class Revision io.close_write
attr_accessor :identifier, :author, :time, :message, :paths block.call(io) if block_given?
def initialize(attributes={}) end
self.identifier = attributes[:identifier] end
self.author = attributes[:author] end
self.time = attributes[:time]
self.message = attributes[:message] || "" class Entries < Array
self.paths = attributes[:paths] def sort_by_name
end sort {|x,y|
end if x.kind == y.kind
x.name <=> y.name
else
x.kind <=> y.kind
end
}
end
def revisions
revisions ||= Revisions.new(collect{|entry| entry.lastrev})
end
end
class Info
attr_accessor :root_url, :lastrev
def initialize(attributes={})
self.root_url = attributes[:root_url] if attributes[:root_url]
self.lastrev = attributes[:lastrev]
end
end
class Entry
attr_accessor :name, :path, :kind, :size, :lastrev
def initialize(attributes={})
self.name = attributes[:name] if attributes[:name]
self.path = attributes[:path] if attributes[:path]
self.kind = attributes[:kind] if attributes[:kind]
self.size = attributes[:size].to_i if attributes[:size]
self.lastrev = attributes[:lastrev]
end
def is_file?
'file' == self.kind
end
def is_dir?
'dir' == self.kind
end
end
class Revisions < Array
def latest
sort {|x,y| x.time <=> y.time}.last
end
end
class Revision
attr_accessor :identifier, :author, :time, :message, :paths
def initialize(attributes={})
self.identifier = attributes[:identifier]
self.author = attributes[:author]
self.time = attributes[:time]
self.message = attributes[:message] || ""
self.paths = attributes[:paths]
end
end
# A line of Diff
class Diff
attr_accessor :nb_line_left
attr_accessor :line_left
attr_accessor :nb_line_right
attr_accessor :line_right
attr_accessor :type_diff_right
attr_accessor :type_diff_left
def initialize ()
self.nb_line_left = ''
self.nb_line_right = ''
self.line_left = ''
self.line_right = ''
self.type_diff_right = ''
self.type_diff_left = ''
end
def inspect
puts '### Start Line Diff ###'
puts self.nb_line_left
puts self.line_left
puts self.nb_line_right
puts self.line_right
end
end
class DiffTableList < Array
def initialize (diff, type="inline")
diff_table = DiffTable.new type
diff.each do |line|
if line =~ /^Index: (.*)$/
self << diff_table if diff_table.length > 1
diff_table = DiffTable.new type
end
a = diff_table.add_line line
end
self << diff_table
end
end
# Class for create a Diff
class DiffTable < Hash
attr_reader :file_name, :line_num_l, :line_num_r
# Initialize with a Diff file and the type of Diff View
# The type view must be inline or sbs (side_by_side)
def initialize (type="inline")
@parsing = false
@nb_line = 1
@start = false
@before = 'same'
@second = true
@type = type
end
# Function for add a line of this Diff
def add_line(line)
unless @parsing
if line =~ /^Index: (.*)$/
@file_name = $1
return false
elsif line =~ /^@@ (\+|\-)(\d+)(,\d+)? (\+|\-)(\d+)(,\d+)? @@/
@line_num_l = $2.to_i
@line_num_r = $5.to_i
@parsing = true
end
else
if line =~ /^_+$/
self.delete(self.keys.sort.last)
@parsing = false
return false
elsif line =~ /^@@ (\+|\-)(\d+)(,\d+)? (\+|\-)(\d+)(,\d+)? @@/
@line_num_l = $2.to_i
@line_num_r = $5.to_i
else
@nb_line += 1 if parse_line(line, @type)
end
end
return true
end
def inspect
puts '### DIFF TABLE ###'
puts "file : #{file_name}"
self.each do |d|
d.inspect
end
end
private
# Test if is a Side By Side type
def sbs?(type, func)
if @start and type == "sbs"
if @before == func and @second
tmp_nb_line = @nb_line
self[tmp_nb_line] = Diff.new
else
@second = false
tmp_nb_line = @start
@start += 1
@nb_line -= 1
end
else
tmp_nb_line = @nb_line
@start = @nb_line
self[tmp_nb_line] = Diff.new
@second = true
end
unless self[tmp_nb_line]
@nb_line += 1
self[tmp_nb_line] = Diff.new
else
self[tmp_nb_line]
end
end
# Escape the HTML for the diff
def escapeHTML(line)
CGI.escapeHTML(line).gsub(/\s/, '&nbsp;')
end
def parse_line (line, type="inline")
if line[0, 1] == "+"
diff = sbs? type, 'add'
@before = 'add'
diff.line_left = escapeHTML line[1..-1]
diff.nb_line_left = @line_num_l
diff.type_diff_left = 'diff_in'
@line_num_l += 1
true
elsif line[0, 1] == "-"
diff = sbs? type, 'remove'
@before = 'remove'
diff.line_right = escapeHTML line[1..-1]
diff.nb_line_right = @line_num_r
diff.type_diff_right = 'diff_out'
@line_num_r += 1
true
elsif line[0, 1] =~ /\s/
@before = 'same'
@start = false
diff = Diff.new
diff.line_right = escapeHTML line[1..-1]
diff.nb_line_right = @line_num_r
diff.line_left = escapeHTML line[1..-1]
diff.nb_line_left = @line_num_l
self[@nb_line] = diff
@line_num_l += 1
@line_num_r += 1
true
else
false
end
end
end
end end
+33
View File
@@ -0,0 +1,33 @@
class TimeEntry < ActiveRecord::Base
# could have used polymorphic association
# project association here allows easy loading of time entries at project level with one database trip
belongs_to :project
belongs_to :issue
belongs_to :user
belongs_to :activity, :class_name => 'Enumeration', :foreign_key => :activity_id
attr_protected :project_id, :user_id, :tyear, :tmonth, :tweek
validates_presence_of :user_id, :activity_id, :project_id, :hours, :spent_on
validates_numericality_of :hours, :allow_nil => true
validates_length_of :comment, :maximum => 255
def before_validation
self.project = issue.project if issue && project.nil?
end
def validate
errors.add :hours, :activerecord_error_invalid if hours && (hours < 0 || hours >= 1000)
errors.add :project_id, :activerecord_error_invalid if project.nil?
errors.add :issue_id, :activerecord_error_invalid if (issue_id && !issue) || (issue && project!=issue.project)
end
# tyear, tmonth, tweek assigned where setting spent_on attributes
# these attributes make time aggregations easier
def spent_on=(date)
super
self.tyear = spent_on ? spent_on.year : nil
self.tmonth = spent_on ? spent_on.month : nil
self.tweek = spent_on ? spent_on.cweek : nil
end
end
+1 -1
View File
@@ -31,7 +31,7 @@ class Token < ActiveRecord::Base
# Delete all expired tokens # Delete all expired tokens
def self.destroy_expired def self.destroy_expired
Token.delete_all ["created_on < ?", Time.now - @@validity_time] Token.delete_all ["action <> 'feeds' AND created_on < ?", Time.now - @@validity_time]
end end
private private
+31 -31
View File
@@ -1,33 +1,33 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang # Copyright (C) 2006 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# 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.
class Tracker < ActiveRecord::Base class Tracker < ActiveRecord::Base
before_destroy :check_integrity before_destroy :check_integrity
has_many :issues has_many :issues
has_many :workflows, :dependent => :delete_all has_many :workflows, :dependent => :delete_all
has_and_belongs_to_many :custom_fields, :class_name => 'IssueCustomField', :join_table => 'custom_fields_trackers', :association_foreign_key => 'custom_field_id' has_and_belongs_to_many :custom_fields, :class_name => 'IssueCustomField', :join_table => "#{table_name_prefix}custom_fields_trackers#{table_name_suffix}", :association_foreign_key => 'custom_field_id'
acts_as_list acts_as_list
validates_presence_of :name validates_presence_of :name
validates_uniqueness_of :name validates_uniqueness_of :name
validates_format_of :name, :with => /^[\w\s\'\-]*$/i validates_format_of :name, :with => /^[\w\s\'\-]*$/i
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])
end end
end end
+152 -140
View File
@@ -1,142 +1,154 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang # Copyright (C) 2006-2007 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# 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 "digest/sha1" require "digest/sha1"
class User < ActiveRecord::Base class User < ActiveRecord::Base
has_many :memberships, :class_name => 'Member', :include => [ :project, :role ], :dependent => :delete_all has_many :memberships, :class_name => 'Member', :include => [ :project, :role ], :dependent => :delete_all
has_many :projects, :through => :memberships has_many :projects, :through => :memberships
has_many :custom_values, :dependent => :delete_all, :as => :customized has_many :custom_values, :dependent => :delete_all, :as => :customized
has_one :preference, :dependent => :destroy, :class_name => 'UserPreference' has_one :preference, :dependent => :destroy, :class_name => 'UserPreference'
belongs_to :auth_source has_one :rss_key, :dependent => :destroy, :class_name => 'Token', :conditions => "action='feeds'"
belongs_to :auth_source
attr_accessor :password, :password_confirmation
attr_accessor :last_before_login_on attr_accessor :password, :password_confirmation
# Prevents unauthorized assignments attr_accessor :last_before_login_on
attr_protected :login, :admin, :password, :password_confirmation, :hashed_password # Prevents unauthorized assignments
attr_protected :login, :admin, :password, :password_confirmation, :hashed_password
validates_presence_of :login, :firstname, :lastname, :mail
validates_uniqueness_of :login, :mail validates_presence_of :login, :firstname, :lastname, :mail
# Login must contain lettres, numbers, underscores only validates_uniqueness_of :login, :mail
validates_format_of :firstname, :lastname, :with => /^[\w\s\'\-]*$/i # 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_format_of :mail, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i validates_length_of :login, :maximum => 30
# Password length between 4 and 12 validates_format_of :firstname, :lastname, :with => /^[\w\s\'\-]*$/i
validates_length_of :password, :in => 4..12, :allow_nil => true validates_length_of :firstname, :lastname, :maximum => 30
validates_confirmation_of :password, :allow_nil => true validates_format_of :mail, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i
validates_associated :custom_values, :on => :update validates_length_of :mail, :maximum => 60
# Password length between 4 and 12
# Account statuses validates_length_of :password, :in => 4..12, :allow_nil => true
STATUS_ACTIVE = 1 validates_confirmation_of :password, :allow_nil => true
STATUS_REGISTERED = 2 validates_associated :custom_values, :on => :update
STATUS_LOCKED = 3
# Account statuses
def before_save STATUS_ACTIVE = 1
# update hashed_password if password was set STATUS_REGISTERED = 2
self.hashed_password = User.hash_password(self.password) if self.password STATUS_LOCKED = 3
end
def before_save
def self.active # update hashed_password if password was set
with_scope :find => { :conditions => [ "status = ?", STATUS_ACTIVE ] } do self.hashed_password = User.hash_password(self.password) if self.password
yield end
end
end def self.active
with_scope :find => { :conditions => [ "status = ?", STATUS_ACTIVE ] } do
def self.find_active(*args) yield
active do end
find(*args) end
end
end def self.find_active(*args)
active do
# Returns the user that matches provided login and password, or nil find(*args)
def self.try_to_login(login, password) end
user = find(:first, :conditions => ["login=?", login]) end
if user
# user is already in local database # Returns the user that matches provided login and password, or nil
return nil if !user.active? def self.try_to_login(login, password)
if user.auth_source user = find(:first, :conditions => ["login=?", login])
# user has an external authentication method if user
return nil unless user.auth_source.authenticate(login, password) # user is already in local database
else return nil if !user.active?
# authentication with local password if user.auth_source
return nil unless User.hash_password(password) == user.hashed_password # user has an external authentication method
end return nil unless user.auth_source.authenticate(login, password)
else else
# user is not yet registered, try to authenticate with available sources # authentication with local password
attrs = AuthSource.authenticate(login, password) return nil unless User.hash_password(password) == user.hashed_password
if attrs end
onthefly = new(*attrs) else
onthefly.login = login # user is not yet registered, try to authenticate with available sources
onthefly.language = Setting.default_language attrs = AuthSource.authenticate(login, password)
if onthefly.save if attrs
user = find(:first, :conditions => ["login=?", login]) onthefly = new(*attrs)
logger.info("User '#{user.login}' created on the fly.") if logger onthefly.login = login
end onthefly.language = Setting.default_language
end if onthefly.save
end user = find(:first, :conditions => ["login=?", login])
user.update_attribute(:last_login_on, Time.now) if user logger.info("User '#{user.login}' created on the fly.") if logger
user end
end
rescue => text end
raise text user.update_attribute(:last_login_on, Time.now) if user
end user
# Return user's full name for display rescue => text
def display_name raise text
firstname + " " + lastname end
end
# Return user's full name for display
def name def display_name
display_name firstname + " " + lastname
end end
def active? def name
self.status == STATUS_ACTIVE display_name
end end
def registered? def active?
self.status == STATUS_REGISTERED self.status == STATUS_ACTIVE
end end
def locked? def registered?
self.status == STATUS_LOCKED self.status == STATUS_REGISTERED
end end
def check_password?(clear_password) def locked?
User.hash_password(clear_password) == self.hashed_password self.status == STATUS_LOCKED
end end
def role_for_project(project_id) def check_password?(clear_password)
@role_for_projects ||= User.hash_password(clear_password) == self.hashed_password
begin end
roles = {}
self.memberships.each { |m| roles.store m.project_id, m.role_id } def role_for_project(project)
roles member = memberships.detect {|m| m.project_id == project.id}
end member ? member.role : nil
@role_for_projects[project_id] end
end
def pref
def pref self.preference ||= UserPreference.new(:user => self)
self.preference ||= UserPreference.new(:user => self) end
end
def get_or_create_rss_key
private self.rss_key || Token.create(:user => self, :action => 'feeds')
# Return password digest end
def self.hash_password(clear_password)
Digest::SHA1.hexdigest(clear_password || "") def self.find_by_rss_key(key)
token = Token.find_by_value(key)
token && token.user.active? ? token.user : nil
end
def <=>(user)
lastname <=> user.lastname
end
private
# Return password digest
def self.hash_password(clear_password)
Digest::SHA1.hexdigest(clear_password || "")
end end
end end
+22 -22
View File
@@ -1,23 +1,23 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang # Copyright (C) 2006 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# 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.
class UserCustomField < CustomField class UserCustomField < CustomField
def type_name def type_name
:label_user_plural :label_user_plural
end end
end end
+39 -31
View File
@@ -1,32 +1,40 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang # Copyright (C) 2006 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# 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.
class Version < ActiveRecord::Base class Version < ActiveRecord::Base
before_destroy :check_integrity before_destroy :check_integrity
belongs_to :project belongs_to :project
has_many :fixed_issues, :class_name => 'Issue', :foreign_key => 'fixed_version_id' has_many :fixed_issues, :class_name => 'Issue', :foreign_key => 'fixed_version_id'
has_many :attachments, :as => :container, :dependent => :destroy has_many :attachments, :as => :container, :dependent => :destroy
validates_presence_of :name validates_presence_of :name
validates_uniqueness_of :name, :scope => [:project_id] validates_uniqueness_of :name, :scope => [:project_id]
validates_format_of :effective_date, :with => /^\d{4}-\d{2}-\d{2}$/, :message => :activerecord_error_not_a_date validates_format_of :effective_date, :with => /^\d{4}-\d{2}-\d{2}$/, :message => :activerecord_error_not_a_date
private def start_date
def check_integrity effective_date
raise "Can't delete version" if self.fixed_issues.find(:first) end
end
def due_date
effective_date
end
private
def check_integrity
raise "Can't delete version" if self.fixed_issues.find(:first)
end
end end
+44
View File
@@ -0,0 +1,44 @@
# 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 Wiki < ActiveRecord::Base
belongs_to :project
has_many :pages, :class_name => 'WikiPage', :dependent => :destroy
validates_presence_of :start_page
# find the page with the given title
# if page doesn't exist, return a new page
def find_or_new_page(title)
title = Wiki.titleize(title || start_page)
find_page(title) || WikiPage.new(:wiki => self, :title => title)
end
# find the page with the given title
def find_page(title)
pages.find_by_title(Wiki.titleize(title || start_page))
end
# turn a string into a valid page title
def self.titleize(title)
# replace spaces with _ and remove unwanted caracters
title = title.gsub(/\s+/, '_').delete(',;|') if title
# upcase the first letter
title = title[0..0].upcase + title[1..-1] if title
title
end
end
+58
View File
@@ -0,0 +1,58 @@
# 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 'zlib'
class WikiContent < ActiveRecord::Base
belongs_to :page, :class_name => 'WikiPage', :foreign_key => 'page_id'
belongs_to :author, :class_name => 'User', :foreign_key => 'author_id'
validates_presence_of :text
acts_as_versioned
class Version
belongs_to :author, :class_name => 'User', :foreign_key => 'author_id'
attr_protected :data
def text=(plain)
case Setting.wiki_compression
when 'gzip'
begin
self.data = Zlib::Deflate.deflate(plain, Zlib::BEST_COMPRESSION)
self.compression = 'gzip'
rescue
self.data = plain
self.compression = ''
end
else
self.data = plain
self.compression = ''
end
plain
end
def text
@text ||= case compression
when 'gzip'
Zlib::Inflate.inflate(data)
else
# uncompressed data
data
end
end
end
end
+44
View File
@@ -0,0 +1,44 @@
# 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 WikiPage < ActiveRecord::Base
belongs_to :wiki
has_one :content, :class_name => 'WikiContent', :foreign_key => 'page_id', :dependent => :destroy
validates_presence_of :title
validates_format_of :title, :with => /^[^,\s]*$/
validates_uniqueness_of :title, :scope => :wiki_id, :case_sensitive => false
validates_associated :content
def before_save
self.title = Wiki.titleize(title)
end
def pretty_title
WikiPage.pretty_title(title)
end
def content_for_version(version=nil)
result = content.versions.find_by_version(version.to_i) if version
result ||= content
result
end
def self.pretty_title(str)
(str && str.is_a?(String)) ? str.tr('_', ' ') : str
end
end
+23 -23
View File
@@ -1,24 +1,24 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang # Copyright (C) 2006 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# 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.
class Workflow < ActiveRecord::Base class Workflow < ActiveRecord::Base
belongs_to :role belongs_to :role
belongs_to :old_status, :class_name => 'IssueStatus', :foreign_key => 'old_status_id' belongs_to :old_status, :class_name => 'IssueStatus', :foreign_key => 'old_status_id'
belongs_to :new_status, :class_name => 'IssueStatus', :foreign_key => 'new_status_id' belongs_to :new_status, :class_name => 'IssueStatus', :foreign_key => 'new_status_id'
validates_presence_of :role, :old_status, :new_status validates_presence_of :role, :old_status, :new_status
end end
+21 -21
View File
@@ -1,24 +1,24 @@
<center> <center>
<div class="box login"> <div class="box login">
<h2 class="icon22 icon22-authent"><%=l(:label_please_login)%></h2> <h2 class="icon22 icon22-authent"><%=l(:label_please_login)%></h2>
<% form_tag({:action=> "login"}, :class => "tabular") do %>
<p><label for="login"><%=l(:field_login)%>:</label>
<%= text_field_tag 'login', nil, :size => 25 %></p>
<p><label for="password"><%=l(:field_password)%>:</label>
<%= password_field_tag 'password', nil, :size => 25 %></p>
<p><center><input type="submit" name="login" value="<%=l(:button_login)%> &#187;" class="primary" /></center>
<% end %>
<br>
<% links = []
links << link_to(l(:label_register), :action => 'register') if Setting.self_registration?
links << link_to(l(:label_password_lost), :action => 'lost_password') if Setting.lost_password?
%>
<%= links.join(" | ") %>
</p>
</div> <% form_tag({:action=> "login"}, :class => "tabular") do %>
<p><label for="login"><%=l(:field_login)%>:</label>
<%= text_field_tag 'login', nil, :size => 25 %></p>
<p><label for="password"><%=l(:field_password)%>:</label>
<%= password_field_tag 'password', nil, :size => 25 %></p>
<p><center><input type="submit" name="login" value="<%=l(:button_login)%> &#187;" class="primary" /></center>
<% end %>
<br>
<% links = []
links << link_to(l(:label_register), :action => 'register') if Setting.self_registration?
links << link_to(l(:label_password_lost), :action => 'lost_password') if Setting.lost_password?
%>
<%= links.join(" | ") %>
</p>
</div>
</center> </center>
+13 -13
View File
@@ -1,14 +1,14 @@
<center> <center>
<div class="box login"> <div class="box login">
<h2><%=l(:label_password_lost)%></h2> <h2><%=l(:label_password_lost)%></h2>
<% form_tag({:action=> "lost_password"}, :class => "tabular") do %> <% form_tag({:action=> "lost_password"}, :class => "tabular") do %>
<p><label for="mail"><%=l(:field_mail)%> <span class="required">*</span></label> <p><label for="mail"><%=l(:field_mail)%> <span class="required">*</span></label>
<%= text_field_tag 'mail', nil, :size => 40 %></p> <%= text_field_tag 'mail', nil, :size => 40 %></p>
<p><center><%= submit_tag l(:button_submit) %></center></p> <p><center><%= submit_tag l(:button_submit) %></center></p>
<% end %> <% end %>
</div> </div>
</center> </center>
+10 -9
View File
@@ -1,21 +1,22 @@
<center> <center>
<div class="box login"> <div class="box login">
<h2><%=l(:label_password_lost)%></h2> <h2><%=l(:label_password_lost)%></h2>
<p><%=l(:field_login)%>: <strong><%= @user.login %></strong><br /> <p><%=l(:field_login)%>: <strong><%= @user.login %></strong><br />
<%= error_messages_for 'user' %> <%= error_messages_for 'user' %>
<% form_tag({:token => @token.value}, :class => "tabular") do %> <% form_tag({:token => @token.value}, :class => "tabular") do %>
<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 %></p> <%= password_field_tag 'new_password', nil, :size => 25 %><br />
<em><%= l(:text_length_between, 4, 12) %></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>
<p><center><%= submit_tag l(:button_save) %></center></p> <p><center><%= submit_tag l(:button_save) %></center></p>
<% end %> <% end %>
</div> </div>
</center> </center>
+9 -9
View File
@@ -7,7 +7,7 @@
<!--[form:user]--> <!--[form:user]-->
<p><label for="user_login"><%=l(:field_login)%> <span class="required">*</span></label> <p><label for="user_login"><%=l(:field_login)%> <span class="required">*</span></label>
<%= 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 %></p>
@@ -16,22 +16,22 @@
<p><label for="user_firstname"><%=l(:field_firstname)%> <span class="required">*</span></label> <p><label for="user_firstname"><%=l(:field_firstname)%> <span class="required">*</span></label>
<%= text_field 'user', 'firstname' %></p> <%= text_field 'user', 'firstname' %></p>
<p><label for="user_lastname"><%=l(:field_lastname)%> <span class="required">*</span></label> <p><label for="user_lastname"><%=l(:field_lastname)%> <span class="required">*</span></label>
<%= text_field 'user', 'lastname' %></p> <%= text_field 'user', 'lastname' %></p>
<p><label for="user_mail"><%=l(:field_mail)%> <span class="required">*</span></label> <p><label for="user_mail"><%=l(:field_mail)%> <span class="required">*</span></label>
<%= text_field 'user', 'mail' %></p> <%= text_field 'user', 'mail' %></p>
<p><label for="user_language"><%=l(:field_language)%></label> <p><label for="user_language"><%=l(:field_language)%></label>
<%= select("user", "language", lang_options_for_select) %></p> <%= select("user", "language", lang_options_for_select) %></p>
<% 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> <p><label for="user_mail_notification"><%=l(:field_mail_notification)%></label>
<%= check_box 'user', 'mail_notification' %></p> <%= check_box 'user', 'mail_notification' %></p>
<!--[eoform:user]--> <!--[eoform:user]-->
</div> </div>
+25 -25
View File
@@ -1,26 +1,26 @@
<h2><%= @user.display_name %></h2> <h2><%= @user.display_name %></h2>
<p> <p>
<%= mail_to @user.mail unless @user.pref.hide_mail %> <%= mail_to @user.mail unless @user.pref.hide_mail %>
<ul> <ul>
<li><%=l(:label_registered_on)%>: <%= format_date(@user.created_on) %></li> <li><%=l(:label_registered_on)%>: <%= format_date(@user.created_on) %></li>
<% for custom_value in @custom_values %> <% for custom_value in @custom_values %>
<% if !custom_value.value.empty? %> <% if !custom_value.value.empty? %>
<li><%= custom_value.custom_field.name%>: <%=h show_value(custom_value) %></li> <li><%= custom_value.custom_field.name%>: <%=h show_value(custom_value) %></li>
<% end %> <% end %>
<% end %> <% end %>
</ul> </ul>
</p> </p>
<h3><%=l(:label_project_plural)%></h3> <h3><%=l(:label_project_plural)%></h3>
<p> <p>
<% for membership in @user.memberships %> <% for membership in @user.memberships %>
<%= membership.project.name %> (<%= membership.role.name %>, <%= format_date(membership.created_on) %>) <%= membership.project.name %> (<%= membership.role.name %>, <%= format_date(membership.created_on) %>)
<br /> <br />
<% end %> <% end %>
</p> </p>
<h3><%=l(:label_activity)%></h3> <h3><%=l(:label_activity)%></h3>
<p> <p>
<%=l(:label_reported_issues)%>: <%= Issue.count(["author_id=?", @user.id]) %> <%=l(:label_reported_issues)%>: <%= Issue.count(["author_id=?", @user.id]) %>
</p> </p>
+25
View File
@@ -0,0 +1,25 @@
<div id="menuAdmin" class="menu" onmouseover="menuMouseover(event)">
<a class="menuItem" href="#" onmouseover="menuItemMouseover(event,'menuProjects');" onclick="this.blur(); return false;"><span class="menuItemText"><%=l(:label_project_plural)%></span><span class="menuItemArrow">&#9654;</span></a>
<a class="menuItem" href="#" onmouseover="menuItemMouseover(event,'menuUsers');" onclick="this.blur(); return false;"><span class="menuItemText"><%=l(:label_user_plural)%></span><span class="menuItemArrow">&#9654;</span></a>
<%= link_to l(:label_role_and_permissions), {:controller => 'roles' }, :class => "menuItem" %>
<a class="menuItem" href="#" onmouseover="menuItemMouseover(event,'menuTrackers');" onclick="this.blur(); return false;"><span class="menuItemText"><%=l(:label_issue_tracking)%></span><span class="menuItemArrow">&#9654;</span></a>
<%= link_to l(:label_custom_field_plural), {:controller => 'custom_fields' }, :class => "menuItem" %>
<%= link_to l(:label_enumerations), {:controller => 'enumerations' }, :class => "menuItem" %>
<%= link_to l(:field_mail_notification), {:controller => 'admin', :action => 'mail_options' }, :class => "menuItem" %>
<%= link_to l(:label_authentication), {:controller => 'auth_sources' }, :class => "menuItem" %>
<%= link_to l(:label_settings), {:controller => 'settings' }, :class => "menuItem" %>
<%= link_to l(:label_information_plural), {:controller => 'admin', :action => 'info' }, :class => "menuItem" %>
</div>
<div id="menuTrackers" class="menu">
<%= link_to l(:label_tracker_plural), {:controller => 'trackers' }, :class => "menuItem" %>
<%= link_to l(:label_issue_status_plural), {:controller => 'issue_statuses' }, :class => "menuItem" %>
<%= link_to l(:label_workflow), {:controller => 'roles', :action => 'workflow' }, :class => "menuItem" %>
</div>
<div id="menuProjects" class="menu">
<%= link_to l(:button_list), {:controller => 'admin', :action => 'projects' }, :class => "menuItem" %>
<%= link_to l(:label_new), {:controller => 'projects', :action => 'add' }, :class => "menuItem" %>
</div>
<div id="menuUsers" class="menu">
<%= link_to l(:button_list), {:controller => 'users' }, :class => "menuItem" %>
<%= link_to l(:label_new), {:controller => 'users', :action => 'add' }, :class => "menuItem" %>
</div>
+44 -44
View File
@@ -1,45 +1,45 @@
<h2><%=l(:label_administration)%></h2> <h2><%=l(:label_administration)%></h2>
<p class="icon22 icon22-projects"> <p class="icon22 icon22-projects">
<%= link_to l(:label_project_plural), :controller => 'admin', :action => 'projects' %> | <%= link_to l(:label_project_plural), :controller => 'admin', :action => 'projects' %> |
<%= link_to l(:label_new), :controller => 'projects', :action => 'add' %> <%= link_to l(:label_new), :controller => 'projects', :action => 'add' %>
</p> </p>
<p class="icon22 icon22-users"> <p class="icon22 icon22-users">
<%= link_to l(:label_user_plural), :controller => 'users' %> | <%= link_to l(:label_user_plural), :controller => 'users' %> |
<%= link_to l(:label_new), :controller => 'users', :action => 'add' %> <%= link_to l(:label_new), :controller => 'users', :action => 'add' %>
</p> </p>
<p class="icon22 icon22-role"> <p class="icon22 icon22-role">
<%= link_to l(:label_role_and_permissions), :controller => 'roles' %> <%= link_to l(:label_role_and_permissions), :controller => 'roles' %>
</p> </p>
<p class="icon22 icon22-tracker"> <p class="icon22 icon22-tracker">
<%= link_to l(:label_tracker_plural), :controller => 'trackers' %> | <%= link_to l(:label_tracker_plural), :controller => 'trackers' %> |
<%= link_to l(:label_issue_status_plural), :controller => 'issue_statuses' %> | <%= link_to l(:label_issue_status_plural), :controller => 'issue_statuses' %> |
<%= link_to l(:label_workflow), :controller => 'roles', :action => 'workflow' %> <%= link_to l(:label_workflow), :controller => 'roles', :action => 'workflow' %>
</p> </p>
<p class="icon22 icon22-workflow"> <p class="icon22 icon22-workflow">
<%= link_to l(:label_custom_field_plural), :controller => 'custom_fields' %> <%= link_to l(:label_custom_field_plural), :controller => 'custom_fields' %>
</p> </p>
<p class="icon22 icon22-options"> <p class="icon22 icon22-options">
<%= link_to l(:label_enumerations), :controller => 'enumerations' %> <%= link_to l(:label_enumerations), :controller => 'enumerations' %>
</p> </p>
<p class="icon22 icon22-notifications"> <p class="icon22 icon22-notifications">
<%= link_to l(:field_mail_notification), :controller => 'admin', :action => 'mail_options' %> <%= link_to l(:field_mail_notification), :controller => 'admin', :action => 'mail_options' %>
</p> </p>
<p class="icon22 icon22-authent"> <p class="icon22 icon22-authent">
<%= link_to l(:label_authentication), :controller => 'auth_sources' %> <%= link_to l(:label_authentication), :controller => 'auth_sources' %>
</p> </p>
<p class="icon22 icon22-settings"> <p class="icon22 icon22-settings">
<%= link_to l(:label_settings), :controller => 'settings' %> <%= link_to l(:label_settings), :controller => 'settings' %>
</p> </p>
<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>
+9 -3
View File
@@ -1,3 +1,9 @@
<h2><%=l(:label_information_plural)%></h2> <h2><%=l(:label_information_plural)%></h2>
<p><%=l(:field_version)%>: <strong>redMine <%= Redmine::VERSION %></strong></p> <p><%=l(:field_version)%>: <strong>redMine <%= Redmine::VERSION %></strong> (<%= @db_adapter_name %>)</p>
<table class="list">
<tr class="odd"><td>File repository writable</td><td><%= image_tag (@flags[:file_repository_writable] ? 'true.png' : 'false.png'), :style => "vertical-align:bottom;" %></td></tr>
<tr class="even"><td>Default administrator account changed</td><td><%= image_tag (@flags[:default_admin_changed] ? 'true.png' : 'false.png'), :style => "vertical-align:bottom;" %></td></tr>
<tr class="odd"><td>Textile available</td><td><%= image_tag (@flags[:textile_available] ? 'true.png' : 'false.png'), :style => "vertical-align:bottom;" %></td></tr>
</table>
+25 -24
View File
@@ -1,24 +1,25 @@
<h2><%=l(:field_mail_notification)%></h2> <h2><%=l(:field_mail_notification)%></h2>
<% form_tag ({:action => 'mail_options'}, :id => 'mail_options_form') do %> <% form_tag({:action => 'mail_options'}, :id => 'mail_options_form') do %>
<div class="box"> <div class="box">
<p><%=l(:text_select_mail_notifications)%></p> <p><%=l(:text_select_mail_notifications)%></p>
<% actions = @actions.group_by {|p| p.group_id } %> <% actions = @actions.group_by {|p| p.group_id } %>
<% actions.keys.sort.each do |group_id| %> <% actions.keys.sort.each do |group_id| %>
<fieldset style="margin-top: 6px;"><legend><strong><%= l(Permission::GROUPS[group_id]) %></strong></legend> <fieldset style="margin-top: 6px;"><legend><strong><%= l(Permission::GROUPS[group_id]) %></strong></legend>
<% actions[group_id].each do |p| %> <% actions[group_id].each do |p| %>
<div style="width:170px;float:left;"><%= check_box_tag "action_ids[]", p.id, p.mail_enabled? %> <div style="width:170px;float:left;"><%= check_box_tag "action_ids[]", p.id, p.mail_enabled? %>
<%= l(p.description.to_sym) %> <%= l(p.description.to_sym) %>
</div> </div>
<% end %> <% end %>
</fieldset> <div class="clear"></div>
<% end %> </fieldset>
<% end %>
<br />
<p><%= check_all_links 'mail_options_form' %></p> <br />
</div> <p><%= check_all_links('mail_options_form') %></p>
</div>
<%= submit_tag l(:button_save) %>
<% end %> <%= submit_tag l(:button_save) %>
<% end %>
+11 -11
View File
@@ -6,24 +6,24 @@
<table class="list"> <table class="list">
<thead><tr> <thead><tr>
<%= sort_header_tag('name', :caption => l(:label_project)) %> <%= sort_header_tag('name', :caption => l(:label_project)) %>
<th><%=l(:field_description)%></th> <th><%=l(:field_description)%></th>
<th><%=l(:field_is_public)%></th> <th><%=l(:field_is_public)%></th>
<th><%=l(:label_subproject_plural)%></th> <th><%=l(:label_subproject_plural)%></th>
<%= sort_header_tag('created_on', :caption => l(:field_created_on)) %> <%= sort_header_tag('created_on', :caption => l(:field_created_on)) %>
<th></th> <th></th>
</tr></thead> </tr></thead>
<tbody> <tbody>
<% for project in @projects %> <% for project in @projects %>
<tr class="<%= cycle("odd", "even") %>"> <tr class="<%= cycle("odd", "even") %>">
<td><%= link_to project.name, :controller => 'projects', :action => 'settings', :id => project %> <td><%= link_to project.name, :controller => 'projects', :action => 'settings', :id => project %>
<td><%=h project.description %> <td><%=h project.description %>
<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 %>
<td align="center"><%= format_date(project.created_on) %> <td align="center"><%= format_date(project.created_on) %>
<td align="center"> <td align="center">
<%= button_to l(:button_delete), { :controller => 'projects', :action => 'destroy', :id => project }, :class => "button-small" %> <%= button_to l(:button_delete), { :controller => 'projects', :action => 'destroy', :id => project }, :class => "button-small" %>
</td> </td>
</tr> </tr>
<% end %> <% end %>
</tbody> </tbody>
+4 -4
View File
@@ -1,4 +1,4 @@
<h2>404</h2> <h2>404</h2>
<p><%= l(:notice_file_not_found) %></p> <p><%= l(:notice_file_not_found) %></p>
<p><a href="javascript:history.back()">Back</a></p> <p><a href="javascript:history.back()">Back</a></p>
+8 -8
View File
@@ -57,30 +57,30 @@ function deleteValueField(e) {
<!--[form:custom_field]--> <!--[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>
<p><label for="custom_field_min_length"><%=l(:label_min_max_length)%></label> <p><label for="custom_field_min_length"><%=l(:label_min_max_length)%></label>
<%= f.text_field :min_length, :size => 5, :no_label => true %> - <%= f.text_field :min_length, :size => 5, :no_label => true %> -
<%= f.text_field :max_length, :size => 5, :no_label => true %><br>(<%=l(:text_min_max_length_info)%>)</p> <%= f.text_field :max_length, :size => 5, :no_label => true %><br>(<%=l(:text_min_max_length_info)%>)</p>
<p><%= f.text_field :regexp, :size => 50 %><br>(<%=l(:text_regexp_info)%>)</p> <p><%= f.text_field :regexp, :size => 50 %><br>(<%=l(:text_regexp_info)%>)</p>
<p id="custom_field_possible_values"><label><%= l(:field_possible_values) %> <%= image_to_function "add.png", "addValueField();return false" %></label> <p id="custom_field_possible_values"><label><%= l(:field_possible_values) %> <%= image_to_function "add.png", "addValueField();return false" %></label>
<% (@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();" %> <%= javascript_tag "toggle_custom_field_format();" %>
<!--[eoform:custom_field]--> <!--[eoform:custom_field]-->
<div class="box"> <div class="box">
<% case @custom_field.type.to_s <% case @custom_field.type.to_s
when "IssueCustomField" %> when "IssueCustomField" %>
<fieldset><legend><%=l(:label_tracker_plural)%></legend> <fieldset><legend><%=l(:label_tracker_plural)%></legend>
<% for tracker in @trackers %> <% for tracker in @trackers %>
<%= check_box_tag "tracker_ids[]", tracker.id, (@custom_field.trackers.include? tracker) %> <%= tracker.name %> <%= check_box_tag "tracker_ids[]", tracker.id, (@custom_field.trackers.include? tracker) %> <%= tracker.name %>
<% end %> <% end %>
</fieldset> </fieldset>
&nbsp; &nbsp;
<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>
+12 -12
View File
@@ -6,29 +6,29 @@
<li><%= link_to l(:label_project_plural), {}, :id=> "tab-ProjectCustomField", :onclick => "showTab('ProjectCustomField'); this.blur(); return false;" %></li> <li><%= link_to l(:label_project_plural), {}, :id=> "tab-ProjectCustomField", :onclick => "showTab('ProjectCustomField'); this.blur(); return false;" %></li>
<li><%= link_to l(:label_user_plural), {}, :id=> "tab-UserCustomField", :onclick => "showTab('UserCustomField'); this.blur(); return false;" %></li> <li><%= link_to l(:label_user_plural), {}, :id=> "tab-UserCustomField", :onclick => "showTab('UserCustomField'); this.blur(); return false;" %></li>
</ul> </ul>
</div> </div>
<% %w(IssueCustomField ProjectCustomField UserCustomField).each do |type| %> <% %w(IssueCustomField ProjectCustomField UserCustomField).each do |type| %>
<div id="tab-content-<%= type %>" class="tab-content"> <div id="tab-content-<%= type %>" class="tab-content">
<table class="list"> <table class="list">
<thead><tr> <thead><tr>
<th width="30%"><%=l(:field_name)%></th> <th width="30%"><%=l(:field_name)%></th>
<th><%=l(:field_field_format)%></th> <th><%=l(:field_field_format)%></th>
<th><%=l(:field_is_required)%></th> <th><%=l(:field_is_required)%></th>
<% if type == 'IssueCustomField' %> <% if type == 'IssueCustomField' %>
<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 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] || []) %>
<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>
<td align="center"><%= image_tag 'true.png' if custom_field.is_required? %></td> <td align="center"><%= image_tag 'true.png' if custom_field.is_required? %></td>
<% if type == 'IssueCustomField' %> <% if type == 'IssueCustomField' %>
<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"> <td align="center">
+2 -2
View File
@@ -1,3 +1,3 @@
<p><%= link_to h(document.title), :controller => 'documents', :action => 'show', :id => document %><br /> <p><%= link_to h(document.title), :controller => 'documents', :action => 'show', :id => document %><br />
<% unless document.description.empty? %><%=h(truncate(document.description, 250)) %><br /><% end %> <% unless document.description.empty? %><%=h(truncate(document.description, 250)) %><br /><% end %>
<em><%= format_time(document.created_on) %></em></p> <em><%= format_time(document.created_on) %></em></p>
+5 -5
View File
@@ -1,16 +1,16 @@
<%= error_messages_for 'document' %> <%= error_messages_for 'document' %>
<div class="box"> <div class="box">
<!--[form:document]--> <!--[form:document]-->
<p><label for="document_category_id"><%=l(:field_category)%></label> <p><label for="document_category_id"><%=l(:field_category)%></label>
<select name="document[category_id]"> <select name="document[category_id]">
<%= options_from_collection_for_select @categories, "id", "name", @document.category_id %> <%= options_from_collection_for_select @categories, "id", "name", @document.category_id %>
</select></p> </select></p>
<p><label for="document_title"><%=l(:field_title)%> <span class="required">*</span></label> <p><label for="document_title"><%=l(:field_title)%> <span class="required">*</span></label>
<%= text_field 'document', 'title', :size => 60 %></p> <%= text_field 'document', 'title', :size => 60 %></p>
<p><label for="document_description"><%=l(:field_description)%></label> <p><label for="document_description"><%=l(:field_description)%></label>
<%= text_area 'document', 'description', :cols => 60, :rows => 15 %></p> <%= text_area 'document', 'description', :cols => 60, :rows => 15, :class => 'wiki-edit' %></p>
<!--[eoform:document]--> <!--[eoform:document]-->
</div> </div>
+34 -33
View File
@@ -1,37 +1,38 @@
<div class="contextual"> <div class="contextual">
<%= link_to_if_authorized l(:button_edit), {:controller => 'documents', :action => 'edit', :id => @document}, :class => 'icon icon-edit' %> <%= link_to_if_authorized l(:button_edit), {:controller => 'documents', :action => 'edit', :id => @document}, :class => 'icon icon-edit' %>
<%= 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><%= @document.title %></h2>
<p><em><%= @document.category.name %><br /> <p><em><%= @document.category.name %><br />
<%= format_date @document.created_on %></em></p> <%= format_date @document.created_on %></em></p>
<%= textilizable @document.description %> <%= textilizable @document.description %>
<br /> <br />
<h3><%= l(:label_attachment_plural) %></h3> <h3><%= l(:label_attachment_plural) %></h3>
<ul class="documents"> <ul class="documents">
<% for attachment in @attachments %> <% for attachment in @attachments %>
<li> <li>
<div class="contextual"> <div class="contextual">
<%= link_to_if_authorized l(:button_delete), {:controller => 'documents', :action => 'destroy_attachment', :id => @document, :attachment_id => attachment}, :confirm => l(:text_are_you_sure), :method => :post, :class => 'icon icon-del' %> <%= link_to_if_authorized l(:button_delete), {:controller => 'documents', :action => 'destroy_attachment', :id => @document, :attachment_id => attachment}, :confirm => l(:text_are_you_sure), :method => :post, :class => 'icon icon-del' %>
</div> </div>
<%= link_to attachment.filename, :action => 'download', :id => @document, :attachment_id => attachment %> <%= link_to attachment.filename, :action => 'download', :id => @document, :attachment_id => attachment %>
(<%= number_to_human_size attachment.filesize %>)<br /> (<%= number_to_human_size attachment.filesize %>)<br />
<em><%= attachment.author.display_name %>, <%= format_date(attachment.created_on) %></em><br /> <em><%= attachment.author.display_name %>, <%= format_date(attachment.created_on) %></em><br />
<%= lwr(:label_download, attachment.downloads) %> <%= lwr(:label_download, attachment.downloads) %>
</li> </li>
<% end %>
</ul>
<br />
<% if authorize_for('documents', 'add_attachment') %>
<p><%= toggle_link l(:label_attachment_new), "add_attachment_form" %></p>
<% form_tag ({ :controller => 'documents', :action => 'add_attachment', :id => @document }, :multipart => true, :class => "tabular", :id => "add_attachment_form", :style => "display:none;") do %>
<p id="attachments_p"><label for="attachment_file"><%=l(:label_attachment)%>
<%= 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>
<%= submit_tag l(:button_add) %>
<% end %>
<% end %> <% end %>
</ul>
<br />
<% if authorize_for('documents', 'add_attachment') %>
<% form_tag ({ :controller => 'documents', :action => 'add_attachment', :id => @document }, :multipart => true, :class => "tabular") do %>
<p id="attachments_p"><label for="attachment_file"><%=l(:label_attachment)%>
<%= 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>
<%= submit_tag l(:button_add) %>
<% end %>
<% end %>
+16 -16
View File
@@ -1,19 +1,19 @@
<h2><%=l(:label_enumerations)%></h2> <h2><%=l(:label_enumerations)%></h2>
&nbsp; &nbsp;
<% Enumeration::OPTIONS.each do |option, name| %> <% Enumeration::OPTIONS.each do |option, name| %>
<% if params[:opt]==option %> <% if params[:opt]==option %>
<h3><%= l(name) %></h3> <h3><%= l(name) %></h3>
<ul> <ul>
<% for value in Enumeration.find(:all, :conditions => ["opt = ?", option]) %> <% for value in Enumeration.find(:all, :conditions => ["opt = ?", option]) %>
<li><%= link_to value.name, :action => 'edit', :id => value %></li> <li><%= link_to value.name, :action => 'edit', :id => value %></li>
<% end %> <% end %>
</ul> </ul>
<p><%= link_to l(:label_enumeration_new), { :action => 'new', :opt => option }, :class => "icon icon-add" %></p>&nbsp; <p><%= link_to l(:label_enumeration_new), { :action => 'new', :opt => option }, :class => "icon icon-add" %></p>&nbsp;
<% else %> <% else %>
<h3><%= link_to l(name), :opt => option %></h3> <h3><%= link_to l(name), :opt => option %></h3>
<% end %> <% end %>
<% end %> <% end %>

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