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
+5
View File
@@ -51,5 +51,10 @@ class AdminController < ApplicationController
end end
def info def info
@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 end
+18 -2
View File
@@ -17,6 +17,7 @@
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) def logged_in_user=(user)
@logged_in_user = user @logged_in_user = user
@@ -31,6 +32,12 @@ class ApplicationController < ActionController::Base
end end
end end
# Returns the role that the logged in user has on the current project
# or nil if current user is not a member of the project
def logged_in_user_membership
@user_membership ||= logged_in_user.role_for_project(@project)
end
# check if login is globally required to access the application # check if login is globally required to access the application
def check_if_login_required def check_if_login_required
require_login if Setting.login_required? require_login if Setting.login_required?
@@ -81,14 +88,23 @@ class ApplicationController < ActionController::Base
# admin is always authorized # admin is always authorized
return true if self.logged_in_user.admin? return true if self.logged_in_user.admin?
# if not admin, check membership permission # if not admin, check membership permission
@user_membership ||= Member.find(:first, :conditions => ["user_id=? and project_id=?", self.logged_in_user.id, @project.id]) if logged_in_user_membership and Permission.allowed_to_role( "%s/%s" % [ ctrl, action ], logged_in_user_membership )
if @user_membership and Permission.allowed_to_role( "%s/%s" % [ ctrl, action ], @user_membership.role_id )
return true return true
end end
render :nothing => true, :status => 403 render :nothing => true, :status => 403
false false
end end
# make sure that the user is a member of the project (or admin) if project is private
# used as a before_filter for actions that do not require any particular permission on the project
def check_project_privacy
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
+73 -2
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
+2 -5
View File
@@ -33,11 +33,8 @@ class HelpController < ApplicationController
# 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
redirect_to "/manual/#{lang}/index.html"
end
end end
private private
+25 -15
View File
@@ -25,14 +25,14 @@ class IssuesController < ApplicationController
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
@@ -67,9 +67,6 @@ class IssuesController < ApplicationController
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])
#@history.author_id = self.logged_in_user.id if self.logged_in_user
#@history.status = @issue.status
if @issue.save if @issue.save
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?
@@ -82,20 +79,22 @@ class IssuesController < ApplicationController
end end
def change_status def change_status
#@history = @issue.histories.build(params[:history]) @status_options = @issue.status.find_new_statuses_allowed_to(logged_in_user.role_for_project(@project), @issue.tracker) if logged_in_user
@status_options = @issue.status.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
@new_status = IssueStatus.find(params[:new_status_id]) @new_status = IssueStatus.find(params[:new_status_id])
if params[:confirm] if params[:confirm]
begin begin
#@history.author_id = self.logged_in_user.id if self.logged_in_user
#@issue.status = @history.status
#@issue.fixed_version_id = (params[:issue][:fixed_version_id])
#@issue.assigned_to_id = (params[:issue][:assigned_to_id])
#@issue.done_ratio = (params[:issue][:done_ratio])
#@issue.lock_version = (params[:issue][:lock_version])
journal = @issue.init_journal(self.logged_in_user, params[:notes]) journal = @issue.init_journal(self.logged_in_user, params[:notes])
@issue.status = @new_status @issue.status = @new_status
if @issue.update_attributes(params[:issue]) if @issue.update_attributes(params[:issue])
# Save attachments
params[:attachments].each { |file|
next unless file.size > 0
a = Attachment.create(:container => @issue, :file => file, :author => logged_in_user)
journal.details << JournalDetail.new(:property => 'attachment',
:prop_key => a.id,
:value => a.filename) unless a.new_record?
} if params[:attachments] and params[:attachments].is_a? Array
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
@@ -116,17 +115,28 @@ class IssuesController < ApplicationController
def add_attachment def add_attachment
# Save the attachments # Save the attachments
@attachments = [] @attachments = []
journal = @issue.init_journal(self.logged_in_user)
params[:attachments].each { |file| params[:attachments].each { |file|
next unless file.size > 0 next unless file.size > 0
a = Attachment.create(:container => @issue, :file => file, :author => logged_in_user) a = Attachment.create(:container => @issue, :file => file, :author => logged_in_user)
@attachments << a unless a.new_record? @attachments << a unless a.new_record?
journal.details << JournalDetail.new(:property => 'attachment',
:prop_key => a.id,
:value => a.filename) unless a.new_record?
} if params[:attachments] and params[:attachments].is_a? Array } if params[:attachments] and params[:attachments].is_a? Array
journal.save if journal.details.any?
Mailer.deliver_attachments_add(@attachments) if !@attachments.empty? and Permission.find_by_controller_and_action(params[:controller], params[:action]).mail_enabled? 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
def destroy_attachment def destroy_attachment
@issue.attachments.find(params[:attachment_id]).destroy a = @issue.attachments.find(params[:attachment_id])
a.destroy
journal = @issue.init_journal(self.logged_in_user)
journal.details << JournalDetail.new(:property => 'attachment',
:prop_key => a.id,
:old_value => a.filename)
journal.save
redirect_to :action => 'show', :id => @issue redirect_to :action => 'show', :id => @issue
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
+151 -38
View File
@@ -39,14 +39,15 @@ class ProjectsController < ApplicationController
# Lists public projects # Lists public projects
def list def list
sort_init 'name', 'asc' sort_init "#{Project.table_name}.name", "asc"
sort_update sort_update
@project_count = Project.count(:all, :conditions => ["is_public=?", true]) @project_count = Project.count(:all, :conditions => ["is_public=?", true])
@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,
:conditions => ["is_public=?", true], :conditions => ["#{Project.table_name}.is_public=?", true],
:include => :parent,
:limit => @project_pages.items_per_page, :limit => @project_pages.items_per_page,
:offset => @project_pages.current.offset :offset => @project_pages.current.offset
@@ -68,6 +69,10 @@ class ProjectsController < ApplicationController
@project.repository = Repository.new @project.repository = Repository.new
@project.repository.attributes = params[:repository] @project.repository.attributes = params[:repository]
end end
if "1" == params[:wiki_enabled]
@project.wiki = Wiki.new
@project.wiki.attributes = params[:wiki]
end
if @project.save if @project.save
flash[:notice] = l(:notice_successful_create) flash[:notice] = l(:notice_successful_create)
redirect_to :controller => 'admin', :action => 'projects' redirect_to :controller => 'admin', :action => 'projects'
@@ -78,11 +83,11 @@ class ProjectsController < ApplicationController
# Show @project # Show @project
def show def show
@custom_values = @project.custom_values.find(:all, :include => :custom_field) @custom_values = @project.custom_values.find(:all, :include => :custom_field)
@members = @project.members.find(:all, :include => [:user, :role]) @members_by_role = @project.members.find(:all, :include => [:user, :role], :order => 'position').group_by {|m| m.role}
@subprojects = @project.children if @project.children.size > 0 @subprojects = @project.children if @project.children.size > 0
@news = @project.news.find(:all, :limit => 5, :include => [ :author, :project ], :order => "news.created_on DESC") @news = @project.news.find(:all, :limit => 5, :include => [ :author, :project ], :order => "#{News.table_name}.created_on DESC")
@trackers = Tracker.find(:all, :order => 'position') @trackers = Tracker.find(:all, :order => 'position')
@open_issues_by_tracker = Issue.count(:group => :tracker, :joins => "INNER JOIN issue_statuses ON issue_statuses.id = issues.status_id", :conditions => ["project_id=? and issue_statuses.is_closed=?", @project.id, false]) @open_issues_by_tracker = Issue.count(:group => :tracker, :joins => "INNER JOIN #{IssueStatus.table_name} ON #{IssueStatus.table_name}.id = #{Issue.table_name}.status_id", :conditions => ["project_id=? and #{IssueStatus.table_name}.is_closed=?", @project.id, false])
@total_issues_by_tracker = Issue.count(:group => :tracker, :conditions => ["project_id=?", @project.id]) @total_issues_by_tracker = Issue.count(:group => :tracker, :conditions => ["project_id=?", @project.id])
end end
@@ -110,7 +115,16 @@ class ProjectsController < ApplicationController
@project.repository = nil @project.repository = nil
when "1" when "1"
@project.repository ||= Repository.new @project.repository ||= Repository.new
@project.repository.attributes = params[:repository] @project.repository.update_attributes params[:repository]
end
end
if params[:wiki_enabled]
case params[:wiki_enabled]
when "0"
@project.wiki.destroy if @project.wiki
when "1"
@project.wiki ||= Wiki.new
@project.wiki.update_attributes params[:wiki]
end end
end end
@project.attributes = params[:project] @project.attributes = params[:project]
@@ -198,12 +212,20 @@ class ProjectsController < ApplicationController
def add_issue def add_issue
@tracker = Tracker.find(params[:tracker_id]) @tracker = Tracker.find(params[:tracker_id])
@priorities = Enumeration::get_values('IPRI') @priorities = Enumeration::get_values('IPRI')
default_status = IssueStatus.default
@issue = Issue.new(:project => @project, :tracker => @tracker) @issue = Issue.new(:project => @project, :tracker => @tracker)
@issue.status = default_status
@allowed_statuses = default_status.find_new_statuses_allowed_to(logged_in_user.role_for_project(@project), @issue.tracker) if logged_in_user
if request.get? if request.get?
@issue.start_date = Date.today @issue.start_date = Date.today
@custom_values = @project.custom_fields_for_issues(@tracker).collect { |x| CustomValue.new(:custom_field => x, :customized => @issue) } @custom_values = @project.custom_fields_for_issues(@tracker).collect { |x| CustomValue.new(:custom_field => x, :customized => @issue) }
else else
@issue.attributes = params[:issue] @issue.attributes = params[:issue]
requested_status = IssueStatus.find_by_id(params[:issue][:status_id])
@issue.status = (@allowed_statuses.include? requested_status) ? requested_status : default_status
@issue.author_id = self.logged_in_user.id if self.logged_in_user @issue.author_id = self.logged_in_user.id if self.logged_in_user
# Multiple file upload # Multiple file upload
@attachments = [] @attachments = []
@@ -223,7 +245,7 @@ class ProjectsController < ApplicationController
# Show filtered/sorted issues list of @project # Show filtered/sorted issues list of @project
def list_issues def list_issues
sort_init 'issues.id', 'desc' sort_init "#{Issue.table_name}.id", "desc"
sort_update sort_update
retrieve_query retrieve_query
@@ -240,7 +262,7 @@ class ProjectsController < ApplicationController
@issue_count = Issue.count(:include => [:status, :project], :conditions => @query.statement) @issue_count = Issue.count(:include => [:status, :project], :conditions => @query.statement)
@issue_pages = Paginator.new self, @issue_count, @results_per_page, params['page'] @issue_pages = Paginator.new self, @issue_count, @results_per_page, params['page']
@issues = Issue.find :all, :order => sort_clause, @issues = Issue.find :all, :order => sort_clause,
:include => [ :author, :status, :tracker, :project, :priority ], :include => [ :assigned_to, :status, :tracker, :project, :priority ],
:conditions => @query.statement, :conditions => @query.statement,
:limit => @issue_pages.items_per_page, :limit => @issue_pages.items_per_page,
:offset => @issue_pages.current.offset :offset => @issue_pages.current.offset
@@ -251,18 +273,18 @@ class ProjectsController < ApplicationController
# Export filtered/sorted issues list to CSV # Export filtered/sorted issues list to CSV
def export_issues_csv def export_issues_csv
sort_init 'issues.id', 'desc' sort_init "#{Issue.table_name}.id", "desc"
sort_update sort_update
retrieve_query retrieve_query
render :action => 'list_issues' and return unless @query.valid? render :action => 'list_issues' and return unless @query.valid?
@issues = Issue.find :all, :order => sort_clause, @issues = Issue.find :all, :order => sort_clause,
:include => [ :author, :status, :tracker, :priority, {:custom_values => :custom_field} ], :include => [ :assigned_to, :author, :status, :tracker, :priority, {:custom_values => :custom_field} ],
:conditions => @query.statement, :conditions => @query.statement,
:limit => Setting.issues_export_limit :limit => Setting.issues_export_limit
ic = Iconv.new('ISO-8859-1', 'UTF-8') ic = Iconv.new(l(:general_csv_encoding), 'UTF-8')
export = StringIO.new export = StringIO.new
CSV::Writer.generate(export, l(:general_csv_separator)) do |csv| CSV::Writer.generate(export, l(:general_csv_separator)) do |csv|
# csv header fields # csv header fields
@@ -270,6 +292,7 @@ class ProjectsController < ApplicationController
l(:field_tracker), l(:field_tracker),
l(:field_priority), l(:field_priority),
l(:field_subject), l(:field_subject),
l(:field_assigned_to),
l(:field_author), l(:field_author),
l(:field_start_date), l(:field_start_date),
l(:field_due_date), l(:field_due_date),
@@ -287,7 +310,8 @@ class ProjectsController < ApplicationController
issue.tracker.name, issue.tracker.name,
issue.priority.name, issue.priority.name,
issue.subject, issue.subject,
issue.author.display_name, (issue.assigned_to ? issue.assigned_to.name : ""),
issue.author.name,
issue.start_date ? l_date(issue.start_date) : nil, issue.start_date ? l_date(issue.start_date) : nil,
issue.due_date ? l_date(issue.due_date) : nil, issue.due_date ? l_date(issue.due_date) : nil,
issue.done_ratio, issue.done_ratio,
@@ -306,7 +330,7 @@ class ProjectsController < ApplicationController
# Export filtered/sorted issues to PDF # Export filtered/sorted issues to PDF
def export_issues_pdf def export_issues_pdf
sort_init 'issues.id', 'desc' sort_init "#{Issue.table_name}.id", "desc"
sort_update sort_update
retrieve_query retrieve_query
@@ -327,7 +351,7 @@ class ProjectsController < ApplicationController
redirect_to :action => 'list_issues', :id => @project and return unless @issues redirect_to :action => 'list_issues', :id => @project and return unless @issues
@projects = [] @projects = []
# find projects to which the user is allowed to move the issue # find projects to which the user is allowed to move the issue
@logged_in_user.memberships.each {|m| @projects << m.project if Permission.allowed_to_role("projects/move_issues", m.role_id)} @logged_in_user.memberships.each {|m| @projects << m.project if Permission.allowed_to_role("projects/move_issues", m.role)}
# issue can be moved to any tracker # issue can be moved to any tracker
@trackers = Tracker.find(:all) @trackers = Tracker.find(:all)
if request.post? and params[:new_project_id] and params[:new_tracker_id] if request.post? and params[:new_project_id] and params[:new_tracker_id]
@@ -380,7 +404,7 @@ class ProjectsController < ApplicationController
# Show news list of @project # Show news list of @project
def list_news def list_news
@news_pages, @news = paginate :news, :per_page => 10, :conditions => ["project_id=?", @project.id], :include => :author, :order => "news.created_on DESC" @news_pages, @news = paginate :news, :per_page => 10, :conditions => ["project_id=?", @project.id], :include => :author, :order => "#{News.table_name}.created_on DESC"
render :action => "list_news", :layout => false if request.xhr? render :action => "list_news", :layout => false if request.xhr?
end end
@@ -407,31 +431,23 @@ class ProjectsController < ApplicationController
# Show changelog for @project # Show changelog for @project
def changelog def changelog
@trackers = Tracker.find(:all, :conditions => ["is_in_chlog=?", true], :order => 'position') @trackers = Tracker.find(:all, :conditions => ["is_in_chlog=?", true], :order => 'position')
if request.get? retrieve_selected_tracker_ids(@trackers)
@selected_tracker_ids = @trackers.collect {|t| t.id.to_s }
else
@selected_tracker_ids = params[:tracker_ids].collect { |id| id.to_i.to_s } if params[:tracker_ids] and params[:tracker_ids].is_a? Array
end
@selected_tracker_ids ||= []
@fixed_issues = @project.issues.find(:all, @fixed_issues = @project.issues.find(:all,
:include => [ :fixed_version, :status, :tracker ], :include => [ :fixed_version, :status, :tracker ],
:conditions => [ "issue_statuses.is_closed=? and issues.tracker_id in (#{@selected_tracker_ids.join(',')}) and issues.fixed_version_id is not null", true], :conditions => [ "#{IssueStatus.table_name}.is_closed=? and #{Issue.table_name}.tracker_id in (#{@selected_tracker_ids.join(',')}) and #{Issue.table_name}.fixed_version_id is not null", true],
:order => "versions.effective_date DESC, issues.id DESC" :order => "#{Version.table_name}.effective_date DESC, #{Issue.table_name}.id DESC"
) unless @selected_tracker_ids.empty? ) unless @selected_tracker_ids.empty?
@fixed_issues ||= [] @fixed_issues ||= []
end end
def roadmap def roadmap
@trackers = Tracker.find(:all, :conditions => ["is_in_roadmap=?", true], :order => 'position') @trackers = Tracker.find(:all, :conditions => ["is_in_roadmap=?", true], :order => 'position')
if request.get? retrieve_selected_tracker_ids(@trackers)
@selected_tracker_ids = @trackers.collect {|t| t.id.to_s }
else
@selected_tracker_ids = params[:tracker_ids].collect { |id| id.to_i.to_s } if params[:tracker_ids] and params[:tracker_ids].is_a? Array
end
@selected_tracker_ids ||= []
@versions = @project.versions.find(:all, @versions = @project.versions.find(:all,
:conditions => [ "versions.effective_date>?", Date.today], :conditions => [ "#{Version.table_name}.effective_date>?", Date.today],
:order => "versions.effective_date ASC" :order => "#{Version.table_name}.effective_date ASC"
) )
end end
@@ -451,7 +467,7 @@ class ProjectsController < ApplicationController
@events_by_day = {} @events_by_day = {}
unless params[:show_issues] == "0" unless params[:show_issues] == "0"
@project.issues.find(:all, :include => [:author, :status], :conditions => ["issues.created_on>=? and issues.created_on<=?", @date_from, @date_to] ).each { |i| @project.issues.find(:all, :include => [:author], :conditions => ["#{Issue.table_name}.created_on>=? and #{Issue.table_name}.created_on<=?", @date_from, @date_to] ).each { |i|
@events_by_day[i.created_on.to_date] ||= [] @events_by_day[i.created_on.to_date] ||= []
@events_by_day[i.created_on.to_date] << i @events_by_day[i.created_on.to_date] << i
} }
@@ -459,7 +475,7 @@ class ProjectsController < ApplicationController
end end
unless params[:show_news] == "0" unless params[:show_news] == "0"
@project.news.find(:all, :conditions => ["news.created_on>=? and news.created_on<=?", @date_from, @date_to], :include => :author ).each { |i| @project.news.find(:all, :conditions => ["#{News.table_name}.created_on>=? and #{News.table_name}.created_on<=?", @date_from, @date_to], :include => :author ).each { |i|
@events_by_day[i.created_on.to_date] ||= [] @events_by_day[i.created_on.to_date] ||= []
@events_by_day[i.created_on.to_date] << i @events_by_day[i.created_on.to_date] << i
} }
@@ -467,7 +483,7 @@ class ProjectsController < ApplicationController
end end
unless params[:show_files] == "0" unless params[:show_files] == "0"
Attachment.find(:all, :select => "attachments.*", :joins => "LEFT JOIN versions ON versions.id = attachments.container_id", :conditions => ["attachments.container_type='Version' and versions.project_id=? and attachments.created_on>=? and attachments.created_on<=?", @project.id, @date_from, @date_to], :include => :author ).each { |i| Attachment.find(:all, :select => "#{Attachment.table_name}.*", :joins => "LEFT JOIN #{Version.table_name} ON #{Version.table_name}.id = #{Attachment.table_name}.container_id", :conditions => ["#{Attachment.table_name}.container_type='Version' and #{Version.table_name}.project_id=? and #{Attachment.table_name}.created_on>=? and #{Attachment.table_name}.created_on<=?", @project.id, @date_from, @date_to], :include => :author ).each { |i|
@events_by_day[i.created_on.to_date] ||= [] @events_by_day[i.created_on.to_date] ||= []
@events_by_day[i.created_on.to_date] << i @events_by_day[i.created_on.to_date] << i
} }
@@ -475,21 +491,55 @@ class ProjectsController < ApplicationController
end end
unless params[:show_documents] == "0" unless params[:show_documents] == "0"
@project.documents.find(:all, :conditions => ["documents.created_on>=? and documents.created_on<=?", @date_from, @date_to] ).each { |i| @project.documents.find(:all, :conditions => ["#{Document.table_name}.created_on>=? and #{Document.table_name}.created_on<=?", @date_from, @date_to] ).each { |i|
@events_by_day[i.created_on.to_date] ||= [] @events_by_day[i.created_on.to_date] ||= []
@events_by_day[i.created_on.to_date] << i @events_by_day[i.created_on.to_date] << i
} }
Attachment.find(:all, :select => "attachments.*", :joins => "LEFT JOIN documents ON documents.id = attachments.container_id", :conditions => ["attachments.container_type='Document' and documents.project_id=? and attachments.created_on>=? and attachments.created_on<=?", @project.id, @date_from, @date_to], :include => :author ).each { |i| Attachment.find(:all, :select => "attachments.*", :joins => "LEFT JOIN #{Document.table_name} ON #{Document.table_name}.id = #{Attachment.table_name}.container_id", :conditions => ["#{Attachment.table_name}.container_type='Document' and #{Document.table_name}.project_id=? and #{Attachment.table_name}.created_on>=? and #{Attachment.table_name}.created_on<=?", @project.id, @date_from, @date_to], :include => :author ).each { |i|
@events_by_day[i.created_on.to_date] ||= [] @events_by_day[i.created_on.to_date] ||= []
@events_by_day[i.created_on.to_date] << i @events_by_day[i.created_on.to_date] << i
} }
@show_documents = 1 @show_documents = 1
end end
unless params[:show_wiki_edits] == "0"
select = "#{WikiContent.versioned_table_name}.updated_on, #{WikiContent.versioned_table_name}.comment, " +
"#{WikiContent.versioned_table_name}.#{WikiContent.version_column}, #{WikiPage.table_name}.title"
joins = "LEFT JOIN #{WikiPage.table_name} ON #{WikiPage.table_name}.id = #{WikiContent.versioned_table_name}.page_id " +
"LEFT JOIN #{Wiki.table_name} ON #{Wiki.table_name}.id = #{WikiPage.table_name}.wiki_id "
conditions = ["#{Wiki.table_name}.project_id = ? AND #{WikiContent.versioned_table_name}.updated_on BETWEEN ? AND ?",
@project.id, @date_from, @date_to]
WikiContent.versioned_class.find(:all, :select => select, :joins => joins, :conditions => conditions).each { |i|
# We provide this alias so all events can be treated in the same manner
def i.created_on
self.updated_on
end
@events_by_day[i.created_on.to_date] ||= []
@events_by_day[i.created_on.to_date] << i
}
@show_wiki_edits = 1
end
unless @project.repository.nil? || params[:show_changesets] == "0"
@project.repository.changesets.find(:all, :conditions => ["#{Changeset.table_name}.committed_on BETWEEN ? AND ?", @date_from, @date_to]).each { |i|
def i.created_on
self.committed_on
end
@events_by_day[i.created_on.to_date] ||= []
@events_by_day[i.created_on.to_date] << i
}
@show_changesets = 1
end
render :layout => false if request.xhr? render :layout => false if request.xhr?
end end
def calendar def calendar
@trackers = Tracker.find(:all, :order => 'position')
retrieve_selected_tracker_ids(@trackers)
if params[:year] and params[:year].to_i > 1900 if params[:year] and params[:year].to_i > 1900
@year = params[:year].to_i @year = params[:year].to_i
if params[:month] and params[:month].to_i > 0 and params[:month].to_i < 13 if params[:month] and params[:month].to_i > 0 and params[:month].to_i < 13
@@ -506,11 +556,25 @@ class ProjectsController < ApplicationController
# finish on sunday # finish on sunday
@date_to = @date_to + (7-@date_to.cwday) @date_to = @date_to + (7-@date_to.cwday)
@issues = @project.issues.find(:all, :include => [:tracker, :status, :assigned_to, :priority], :conditions => ["((start_date>=? and start_date<=?) or (due_date>=? and due_date<=?))", @date_from, @date_to, @date_from, @date_to]) @events = []
@project.issues_with_subprojects(params[:with_subprojects]) do
@events += Issue.find(:all,
:include => [:tracker, :status, :assigned_to, :priority],
:conditions => ["((start_date>=? and start_date<=?) or (due_date>=? and due_date<=?)) and #{Issue.table_name}.tracker_id in (#{@selected_tracker_ids.join(',')})", @date_from, @date_to, @date_from, @date_to]
) unless @selected_tracker_ids.empty?
end
@events += @project.versions.find(:all, :conditions => ["effective_date BETWEEN ? AND ?", @date_from, @date_to])
@ending_events_by_days = @events.group_by {|event| event.due_date}
@starting_events_by_days = @events.group_by {|event| event.start_date}
render :layout => false if request.xhr? render :layout => false if request.xhr?
end end
def gantt def gantt
@trackers = Tracker.find(:all, :order => 'position')
retrieve_selected_tracker_ids(@trackers)
if params[:year] and params[:year].to_i >0 if params[:year] and params[:year].to_i >0
@year_from = params[:year].to_i @year_from = params[:year].to_i
if params[:month] and params[:month].to_i >=1 and params[:month].to_i <= 12 if params[:month] and params[:month].to_i >=1 and params[:month].to_i <= 12
@@ -528,7 +592,17 @@ class ProjectsController < ApplicationController
@date_from = Date.civil(@year_from, @month_from, 1) @date_from = Date.civil(@year_from, @month_from, 1)
@date_to = (@date_from >> @months) - 1 @date_to = (@date_from >> @months) - 1
@issues = @project.issues.find(:all, :order => "start_date, due_date", :include => [:tracker, :status, :assigned_to, :priority], :conditions => ["(((start_date>=? and start_date<=?) or (due_date>=? and due_date<=?) or (start_date<? and due_date>?)) and start_date is not null and due_date is not null)", @date_from, @date_to, @date_from, @date_to, @date_from, @date_to])
@events = []
@project.issues_with_subprojects(params[:with_subprojects]) do
@events += Issue.find(:all,
:order => "start_date, due_date",
:include => [:tracker, :status, :assigned_to, :priority],
:conditions => ["(((start_date>=? and start_date<=?) or (due_date>=? and due_date<=?) or (start_date<? and due_date>?)) and start_date is not null and due_date is not null and #{Issue.table_name}.tracker_id in (#{@selected_tracker_ids.join(',')}))", @date_from, @date_to, @date_from, @date_to, @date_from, @date_to]
) unless @selected_tracker_ids.empty?
end
@events += @project.versions.find(:all, :conditions => ["effective_date BETWEEN ? AND ?", @date_from, @date_to])
@events.sort! {|x,y| x.start_date <=> y.start_date }
if params[:output]=='pdf' if params[:output]=='pdf'
@options_for_rfpdf ||= {} @options_for_rfpdf ||= {}
@@ -539,6 +613,37 @@ class ProjectsController < ApplicationController
end end
end end
def search
@question = params[:q] || ""
@question.strip!
@all_words = params[:all_words] || (params[:submit] ? false : true)
@scope = params[:scope] || (params[:submit] ? [] : %w(issues changesets news documents wiki) )
# tokens must be at least 3 character long
@tokens = @question.split.uniq.select {|w| w.length > 2 }
if !@tokens.empty?
# no more than 5 tokens to search for
@tokens.slice! 5..-1 if @tokens.size > 5
# strings used in sql like statement
like_tokens = @tokens.collect {|w| "%#{w}%"}
operator = @all_words ? " AND " : " OR "
limit = 10
@results = []
@results += @project.issues.find(:all, :limit => limit, :include => :author, :conditions => [ (["(LOWER(subject) like ? OR LOWER(description) like ?)"] * like_tokens.size).join(operator), * (like_tokens * 2).sort] ) if @scope.include? 'issues'
@results += @project.news.find(:all, :limit => limit, :conditions => [ (["(LOWER(title) like ? OR LOWER(description) like ?)"] * like_tokens.size).join(operator), * (like_tokens * 2).sort], :include => :author ) if @scope.include? 'news'
@results += @project.documents.find(:all, :limit => limit, :conditions => [ (["(LOWER(title) like ? OR LOWER(description) like ?)"] * like_tokens.size).join(operator), * (like_tokens * 2).sort] ) if @scope.include? 'documents'
@results += @project.wiki.pages.find(:all, :limit => limit, :include => :content, :conditions => [ (["(LOWER(title) like ? OR LOWER(text) like ?)"] * like_tokens.size).join(operator), * (like_tokens * 2).sort] ) if @project.wiki && @scope.include?('wiki')
@results += @project.repository.changesets.find(:all, :limit => limit, :conditions => [ (["(LOWER(comment) like ?)"] * like_tokens.size).join(operator), * (like_tokens).sort] ) if @project.repository && @scope.include?('changesets')
@question = @tokens.join(" ")
else
@question = ""
end
end
def feeds
@queries = @project.queries.find :all, :conditions => ["is_public=? or user_id=?", true, (logged_in_user ? logged_in_user.id : 0)]
@key = logged_in_user.get_or_create_rss_key.value if logged_in_user
end
private private
# Find project of id params[:id] # Find project of id params[:id]
# if not found, redirect to project list # if not found, redirect to project list
@@ -550,6 +655,14 @@ private
render_404 render_404
end end
def retrieve_selected_tracker_ids(selectable_trackers)
if ids = params[:tracker_ids]
@selected_tracker_ids = (ids.is_a? Array) ? ids.collect { |id| id.to_i.to_s } : ids.split('/').collect { |id| id.to_i.to_s }
else
@selected_tracker_ids = selectable_trackers.collect {|t| t.id.to_s }
end
end
# Retrieve query from session or build a new query # Retrieve query from session or build a new query
def retrieve_query def retrieve_query
if params[:query_id] if params[:query_id]
+28 -4
View File
@@ -47,16 +47,25 @@ class ReportsController < ApplicationController
@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"
when "subproject"
@field = "project_id"
@rows = @project.children
@data = issues_by_subproject
@report_title = l(:field_subproject)
render :template => "reports/issue_report_details"
else else
@queries = @project.queries.find :all, :conditions => ["is_public=? or user_id=?", true, (logged_in_user ? logged_in_user.id : 0)] @queries = @project.queries.find :all, :conditions => ["is_public=? or user_id=?", true, (logged_in_user ? logged_in_user.id : 0)]
@trackers = Tracker.find(:all, :order => 'position') @trackers = Tracker.find(:all, :order => 'position')
@priorities = Enumeration::get_values('IPRI') @priorities = Enumeration::get_values('IPRI')
@categories = @project.issue_categories @categories = @project.issue_categories
@authors = @project.members.collect { |m| m.user } @authors = @project.members.collect { |m| m.user }
@subprojects = @project.children
issues_by_tracker issues_by_tracker
issues_by_priority issues_by_priority
issues_by_category issues_by_category
issues_by_author issues_by_author
issues_by_subproject
@total_hours = @project.time_entries.sum(:hours)
render :template => "reports/issue_report" render :template => "reports/issue_report"
end end
end end
@@ -112,7 +121,7 @@ private
t.id as tracker_id, t.id as tracker_id,
count(i.id) as total count(i.id) as total
from from
issues i, issue_statuses s, trackers t #{Issue.table_name} i, #{IssueStatus.table_name} s, #{Tracker.table_name} t
where where
i.status_id=s.id i.status_id=s.id
and i.tracker_id=t.id and i.tracker_id=t.id
@@ -127,7 +136,7 @@ private
p.id as priority_id, p.id as priority_id,
count(i.id) as total count(i.id) as total
from from
issues i, issue_statuses s, enumerations p #{Issue.table_name} i, #{IssueStatus.table_name} s, #{Enumeration.table_name} p
where where
i.status_id=s.id i.status_id=s.id
and i.priority_id=p.id and i.priority_id=p.id
@@ -142,7 +151,7 @@ private
c.id as category_id, c.id as category_id,
count(i.id) as total count(i.id) as total
from from
issues i, issue_statuses s, issue_categories c #{Issue.table_name} i, #{IssueStatus.table_name} s, #{IssueCategory.table_name} c
where where
i.status_id=s.id i.status_id=s.id
and i.category_id=c.id and i.category_id=c.id
@@ -157,11 +166,26 @@ private
a.id as author_id, a.id as author_id,
count(i.id) as total count(i.id) as total
from from
issues i, issue_statuses s, users a #{Issue.table_name} i, #{IssueStatus.table_name} s, #{User.table_name} a
where where
i.status_id=s.id i.status_id=s.id
and i.author_id=a.id and i.author_id=a.id
and i.project_id=#{@project.id} and i.project_id=#{@project.id}
group by s.id, s.is_closed, a.id") group by s.id, s.is_closed, a.id")
end 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
+144 -10
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
+14 -1
View File
@@ -28,7 +28,7 @@ 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
@@ -99,4 +99,17 @@ class RolesController < ApplicationController
@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 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
+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
+7
View File
@@ -35,9 +35,16 @@ class TrackersController < ApplicationController
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
+7 -1
View File
@@ -32,11 +32,17 @@ class UsersController < ApplicationController
def list def list
sort_init 'login', 'asc' sort_init 'login', 'asc'
sort_update sort_update
@user_count = User.count
@status = params[:status] ? params[:status].to_i : 1
conditions = nil
conditions = ["status=?", @status] unless @status == 0
@user_count = User.count(:conditions => conditions)
@user_pages = Paginator.new self, @user_count, @user_pages = Paginator.new self, @user_count,
15, 15,
params['page'] params['page']
@users = User.find :all,:order => sort_clause, @users = User.find :all,:order => sort_clause,
:conditions => conditions,
:limit => @user_pages.items_per_page, :limit => @user_pages.items_per_page,
:offset => @user_pages.current.offset :offset => @user_pages.current.offset
+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
+58 -10
View File
@@ -34,7 +34,7 @@ module ApplicationHelper
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
@@ -50,6 +50,17 @@ module ApplicationHelper
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 link_to_issue(issue)
link_to "#{issue.tracker.name} ##{issue.id}", :controller => "issues", :action => "show", :id => issue
end
def toggle_link(name, id, options={})
onclick = "Element.toggle('#{id}'); "
onclick << (options[:focus] ? "Form.Element.focus('#{options[:focus]}'); " : "this.blur(); ")
onclick << "return false;"
link_to(name, "#", :onclick => onclick)
end
def image_to_function(name, function, html_options = {}) def image_to_function(name, function, html_options = {})
html_options.symbolize_keys! html_options.symbolize_keys!
tag(:input, html_options.merge({ tag(:input, html_options.merge({
@@ -63,7 +74,7 @@ module ApplicationHelper
end end
def format_time(time) def format_time(time)
l_datetime(time) if time l_datetime((time.is_a? String) ? time.to_time : time) if time
end end
def day_name(day) def day_name(day)
@@ -77,23 +88,58 @@ module ApplicationHelper
def pagination_links_full(paginator, options={}, html_options={}) def pagination_links_full(paginator, options={}, html_options={})
html = '' html = ''
html << link_to_remote(('&#171; ' + l(:label_previous)), html << link_to_remote(('&#171; ' + l(:label_previous)),
{:update => "content", :url => { :page => paginator.current.previous }}, {:update => "content", :url => options.merge(:page => paginator.current.previous)},
{:href => url_for(:action => 'list', :params => params.merge({:page => paginator.current.previous}))}) + ' ' if paginator.current.previous {:href => url_for(:params => options.merge(:page => paginator.current.previous))}) + ' ' if paginator.current.previous
html << (pagination_links_each(paginator, options) do |n| html << (pagination_links_each(paginator, options) do |n|
link_to_remote(n.to_s, link_to_remote(n.to_s,
{:url => {:action => 'list', :params => params.merge({:page => n})}, :update => 'content'}, {:url => {:params => options.merge(:page => n)}, :update => 'content'},
{:href => url_for(:action => 'list', :params => params.merge({:page => n}))}) {:href => url_for(:params => options.merge(:page => n))})
end || '') end || '')
html << ' ' + link_to_remote((l(:label_next) + ' &#187;'), html << ' ' + link_to_remote((l(:label_next) + ' &#187;'),
{:update => "content", :url => { :page => paginator.current.next }}, {:update => "content", :url => options.merge(:page => paginator.current.next)},
{:href => url_for(:action => 'list', :params => params.merge({:page => paginator.current.next}))}) if paginator.current.next {:href => url_for(:params => options.merge(:page => paginator.current.next))}) if paginator.current.next
html html
end end
def textilizable(text) # textilize text according to system settings and RedCloth availability
(Setting.text_formatting == 'textile') && (ActionView::Helpers::TextHelper.method_defined? "textilize") ? RedCloth.new(h(text)).to_html : simple_format(auto_link(h(text))) def textilizable(text, options = {})
# different methods for formatting wiki links
case options[:wiki_links]
when :local
# used for local links to html files
format_wiki_link = Proc.new {|title| "#{title}.html" }
when :anchor
# used for single-file wiki export
format_wiki_link = Proc.new {|title| "##{title}" }
else
if @project
format_wiki_link = Proc.new {|title| url_for :controller => 'wiki', :action => 'index', :id => @project, :page => title }
else
format_wiki_link = Proc.new {|title| title }
end
end
# turn wiki links into textile links:
# example:
# [[link]] -> "link":link
# [[link|title]] -> "title":link
text = text.gsub(/\[\[([^\]\|]+)(\|([^\]\|]+))?\]\]/) {|m| "\"#{$3 || $1}\":" + format_wiki_link.call(Wiki.titleize($1)) }
# turn issue ids to textile links
# example:
# #52 -> "#52":/issues/show/52
text = text.gsub(/#(\d+)(?=\b)/) {|m| "\"##{$1}\":" + url_for(:controller => 'issues', :action => 'show', :id => $1) }
# turn revision ids to textile links (@project needed)
# example:
# 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
# finally textilize text
@do_textilize ||= (Setting.text_formatting == 'textile') && (ActionView::Helpers::TextHelper.method_defined? "textilize")
text = @do_textilize ? auto_link(RedCloth.new(text).to_html) : simple_format(auto_link(h(text)))
end end
def error_messages_for(object_name, options = {}) def error_messages_for(object_name, options = {})
@@ -104,6 +150,7 @@ module ApplicationHelper
full_messages = [] full_messages = []
object.errors.each do |attr, msg| object.errors.each do |attr, msg|
next if msg.nil? next if msg.nil?
msg = msg.first if msg.is_a? Array
if attr == "base" if attr == "base"
full_messages << l(msg) full_messages << l(msg)
else else
@@ -115,6 +162,7 @@ module ApplicationHelper
object.custom_values.each do |v| object.custom_values.each do |v|
v.errors.each do |attr, msg| v.errors.each do |attr, msg|
next if msg.nil? next if msg.nil?
msg = msg.first if msg.is_a? Array
full_messages << "&#171; " + v.custom_field.name + " &#187; " + l(msg) full_messages << "&#171; " + v.custom_field.name + " &#187; " + l(msg)
end end
end end
+27 -5
View File
@@ -16,20 +16,42 @@
# 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
include GLoc
attr_accessor :footer_date attr_accessor :footer_date
def initialize def initialize(lang)
super super()
set_language_if_valid lang
case current_language
when :ja
extend(PDF_Japanese)
AddSJISFont()
@font_for_content = 'SJIS'
@font_for_footer = 'SJIS'
when :zh
extend(PDF_Chinese)
AddBig5Font()
@font_for_content = 'Big5'
@font_for_footer = 'Big5'
else
@font_for_content = 'Arial'
@font_for_footer = 'Helvetica'
end
SetCreator("redMine #{Redmine::VERSION}") SetCreator("redMine #{Redmine::VERSION}")
SetFont(@font_for_content)
end
def SetFontStyle(style, size)
SetFont(@font_for_content, style, size)
end end
def Cell(w,h=0,txt='',border=0,ln=0,align='',fill=0,link='') def Cell(w,h=0,txt='',border=0,ln=0,align='',fill=0,link='')
@ic ||= Iconv.new('ISO-8859-1', 'UTF-8') @ic ||= Iconv.new(l(:general_pdf_encoding), 'UTF-8')
txt = begin txt = begin
@ic.iconv(txt) @ic.iconv(txt)
rescue rescue
@@ -39,7 +61,7 @@ module IfpdfHelper
end end
def Footer def Footer
SetFont('Helvetica', 'I', 8) SetFont(@font_for_footer, 'I', 8)
SetY(-15) SetY(-15)
SetX(15) SetX(15)
Cell(0, 5, @footer_date, 0, 0, 'L') Cell(0, 5, @footer_date, 0, 0, 'L')
+17 -5
View File
@@ -48,6 +48,8 @@ module IssuesHelper
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
when 'attachment'
label = l(:label_attachment)
end end
label ||= detail.prop_key label ||= detail.prop_key
@@ -62,13 +64,23 @@ module IssuesHelper
end end
if detail.value and !detail.value.to_s.empty? if detail.value and !detail.value.to_s.empty?
if old_value case detail.property
label + " " + l(:text_journal_changed, old_value, value) when 'attr', 'cf'
else if old_value
label + " " + l(:text_journal_set_to, value) label + " " + l(:text_journal_changed, old_value, value)
else
label + " " + l(:text_journal_set_to, value)
end
when 'attachment'
"#{label} #{value} #{l(:label_added)}"
end end
else else
label + " " + l(:text_journal_deleted) + " (#{old_value})" 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 end
+10
View File
@@ -16,4 +16,14 @@
# 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
+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
+6
View File
@@ -16,4 +16,10 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
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
+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
+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
+4 -1
View File
@@ -24,7 +24,8 @@ class Enumeration < ActiveRecord::Base
OPTIONS = { OPTIONS = {
"IPRI" => :enumeration_issue_priorities, "IPRI" => :enumeration_issue_priorities,
"DCAT" => :enumeration_doc_categories "DCAT" => :enumeration_doc_categories,
"ACTI" => :enumeration_activities
}.freeze }.freeze
def self.get_values(option) def self.get_values(option)
@@ -42,6 +43,8 @@ private
raise "Can't delete enumeration" if Issue.find(:first, :conditions => ["priority_id=?", self.id]) raise "Can't delete enumeration" if Issue.find(:first, :conditions => ["priority_id=?", self.id])
when "DCAT" when "DCAT"
raise "Can't delete enumeration" if Document.find(:first, :conditions => ["category_id=?", self.id]) 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 end
+6 -2
View File
@@ -28,7 +28,7 @@ class Issue < ActiveRecord::Base
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
@@ -38,7 +38,7 @@ class Issue < ActiveRecord::Base
# 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
@@ -92,6 +92,10 @@ class Issue < ActiveRecord::Base
@current_journal @current_journal
end end
def spent_hours
@spent_hours ||= time_entries.sum(:hours) || 0
end
private private
# Creates an history for the issue # Creates an history for the issue
#def build_history #def build_history
+2 -2
View File
@@ -16,8 +16,8 @@
# 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
+14 -5
View File
@@ -36,12 +36,21 @@ class IssueStatus < ActiveRecord::Base
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
# Uses association cache when called more than one time
def new_statuses_allowed_to(role, tracker) def new_statuses_allowed_to(role, tracker)
statuses = [] new_statuses = [self]
for workflow in self.workflows new_statuses += workflows.select {|w| w.role_id == role.id && w.tracker_id == tracker.id}.collect{|w| w.new_status} if role && tracker
statuses << workflow.new_status if workflow.role_id == role.id and workflow.tracker_id == tracker.id new_statuses.sort{|x, y| x.position <=> y.position }
end unless role.nil? or tracker.nil? end
statuses
# Same thing as above but uses a database query
# More efficient than the previous method if called just once
def find_new_statuses_allowed_to(role, tracker)
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 end
private private
+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
+1 -1
View File
@@ -24,6 +24,6 @@ class News < ActiveRecord::Base
# 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
+3 -2
View File
@@ -30,7 +30,8 @@ class Permission < ActiveRecord::Base
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,
1500 => :label_time_tracking
}.freeze }.freeze
@@cached_perms_for_public = nil @@cached_perms_for_public = nil
@@ -56,7 +57,7 @@ class Permission < ActiveRecord::Base
find(:all, :include => :roles).each {|p| perms.store "#{p.controller}/#{p.action}", p.roles.collect {|r| r.id } } find(:all, :include => :roles).each {|p| perms.store "#{p.controller}/#{p.action}", p.roles.collect {|r| r.id } }
perms perms
end end
allowed_to_public(action) or (@@cached_perms_for_roles[action] and @@cached_perms_for_roles[action].include? role) allowed_to_public(action) or (role && @@cached_perms_for_roles[action] && @@cached_perms_for_roles[action].include?(role.id))
end end
def self.allowed_to_role_expired def self.allowed_to_role_expired
+40 -16
View File
@@ -16,50 +16,74 @@
# 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 :time_entries, :dependent => :delete_all
has_many :queries, :dependent => :delete_all has_many :queries, :dependent => :delete_all
has_many :documents, :dependent => :destroy has_many :documents, :dependent => :destroy
has_many :news, :dependent => :delete_all, :include => :author has_many :news, :dependent => :delete_all, :include => :author
has_many :issue_categories, :dependent => :delete_all, :order => "issue_categories.name" has_many :issue_categories, :dependent => :delete_all, :order => "#{IssueCategory.table_name}.name"
has_one :repository, :dependent => :destroy has_one :repository, :dependent => :destroy
has_and_belongs_to_many :custom_fields, :class_name => 'IssueCustomField', :join_table => 'custom_fields_projects', :association_foreign_key => 'custom_field_id' 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'
acts_as_tree :order => "name", :counter_cache => true acts_as_tree :order => "name", :counter_cache => true
validates_presence_of :name, :description validates_presence_of :name, :description, :identifier
validates_uniqueness_of :name validates_uniqueness_of :name, :identifier
validates_associated :custom_values, :on => :update validates_associated :custom_values, :on => :update
validates_associated :repository validates_associated :repository, :wiki
validates_length_of :name, :maximum => 30
validates_format_of :name, :with => /^[\w\s\'\-]*$/i validates_format_of :name, :with => /^[\w\s\'\-]*$/i
validates_length_of :description, :maximum => 255
validates_length_of :identifier, :in => 3..12
validates_format_of :identifier, :with => /^[a-z0-9\-]*$/
def identifier=(identifier)
super unless identifier_frozen?
end
def identifier_frozen?
errors[:identifier].nil? && !(new_record? || identifier.blank?)
end
def issues_with_subprojects(include_subprojects=false)
conditions = nil
if include_subprojects && children.size > 0
ids = [id] + children.collect {|c| c.id}
conditions = ["#{Issue.table_name}.project_id IN (#{ids.join(',')})"]
else
conditions = ["#{Issue.table_name}.project_id = ?", id]
end
Issue.with_scope :find => { :conditions => conditions } do
yield
end
end
# returns latest created projects # returns latest created projects
# non public projects will be returned only if user is a member of those # non public projects will be returned only if user is a member of those
def self.latest(user=nil, count=5) def self.latest(user=nil, count=5)
find(:all, :limit => count, :conditions => visible_by(user), :order => "projects.created_on DESC") find(:all, :limit => count, :conditions => visible_by(user), :order => "created_on DESC")
end end
def self.visible_by(user=nil) def self.visible_by(user=nil)
if user && !user.memberships.empty? if user && !user.memberships.empty?
return ["projects.is_public = ? or projects.id IN (#{user.memberships.collect{|m| m.project_id}.join(',')})", true] return ["#{Project.table_name}.is_public = ? or #{Project.table_name}.id IN (#{user.memberships.collect{|m| m.project_id}.join(',')})", true]
else else
return ["projects.is_public = ?", true] return ["#{Project.table_name}.is_public = ?", true]
end end
end end
# Returns an array of all custom fields enabled for project issues # Returns an array of all custom fields enabled for project issues
# (explictly associated custom fields and custom fields enabled for all projects) # (explictly associated custom fields and custom fields enabled for all projects)
def custom_fields_for_issues(tracker) def custom_fields_for_issues(tracker)
tracker.custom_fields.find(:all, :include => :projects, all_custom_fields.select {|c| tracker.custom_fields.include? c }
:conditions => ["is_for_all=? or project_id=?", true, self.id])
#(CustomField.for_all + custom_fields).uniq
end end
def all_custom_fields def all_custom_fields
@all_custom_fields ||= IssueCustomField.find(:all, :include => :projects, @all_custom_fields ||= (IssueCustomField.for_all + custom_fields).uniq
:conditions => ["is_for_all=? or project_id=?", true, self.id])
end end
protected protected
+31 -16
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
@@ -124,42 +128,53 @@ class Query < ActiveRecord::Base
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
+4
View File
@@ -26,6 +26,10 @@ class Role < ActiveRecord::Base
validates_uniqueness_of :name validates_uniqueness_of :name
validates_format_of :name, :with => /^[\w\s\'\-]*$/i validates_format_of :name, :with => /^[\w\s\'\-]*$/i
def <=>(role)
position <=> role.position
end
private private
def check_integrity def check_integrity
raise "Can't delete role" if Member.find(:first, :conditions =>["role_id=?", self.id]) raise "Can't delete role" if Member.find(:first, :conditions =>["role_id=?", self.id])
+231 -14
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,6 +16,7 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require 'rexml/document' require 'rexml/document'
require 'cgi'
module SvnRepos module SvnRepos
@@ -23,14 +24,45 @@ module SvnRepos
end end
class Base class Base
@url = nil
@login = nil
@password = nil
def initialize(url, login=nil, password=nil) def initialize(url, root_url=nil, login=nil, password=nil)
@url = url @url = url
@login = login if login && !login.empty? @login = login if login && !login.empty?
@password = (password || "") if @login @password = (password || "") if @login
@root_url = root_url.blank? ? retrieve_root_url : root_url
end
def root_url
@root_url
end
def url
@url
end
# get info about the svn repository
def info
cmd = "svn info --xml #{target('')}"
cmd << " --username #{@login} --password #{@password}" if @login
info = nil
shellout(cmd) do |io|
begin
doc = REXML::Document.new(io)
#root_url = doc.elements["info/entry/repository/root"].text
info = Info.new({:root_url => doc.elements["info/entry/repository/root"].text,
:lastrev => Revision.new({
:identifier => doc.elements["info/entry/commit"].attributes['revision'],
:time => Time.parse(doc.elements["info/entry/commit/date"].text),
:author => (doc.elements["info/entry/commit/author"] ? doc.elements["info/entry/commit/author"].text : "")
})
})
rescue
end
end
return nil if $? && $?.exitstatus != 0
info
rescue Errno::ENOENT => e
return nil
end end
# Returns the entry identified by path and revision identifier # Returns the entry identified by path and revision identifier
@@ -47,6 +79,7 @@ module SvnRepos
identifier = 'HEAD' unless identifier and identifier > 0 identifier = 'HEAD' unless identifier and identifier > 0
entries = Entries.new entries = Entries.new
cmd = "svn list --xml #{target(path)}@#{identifier}" cmd = "svn list --xml #{target(path)}@#{identifier}"
cmd << " --username #{@login} --password #{@password}" if @login
shellout(cmd) do |io| shellout(cmd) do |io|
begin begin
doc = REXML::Document.new(io) doc = REXML::Document.new(io)
@@ -58,7 +91,7 @@ module SvnRepos
:lastrev => Revision.new({ :lastrev => Revision.new({
:identifier => entry.elements['commit'].attributes['revision'], :identifier => entry.elements['commit'].attributes['revision'],
:time => Time.parse(entry.elements['commit'].elements['date'].text), :time => Time.parse(entry.elements['commit'].elements['date'].text),
:author => (entry.elements['commit'].elements['author'] ? entry.elements['commit'].elements['author'].text : "anonymous") :author => (entry.elements['commit'].elements['author'] ? entry.elements['commit'].elements['author'].text : "")
}) })
}) })
end end
@@ -76,8 +109,9 @@ module SvnRepos
identifier_from = 'HEAD' unless identifier_from and identifier_from.to_i > 0 identifier_from = 'HEAD' unless identifier_from and identifier_from.to_i > 0
identifier_to = 1 unless identifier_to and identifier_to.to_i > 0 identifier_to = 1 unless identifier_to and identifier_to.to_i > 0
revisions = Revisions.new revisions = Revisions.new
cmd = "svn log --xml -r #{identifier_from}:#{identifier_to} " cmd = "svn log --xml -r #{identifier_from}:#{identifier_to}"
cmd << "--verbose " if options[:with_paths] cmd << " --username #{@login} --password #{@password}" if @login
cmd << " --verbose " if options[:with_paths]
cmd << target(path) cmd << target(path)
shellout(cmd) do |io| shellout(cmd) do |io|
begin begin
@@ -86,13 +120,15 @@ module SvnRepos
paths = [] paths = []
logentry.elements.each("paths/path") do |path| logentry.elements.each("paths/path") do |path|
paths << {:action => path.attributes['action'], paths << {:action => path.attributes['action'],
:path => path.text :path => path.text,
:from_path => path.attributes['copyfrom-path'],
:from_revision => path.attributes['copyfrom-rev']
} }
end end
paths.sort! { |x,y| x[:path] <=> y[:path] } paths.sort! { |x,y| x[:path] <=> y[:path] }
revisions << Revision.new({:identifier => logentry.attributes['revision'], revisions << Revision.new({:identifier => logentry.attributes['revision'],
:author => (logentry.elements['author'] ? logentry.elements['author'].text : "anonymous"), :author => (logentry.elements['author'] ? logentry.elements['author'].text : ""),
:time => Time.parse(logentry.elements['date'].text), :time => Time.parse(logentry.elements['date'].text),
:message => logentry.elements['msg'].text, :message => logentry.elements['msg'].text,
:paths => paths :paths => paths
@@ -107,7 +143,7 @@ module SvnRepos
raise CommandFailed raise CommandFailed
end end
def diff(path, identifier_from, identifier_to=nil) def diff(path, identifier_from, identifier_to=nil, type="inline")
path ||= '' path ||= ''
if identifier_to and identifier_to.to_i > 0 if identifier_to and identifier_to.to_i > 0
identifier_to = identifier_to.to_i identifier_to = identifier_to.to_i
@@ -118,6 +154,7 @@ module SvnRepos
cmd << "#{identifier_to}:" cmd << "#{identifier_to}:"
cmd << "#{identifier_from}" cmd << "#{identifier_from}"
cmd << "#{target(path)}@#{identifier_from}" cmd << "#{target(path)}@#{identifier_from}"
cmd << " --username #{@login} --password #{@password}" if @login
diff = [] diff = []
shellout(cmd) do |io| shellout(cmd) do |io|
io.each_line do |line| io.each_line do |line|
@@ -125,7 +162,8 @@ module SvnRepos
end end
end end
return nil if $? && $?.exitstatus != 0 return nil if $? && $?.exitstatus != 0
diff DiffTableList.new diff, type
rescue Errno::ENOENT => e rescue Errno::ENOENT => e
raise CommandFailed raise CommandFailed
end end
@@ -133,6 +171,7 @@ module SvnRepos
def cat(path, identifier=nil) def cat(path, identifier=nil)
identifier = (identifier and identifier.to_i > 0) ? identifier.to_i : "HEAD" identifier = (identifier and identifier.to_i > 0) ? identifier.to_i : "HEAD"
cmd = "svn cat #{target(path)}@#{identifier}" cmd = "svn cat #{target(path)}@#{identifier}"
cmd << " --username #{@login} --password #{@password}" if @login
cat = nil cat = nil
shellout(cmd) do |io| shellout(cmd) do |io|
cat = io.read cat = io.read
@@ -144,8 +183,15 @@ module SvnRepos
end end
private private
def retrieve_root_url
info = self.info
info ? info.root_url : nil
end
def target(path) def target(path)
" \"" << "#{@url}/#{path}".gsub(/["'?<>\*]/, '') << "\"" path ||= ""
base = path.match(/^\//) ? root_url : url
" \"" << "#{base}/#{path}".gsub(/["'?<>\*]/, '') << "\""
end end
def logger def logger
@@ -154,7 +200,8 @@ module SvnRepos
def shellout(cmd, &block) def shellout(cmd, &block)
logger.debug "Shelling out: #{cmd}" if logger && logger.debug? logger.debug "Shelling out: #{cmd}" if logger && logger.debug?
IO.popen(cmd) do |io| IO.popen(cmd, "r+") do |io|
io.close_write
block.call(io) if block_given? block.call(io) if block_given?
end end
end end
@@ -176,6 +223,14 @@ module SvnRepos
end end
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 class Entry
attr_accessor :name, :path, :kind, :size, :lastrev attr_accessor :name, :path, :kind, :size, :lastrev
def initialize(attributes={}) def initialize(attributes={})
@@ -210,5 +265,167 @@ module SvnRepos
self.message = attributes[:message] || "" self.message = attributes[:message] || ""
self.paths = attributes[:paths] self.paths = attributes[:paths]
end 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 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
+1 -1
View File
@@ -19,7 +19,7 @@ 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
+21 -9
View File
@@ -22,6 +22,7 @@ class User < ActiveRecord::Base
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'
has_one :rss_key, :dependent => :destroy, :class_name => 'Token', :conditions => "action='feeds'"
belongs_to :auth_source belongs_to :auth_source
attr_accessor :password, :password_confirmation attr_accessor :password, :password_confirmation
@@ -32,9 +33,12 @@ class User < ActiveRecord::Base
validates_presence_of :login, :firstname, :lastname, :mail validates_presence_of :login, :firstname, :lastname, :mail
validates_uniqueness_of :login, :mail validates_uniqueness_of :login, :mail
# Login must contain lettres, numbers, underscores only # Login must contain lettres, numbers, underscores only
validates_format_of :firstname, :lastname, :with => /^[\w\s\'\-]*$/i
validates_format_of :login, :with => /^[a-z0-9_\-@\.]+$/i validates_format_of :login, :with => /^[a-z0-9_\-@\.]+$/i
validates_length_of :login, :maximum => 30
validates_format_of :firstname, :lastname, :with => /^[\w\s\'\-]*$/i
validates_length_of :firstname, :lastname, :maximum => 30
validates_format_of :mail, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i validates_format_of :mail, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i
validates_length_of :mail, :maximum => 60
# Password length between 4 and 12 # Password length between 4 and 12
validates_length_of :password, :in => 4..12, :allow_nil => true validates_length_of :password, :in => 4..12, :allow_nil => true
validates_confirmation_of :password, :allow_nil => true validates_confirmation_of :password, :allow_nil => true
@@ -120,20 +124,28 @@ class User < ActiveRecord::Base
User.hash_password(clear_password) == self.hashed_password User.hash_password(clear_password) == self.hashed_password
end end
def role_for_project(project_id) def role_for_project(project)
@role_for_projects ||= member = memberships.detect {|m| m.project_id == project.id}
begin member ? member.role : nil
roles = {}
self.memberships.each { |m| roles.store m.project_id, m.role_id }
roles
end
@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
self.rss_key || Token.create(:user => self, :action => 'feeds')
end
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 private
# Return password digest # Return password digest
def self.hash_password(clear_password) def self.hash_password(clear_password)
+8
View File
@@ -25,6 +25,14 @@ class Version < ActiveRecord::Base
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
def start_date
effective_date
end
def due_date
effective_date
end
private private
def check_integrity def check_integrity
raise "Can't delete version" if self.fixed_issues.find(:first) raise "Can't delete version" if self.fixed_issues.find(:first)
+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
+2 -1
View File
@@ -9,7 +9,8 @@
<% 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>
+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>
+7 -1
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>
+3 -2
View File
@@ -1,6 +1,6 @@
<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>
@@ -13,11 +13,12 @@
<%= l(p.description.to_sym) %> <%= l(p.description.to_sym) %>
</div> </div>
<% end %> <% end %>
<div class="clear"></div>
</fieldset> </fieldset>
<% end %> <% end %>
<br /> <br />
<p><%= check_all_links 'mail_options_form' %></p> <p><%= check_all_links('mail_options_form') %></p>
</div> </div>
<%= submit_tag l(:button_save) %> <%= submit_tag l(:button_save) %>
+1 -1
View File
@@ -10,7 +10,7 @@
<%= 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>
+2 -1
View File
@@ -28,7 +28,8 @@
<% if authorize_for('documents', 'add_attachment') %> <% if authorize_for('documents', 'add_attachment') %>
<% form_tag ({ :controller => 'documents', :action => 'add_attachment', :id => @document }, :multipart => true, :class => "tabular") do %> <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)%> <p id="attachments_p"><label for="attachment_file"><%=l(:label_attachment)%>
<%= image_to_function "add.png", "addFileField();return false" %></label> <%= image_to_function "add.png", "addFileField();return false" %></label>
<%= file_field_tag 'attachments[]', :size => 30 %> <em>(<%= l(:label_max_size) %>: <%= number_to_human_size(Setting.attachment_max_size.to_i.kilobytes) %>)</em></p> <%= file_field_tag 'attachments[]', :size => 30 %> <em>(<%= l(:label_max_size) %>: <%= number_to_human_size(Setting.attachment_max_size.to_i.kilobytes) %>)</em></p>
+28
View File
@@ -0,0 +1,28 @@
xml.instruct!
xml.rss "version" => "2.0", "xmlns:dc" => "http://purl.org/dc/elements/1.1/" do
xml.channel do
xml.title @title
xml.link url_for(:controller => 'welcome', :only_path => false)
xml.pubDate CGI.rfc1123_date(@journals.first ? @journals.first.created_on : Time.now)
xml.description l(:label_reported_issues)
@journals.each do |journal|
issue = journal.issue
xml.item do
xml.title "#{issue.project.name} - #{issue.tracker.name} ##{issue.id}: #{issue.subject}"
url = url_for(:controller => 'issues' , :action => 'show', :id => issue, :only_path => false)
xml.link url
xml.description do
xml.text! h(journal.notes)
xml.text! "<ul>"
journal.details.each do |detail|
xml.text! "<li>" + show_detail(detail, false) + "</li>"
end
xml.text! "</ul>"
end
xml.pubDate CGI.rfc1123_date(journal.created_on)
xml.guid url
xml.author h(journal.user.name)
end
end
end
end
+28
View File
@@ -0,0 +1,28 @@
xml.instruct!
xml.feed "xmlns" => "http://www.w3.org/2005/Atom" do
xml.title @title
xml.link "rel" => "self", "href" => url_for(:controller => 'feeds', :action => 'history', :format => 'atom', :only_path => false)
xml.link "rel" => "alternate", "href" => url_for(:controller => 'welcome', :only_path => false)
xml.id url_for(:controller => 'welcome', :only_path => false)
xml.updated CGI.rfc1123_date(@journals.first.created_on) if @journals.any?
xml.author { xml.name "#{Setting.app_title}" }
@journals.each do |journal|
issue = journal.issue
xml.entry do
xml.title "#{issue.project.name} - #{issue.tracker.name} ##{issue.id}: #{issue.subject}"
xml.link "rel" => "alternate", "href" => url_for(:controller => 'issues' , :action => 'show', :id => issue, :only_path => false)
xml.id url_for(:controller => 'issues' , :action => 'show', :id => issue, :only_path => false)
xml.updated CGI.rfc1123_date(journal.created_on)
xml.author { xml.name journal.user.name }
xml.summary journal.notes
xml.content "type" => "html" do
xml.text! journal.notes
xml.text! "<ul>"
journal.details.each do |detail|
xml.text! "<li>" + show_detail(detail, false) + "</li>"
end
xml.text! "</ul>"
end
end
end
end
+20
View File
@@ -0,0 +1,20 @@
xml.instruct!
xml.rss "version" => "2.0", "xmlns:dc" => "http://purl.org/dc/elements/1.1/" do
xml.channel do
xml.title @title
xml.link url_for(:controller => 'welcome', :only_path => false)
xml.pubDate CGI.rfc1123_date(@issues.first ? @issues.first.created_on : Time.now)
xml.description l(:label_reported_issues)
@issues.each do |issue|
xml.item do
xml.title "#{issue.project.name} - #{issue.tracker.name} ##{issue.id}: #{issue.subject}"
url = url_for(:controller => 'issues' , :action => 'show', :id => issue, :only_path => false)
xml.link url
xml.description h(issue.description)
xml.pubDate CGI.rfc1123_date(issue.created_on)
xml.guid url
xml.author h(issue.author.name)
end
end
end
end
+22
View File
@@ -0,0 +1,22 @@
xml.instruct!
xml.feed "xmlns" => "http://www.w3.org/2005/Atom" do
xml.title @title
xml.link "rel" => "self", "href" => url_for(:controller => 'feeds', :action => 'issues', :format => 'atom', :only_path => false)
xml.link "rel" => "alternate", "href" => url_for(:controller => 'welcome', :only_path => false)
xml.id url_for(:controller => 'welcome', :only_path => false)
xml.updated CGI.rfc1123_date(@issues.first.updated_on) if @issues.any?
xml.author { xml.name "#{Setting.app_title}" }
@issues.each do |issue|
xml.entry do
xml.title "#{issue.project.name} - #{issue.tracker.name} ##{issue.id}: #{issue.subject}"
xml.link "rel" => "alternate", "href" => url_for(:controller => 'issues' , :action => 'show', :id => issue, :only_path => false)
xml.id url_for(:controller => 'issues' , :action => 'show', :id => issue, :only_path => false)
xml.updated CGI.rfc1123_date(issue.updated_on)
xml.author { xml.name issue.author.name }
xml.summary issue.description
xml.content "type" => "html" do
xml.text! issue.description
end
end
end
end
+1 -1
View File
@@ -3,7 +3,7 @@ xml.rss "version" => "2.0", "xmlns:dc" => "http://purl.org/dc/elements/1.1/" do
xml.channel do xml.channel do
xml.title "#{Setting.app_title}: #{l(:label_news_latest)}" xml.title "#{Setting.app_title}: #{l(:label_news_latest)}"
xml.link url_for(:controller => 'welcome', :only_path => false) xml.link url_for(:controller => 'welcome', :only_path => false)
xml.pubDate CGI.rfc1123_date(@news.first.created_on) xml.pubDate CGI.rfc1123_date(@news.first ? @news.first.created_on : Time.now)
xml.description l(:label_news_latest) xml.description l(:label_news_latest)
@news.each do |news| @news.each do |news|
xml.item do xml.item do
+22
View File
@@ -0,0 +1,22 @@
xml.instruct!
xml.feed "xmlns" => "http://www.w3.org/2005/Atom" do
xml.title "#{Setting.app_title}: #{l(:label_news_latest)}"
xml.link "rel" => "self", "href" => url_for(:controller => 'feeds', :action => 'news', :format => 'atom', :only_path => false)
xml.link "rel" => "alternate", "href" => url_for(:controller => 'welcome', :only_path => false)
xml.id url_for(:controller => 'welcome', :only_path => false)
xml.updated CGI.rfc1123_date(@news.first.created_on) if @news.any?
xml.author { xml.name "#{Setting.app_title}" }
@news.each do |news|
xml.entry do
xml.title news.title
xml.link "rel" => "alternate", "href" => url_for(:controller => 'news' , :action => 'show', :id => news, :only_path => false)
xml.id url_for(:controller => 'news' , :action => 'show', :id => news, :only_path => false)
xml.updated CGI.rfc1123_date(news.created_on)
xml.author { xml.name news.author.name }
xml.summary h(news.summary)
xml.content "type" => "html" do
xml.text! news.description
end
end
end
end
+2 -2
View File
@@ -18,13 +18,13 @@
<td><div class="square" style="background:#<%= status.html_color %>;"></div> <%= link_to status.name, :action => 'edit', :id => status %></td> <td><div class="square" style="background:#<%= status.html_color %>;"></div> <%= link_to status.name, :action => 'edit', :id => status %></td>
<td align="center"><%= image_tag 'true.png' if status.is_default? %></td> <td align="center"><%= image_tag 'true.png' if status.is_default? %></td>
<td align="center"><%= image_tag 'true.png' if status.is_closed? %></td> <td align="center"><%= image_tag 'true.png' if status.is_closed? %></td>
<td align="center"> <td align="center" style="width:15%;">
<%= link_to image_tag('2uparrow.png', :alt => l(:label_sort_highest)), {:action => 'move', :id => status, :position => 'highest'}, :method => :post, :title => l(:label_sort_highest) %> <%= link_to image_tag('2uparrow.png', :alt => l(:label_sort_highest)), {:action => 'move', :id => status, :position => 'highest'}, :method => :post, :title => l(:label_sort_highest) %>
<%= link_to image_tag('1uparrow.png', :alt => l(:label_sort_higher)), {:action => 'move', :id => status, :position => 'higher'}, :method => :post, :title => l(:label_sort_higher) %> - <%= link_to image_tag('1uparrow.png', :alt => l(:label_sort_higher)), {:action => 'move', :id => status, :position => 'higher'}, :method => :post, :title => l(:label_sort_higher) %> -
<%= link_to image_tag('1downarrow.png', :alt => l(:label_sort_lower)), {:action => 'move', :id => status, :position => 'lower'}, :method => :post, :title => l(:label_sort_lower) %> <%= link_to image_tag('1downarrow.png', :alt => l(:label_sort_lower)), {:action => 'move', :id => status, :position => 'lower'}, :method => :post, :title => l(:label_sort_lower) %>
<%= link_to image_tag('2downarrow.png', :alt => l(:label_sort_lowest)), {:action => 'move', :id => status, :position => 'lowest'}, :method => :post, :title => l(:label_sort_lowest) %> <%= link_to image_tag('2downarrow.png', :alt => l(:label_sort_lowest)), {:action => 'move', :id => status, :position => 'lowest'}, :method => :post, :title => l(:label_sort_lowest) %>
</td> </td>
<td align="center"> <td align="center" style="width:10%;">
<%= button_to l(:button_delete), { :action => 'destroy', :id => status }, :confirm => l(:text_are_you_sure), :class => "button-small" %> <%= button_to l(:button_delete), { :action => 'destroy', :id => status }, :confirm => l(:text_are_you_sure), :class => "button-small" %>
</td> </td>
</tr> </tr>
+1 -1
View File
@@ -6,6 +6,6 @@
<% end %> <% end %>
</ul> </ul>
<% if journal.notes? %> <% if journal.notes? %>
<%= simple_format auto_link h(journal.notes) %> <%= textilizable(journal.notes) %>
<% end %> <% end %>
<% end %> <% end %>
+1 -1
View File
@@ -11,7 +11,7 @@
<th align="center"> <th align="center">
<%= link_to issue.id, :controller => 'issues', :action => 'show', :id => issue %> <%= link_to issue.id, :controller => 'issues', :action => 'show', :id => issue %>
</th> </th>
<td><p class="small"><%= issue.project.name %> - <%= issue.tracker.name %><br /> <td><div class="square" style="background:#<%= issue.status.html_color %>;"></div> <p class="small"><%= issue.project.name %> - <%= issue.tracker.name %><br />
<%= issue.status.name %> - <%= format_time(issue.updated_on) %></p></td> <%= issue.status.name %> - <%= format_time(issue.updated_on) %></p></td>
<td> <td>
<p class="small"><%= link_to h(issue.subject), :controller => 'issues', :action => 'show', :id => issue %></p> <p class="small"><%= link_to h(issue.subject), :controller => 'issues', :action => 'show', :id => issue %></p>
+31 -31
View File
@@ -1,65 +1,65 @@
<% pdf.SetFont('Arial','B',11) <% pdf.SetFontStyle('B',11)
pdf.Cell(190,10, "#{issue.project.name} - #{issue.tracker.name} # #{issue.long_id} - #{issue.subject}") pdf.Cell(190,10, "#{issue.project.name} - #{issue.tracker.name} # #{issue.long_id} - #{issue.subject}")
pdf.Ln pdf.Ln
y0 = pdf.GetY y0 = pdf.GetY
pdf.SetFont('Arial','B',9) pdf.SetFontStyle('B',9)
pdf.Cell(35,5, l(:field_status) + ":","LT") pdf.Cell(35,5, l(:field_status) + ":","LT")
pdf.SetFont('Arial','',9) pdf.SetFontStyle('',9)
pdf.Cell(60,5, issue.status.name,"RT") pdf.Cell(60,5, issue.status.name,"RT")
pdf.SetFont('Arial','B',9) pdf.SetFontStyle('B',9)
pdf.Cell(35,5, l(:field_priority) + ":","LT") pdf.Cell(35,5, l(:field_priority) + ":","LT")
pdf.SetFont('Arial','',9) pdf.SetFontStyle('',9)
pdf.Cell(60,5, issue.priority.name,"RT") pdf.Cell(60,5, issue.priority.name,"RT")
pdf.Ln pdf.Ln
pdf.SetFont('Arial','B',9) pdf.SetFontStyle('B',9)
pdf.Cell(35,5, l(:field_author) + ":","L") pdf.Cell(35,5, l(:field_author) + ":","L")
pdf.SetFont('Arial','',9) pdf.SetFontStyle('',9)
pdf.Cell(60,5, issue.author.name,"R") pdf.Cell(60,5, issue.author.name,"R")
pdf.SetFont('Arial','B',9) pdf.SetFontStyle('B',9)
pdf.Cell(35,5, l(:field_category) + ":","L") pdf.Cell(35,5, l(:field_category) + ":","L")
pdf.SetFont('Arial','',9) pdf.SetFontStyle('',9)
pdf.Cell(60,5, (issue.category ? issue.category.name : "-"),"R") pdf.Cell(60,5, (issue.category ? issue.category.name : "-"),"R")
pdf.Ln pdf.Ln
pdf.SetFont('Arial','B',9) pdf.SetFontStyle('B',9)
pdf.Cell(35,5, l(:field_created_on) + ":","L") pdf.Cell(35,5, l(:field_created_on) + ":","L")
pdf.SetFont('Arial','',9) pdf.SetFontStyle('',9)
pdf.Cell(60,5, format_date(issue.created_on),"R") pdf.Cell(60,5, format_date(issue.created_on),"R")
pdf.SetFont('Arial','B',9) pdf.SetFontStyle('B',9)
pdf.Cell(35,5, l(:field_assigned_to) + ":","L") pdf.Cell(35,5, l(:field_assigned_to) + ":","L")
pdf.SetFont('Arial','',9) pdf.SetFontStyle('',9)
pdf.Cell(60,5, (issue.assigned_to ? issue.assigned_to.name : "-"),"R") pdf.Cell(60,5, (issue.assigned_to ? issue.assigned_to.name : "-"),"R")
pdf.Ln pdf.Ln
pdf.SetFont('Arial','B',9) pdf.SetFontStyle('B',9)
pdf.Cell(35,5, l(:field_updated_on) + ":","LB") pdf.Cell(35,5, l(:field_updated_on) + ":","LB")
pdf.SetFont('Arial','',9) pdf.SetFontStyle('',9)
pdf.Cell(60,5, format_date(issue.updated_on),"RB") pdf.Cell(60,5, format_date(issue.updated_on),"RB")
pdf.SetFont('Arial','B',9) pdf.SetFontStyle('B',9)
pdf.Cell(35,5, l(:field_due_date) + ":","LB") pdf.Cell(35,5, l(:field_due_date) + ":","LB")
pdf.SetFont('Arial','',9) pdf.SetFontStyle('',9)
pdf.Cell(60,5, format_date(issue.due_date),"RB") pdf.Cell(60,5, format_date(issue.due_date),"RB")
pdf.Ln pdf.Ln
for custom_value in issue.custom_values for custom_value in issue.custom_values
pdf.SetFont('Arial','B',9) pdf.SetFontStyle('B',9)
pdf.Cell(35,5, custom_value.custom_field.name + ":","L") pdf.Cell(35,5, custom_value.custom_field.name + ":","L")
pdf.SetFont('Arial','',9) pdf.SetFontStyle('',9)
pdf.MultiCell(155,5, (show_value custom_value),"R") pdf.MultiCell(155,5, (show_value custom_value),"R")
end end
pdf.SetFont('Arial','B',9) pdf.SetFontStyle('B',9)
pdf.Cell(35,5, l(:field_subject) + ":","LTB") pdf.Cell(35,5, l(:field_subject) + ":","LTB")
pdf.SetFont('Arial','',9) pdf.SetFontStyle('',9)
pdf.Cell(155,5, issue.subject,"RTB") pdf.Cell(155,5, issue.subject,"RTB")
pdf.Ln pdf.Ln
pdf.SetFont('Arial','B',9) pdf.SetFontStyle('B',9)
pdf.Cell(35,5, l(:field_description) + ":") pdf.Cell(35,5, l(:field_description) + ":")
pdf.SetFont('Arial','',9) pdf.SetFontStyle('',9)
pdf.MultiCell(155,5, issue.description,"BR") pdf.MultiCell(155,5, issue.description,"BR")
pdf.Line(pdf.GetX, y0, pdf.GetX, pdf.GetY) pdf.Line(pdf.GetX, y0, pdf.GetX, pdf.GetY)
@@ -67,34 +67,34 @@
pdf.Ln pdf.Ln
pdf.SetFont('Arial','B',9) pdf.SetFontStyle('B',9)
pdf.Cell(190,5, l(:label_history), "B") pdf.Cell(190,5, l(:label_history), "B")
pdf.Ln pdf.Ln
for journal in issue.journals.find(:all, :include => :user, :order => "journals.created_on desc") for journal in issue.journals.find(:all, :include => :user, :order => "journals.created_on desc")
pdf.SetFont('Arial','B',8) pdf.SetFontStyle('B',8)
pdf.Cell(190,5, format_time(journal.created_on) + " - " + journal.user.name) pdf.Cell(190,5, format_time(journal.created_on) + " - " + journal.user.name)
pdf.Ln pdf.Ln
pdf.SetFont('Arial','I',8) pdf.SetFontStyle('I',8)
for detail in journal.details for detail in journal.details
pdf.Cell(190,5, "- " + show_detail(detail, true)) pdf.Cell(190,5, "- " + show_detail(detail, true))
pdf.Ln pdf.Ln
end end
if journal.notes? if journal.notes?
pdf.SetFont('Arial','',8) pdf.SetFontStyle('',8)
pdf.MultiCell(190,5, journal.notes) pdf.MultiCell(190,5, journal.notes)
end end
pdf.Ln pdf.Ln
end end
pdf.SetFont('Arial','B',9) pdf.SetFontStyle('B',9)
pdf.Cell(190,5, l(:label_attachment_plural), "B") pdf.Cell(190,5, l(:label_attachment_plural), "B")
pdf.Ln pdf.Ln
for attachment in issue.attachments for attachment in issue.attachments
pdf.SetFont('Arial','',8) pdf.SetFontStyle('',8)
pdf.Cell(80,5, attachment.filename) pdf.Cell(80,5, attachment.filename)
pdf.Cell(20,5, number_to_human_size(attachment.filesize),0,0,"R") pdf.Cell(20,5, number_to_human_size(attachment.filesize),0,0,"R")
pdf.Cell(20,5, format_date(attachment.created_on),0,0,"R") pdf.Cell(25,5, format_date(attachment.created_on),0,0,"R")
pdf.Cell(70,5, attachment.author.name,0,0,"R") pdf.Cell(65,5, attachment.author.name,0,0,"R")
pdf.Ln pdf.Ln
end end
%> %>
+1 -1
View File
@@ -1,4 +1,4 @@
<%= link_to "#{issue.tracker.name} ##{issue.id}", { :controller => 'issues', :action => 'show', :id => issue } %></strong>: <%=h issue.subject %><br /> <%= link_to_issue issue %></strong>: <%=h issue.subject %><br />
<br /> <br />
<strong><%= l(:field_start_date) %></strong>: <%= format_date(issue.start_date) %><br /> <strong><%= l(:field_start_date) %></strong>: <%= format_date(issue.start_date) %><br />
<strong><%= l(:field_due_date) %></strong>: <%= format_date(issue.due_date) %><br /> <strong><%= l(:field_due_date) %></strong>: <%= format_date(issue.due_date) %><br />
+11 -21
View File
@@ -1,37 +1,27 @@
<h2><%=l(:label_issue)%> #<%= @issue.id %>: <%=h @issue.subject %></h2> <h2><%=l(:label_issue)%> #<%= @issue.id %>: <%=h @issue.subject %></h2>
<%= error_messages_for 'issue' %> <%= error_messages_for 'issue' %>
<% form_tag({:action => 'change_status', :id => @issue}, :class => "tabular") do %> <% labelled_tabular_form_for(:issue, @issue, :url => {:action => 'change_status', :id => @issue}, :html => {:multipart => true}) do |f| %>
<%= hidden_field_tag 'confirm', 1 %> <%= hidden_field_tag 'confirm', 1 %>
<%= hidden_field_tag 'new_status_id', @new_status.id %> <%= hidden_field_tag 'new_status_id', @new_status.id %>
<%= f.hidden_field :lock_version %>
<div class="box"> <div class="box">
<p><label><%=l(:label_issue_status_new)%></label> <%= @new_status.name %></p> <p><label><%=l(:label_issue_status_new)%></label> <%= @new_status.name %></p>
<p><%= f.select :assigned_to_id, (@issue.project.members.collect {|m| [m.name, m.user_id]}), :include_blank => true %></p>
<p><label for="issue_assigned_to_id"><%=l(:field_assigned_to)%></label> <p><%= f.select :done_ratio, ((0..10).to_a.collect {|r| ["#{r*10} %", r*10] }) %></p>
<select name="issue[assigned_to_id]"> <p><%= f.select :fixed_version_id, (@project.versions.collect {|v| [v.name, v.id]}), { :include_blank => true } %></p>
<option value=""></option>
<%= options_from_collection_for_select @assignable_to, "id", "display_name", @issue.assigned_to_id %></p>
</select></p>
<p><label for="issue_done_ratio"><%=l(:field_done_ratio)%></label>
<%= select("issue", "done_ratio", ((0..10).to_a.collect {|r| ["#{r*10} %", r*10] }) ) %>
</select></p>
<p><label for="issue_fixed_version"><%=l(:field_fixed_version)%></label>
<select name="issue[fixed_version_id]">
<option value="">--none--</option>
<%= options_from_collection_for_select @issue.project.versions, "id", "name", @issue.fixed_version_id %>
</select></p>
<p><label for="notes"><%= l(:field_notes) %></label> <p><label for="notes"><%= l(:field_notes) %></label>
<%= text_area_tag 'notes', @notes, :cols => 60, :rows => 10 %></p> <%= text_area_tag 'notes', @notes, :cols => 60, :rows => 10, :class => 'wiki-edit' %></p>
<% if authorize_for('issues', 'add_attachment') %>
<p id="attachments_p"><label><%=l(:label_attachment_new)%>
<%= 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>
<% end %>
</div> </div>
<%= hidden_field 'issue', 'lock_version' %>
<%= submit_tag l(:button_save) %> <%= submit_tag l(:button_save) %>
<% end %> <% end %>
+3 -4
View File
@@ -8,7 +8,7 @@
<p><label><%=l(:field_status)%></label> <%= @issue.status.name %></p> <p><label><%=l(:field_status)%></label> <%= @issue.status.name %></p>
<p><%= f.select :priority_id, (@priorities.collect {|p| [p.name, p.id]}), :required => true %></p> <p><%= f.select :priority_id, (@priorities.collect {|p| [p.name, p.id]}), :required => true %></p>
<p><%= f.select :assigned_to_id, (@issue.project.members.collect {|m| [m.name, m.user_id]}), :include_blank => true %></p> <p><%= f.select :assigned_to_id, (@issue.project.members.collect {|m| [m.name, m.user_id]}), :include_blank => true %></p>
<p><%= f.select :category_id, (@project.issue_categories.collect {|c| [c.name, c.id]}) %></p> <p><%= f.select :category_id, (@project.issue_categories.collect {|c| [c.name, c.id]}), :include_blank => true %></p>
</div> </div>
<div class="splitcontentright"> <div class="splitcontentright">
@@ -19,14 +19,13 @@
<div class="clear"> <div class="clear">
<p><%= f.text_field :subject, :size => 80, :required => true %></p> <p><%= f.text_field :subject, :size => 80, :required => true %></p>
<p><%= f.text_area :description, :cols => 60, :rows => [[10, @issue.description.length / 50].max, 100].min, :required => true %></p> <p><%= f.text_area :description, :required => true, :cols => 60, :rows => [[10, @issue.description.length / 50].max, 100].min, :class => 'wiki-edit' %></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><%= f.select :fixed_version_id, (@project.versions.collect {|v| [v.name, v.id]}), { :include_blank => true } %> <p><%= f.select :fixed_version_id, (@project.versions.collect {|v| [v.name, v.id]}), { :include_blank => true } %></p>
</select></p>
</div> </div>
<!--[eoform:issue]--> <!--[eoform:issue]-->
</div> </div>
+1 -1
View File
@@ -1,4 +1,4 @@
<% pdf=IfpdfHelper::IFPDF.new <% pdf=IfpdfHelper::IFPDF.new(current_language)
pdf.SetTitle("#{@project.name} - ##{@issue.tracker.name} #{@issue.id}") pdf.SetTitle("#{@project.name} - ##{@issue.tracker.name} #{@issue.id}")
pdf.AliasNbPages pdf.AliasNbPages
pdf.footer_date = format_date(Date.today) pdf.footer_date = format_date(Date.today)
+7 -5
View File
@@ -11,7 +11,7 @@
<td style="width:15%"><b><%=l(:field_priority)%> :</b></td><td style="width:35%"><%= @issue.priority.name %></td> <td style="width:15%"><b><%=l(:field_priority)%> :</b></td><td style="width:35%"><%= @issue.priority.name %></td>
</tr> </tr>
<tr> <tr>
<td><b><%=l(:field_assigned_to)%> :</b></td><td><%= @issue.assigned_to ? @issue.assigned_to.name : "-" %></td> <td><b><%=l(:field_assigned_to)%> :</b></td><td><%= @issue.assigned_to ? link_to_user(@issue.assigned_to) : "-" %></td>
<td><b><%=l(:field_category)%> :</b></td><td><%=h @issue.category ? @issue.category.name : "-" %></td> <td><b><%=l(:field_category)%> :</b></td><td><%=h @issue.category ? @issue.category.name : "-" %></td>
</tr> </tr>
<tr> <tr>
@@ -28,12 +28,13 @@
</tr> </tr>
<tr> <tr>
<td><b><%=l(:field_fixed_version)%> :</b></td><td><%= @issue.fixed_version ? @issue.fixed_version.name : "-" %></td> <td><b><%=l(:field_fixed_version)%> :</b></td><td><%= @issue.fixed_version ? @issue.fixed_version.name : "-" %></td>
<td></td><td></td> <td><b><%=l(:label_spent_time)%> :</b></td>
<td><%= @issue.spent_hours > 0 ? (link_to lwr(:label_f_hour, @issue.spent_hours), {:controller => 'timelog', :action => 'details', :issue_id => @issue}, :class => 'icon icon-time') : "-" %></td>
</tr> </tr>
<tr> <tr>
<% n = 0 <% n = 0
for custom_value in @custom_values %> for custom_value in @custom_values %>
<td><b><%= custom_value.custom_field.name %> :</b></td><td><%=h show_value custom_value %></td> <td><b><%= custom_value.custom_field.name %> :</b></td><td><%= h(show_value(custom_value)) %></td>
<% n = n + 1 <% n = n + 1
if (n > 1) if (n > 1)
n = 0 %> n = 0 %>
@@ -51,6 +52,7 @@ end %>
<div class="contextual"> <div class="contextual">
<%= link_to_if_authorized l(:button_edit), {:controller => 'issues', :action => 'edit', :id => @issue}, :class => 'icon icon-edit' %> <%= link_to_if_authorized l(:button_edit), {:controller => 'issues', :action => 'edit', :id => @issue}, :class => 'icon icon-edit' %>
<%= link_to_if_authorized l(:button_log_time), {:controller => 'timelog', :action => 'edit', :issue_id => @issue}, :class => 'icon icon-time' %>
<%= link_to_if_authorized l(:button_move), {:controller => 'projects', :action => 'move_issues', :id => @project, "issue_ids[]" => @issue.id }, :class => 'icon icon-move' %> <%= link_to_if_authorized l(:button_move), {:controller => 'projects', :action => 'move_issues', :id => @project, "issue_ids[]" => @issue.id }, :class => 'icon icon-move' %>
<%= link_to_if_authorized l(:button_delete), {:controller => 'issues', :action => 'destroy', :id => @issue}, :confirm => l(:text_are_you_sure), :method => :post, :class => 'icon icon-del' %> <%= link_to_if_authorized l(:button_delete), {:controller => 'issues', :action => 'destroy', :id => @issue}, :confirm => l(:text_are_you_sure), :method => :post, :class => 'icon icon-del' %>
</div> </div>
@@ -72,7 +74,7 @@ end %>
<% if @journals_count > @journals.length %>(<%= l(:label_last_changes, @journals.length) %>)<% end %></h3> <% if @journals_count > @journals.length %>(<%= l(:label_last_changes, @journals.length) %>)<% end %></h3>
<%= render :partial => 'history', :locals => { :journals => @journals } %> <%= render :partial => 'history', :locals => { :journals => @journals } %>
<% if @journals_count > @journals.length %> <% if @journals_count > @journals.length %>
<p><center><small>[ <%= link_to l(:label_change_view_all), :action => 'history', :id => @issue %> ]</small></center></p> <p><center><small><%= link_to l(:label_change_view_all), :action => 'history', :id => @issue %></small></center></p>
<% end %> <% end %>
</div> </div>
@@ -104,7 +106,7 @@ end %>
<h3><%= l(:label_add_note) %></h3> <h3><%= l(:label_add_note) %></h3>
<% form_tag ({:controller => 'issues', :action => 'add_note', :id => @issue}, :class => "tabular" ) do %> <% form_tag ({:controller => 'issues', :action => 'add_note', :id => @issue}, :class => "tabular" ) do %>
<p><label for="notes"><%=l(:field_notes)%></label> <p><label for="notes"><%=l(:field_notes)%></label>
<%= text_area_tag 'notes', '', :cols => 60, :rows => 10 %></p> <%= text_area_tag 'notes', '', :cols => 60, :rows => 10, :class => 'wiki-edit' %></p>
<%= submit_tag l(:button_add) %> <%= submit_tag l(:button_add) %>
<% end %> <% end %>
</div> </div>
+6 -21
View File
@@ -60,24 +60,7 @@
</div> </div>
<% if admin_loggedin? %> <% if admin_loggedin? %>
<div id="menuAdmin" class="menu" onmouseover="menuMouseover(event)"> <%= render :partial => 'admin/menu' %>
<a class="menuItem" href="/admin/projects" onmouseover="menuItemMouseover(event,'menuProjects');"><span class="menuItemText"><%=l(:label_project_plural)%></span><span class="menuItemArrow">&#9654;</span></a>
<a class="menuItem" href="/users" onmouseover="menuItemMouseover(event,'menuUsers');"><span class="menuItemText"><%=l(:label_user_plural)%></span><span class="menuItemArrow">&#9654;</span></a>
<a class="menuItem" href="/roles"><%=l(:label_role_and_permissions)%></a>
<a class="menuItem" href="/trackers" onmouseover="menuItemMouseover(event,'menuTrackers');"><span class="menuItemText"><%=l(:label_tracker_plural)%></span><span class="menuItemArrow">&#9654;</span></a>
<a class="menuItem" href="/custom_fields"><%=l(:label_custom_field_plural)%></a>
<a class="menuItem" href="/enumerations"><%=l(:label_enumerations)%></a>
<a class="menuItem" href="/admin/mail_options"><%=l(:field_mail_notification)%></a>
<a class="menuItem" href="/auth_sources"><%=l(:label_authentication)%></a>
<a class="menuItem" href="/settings"><%=l(:label_settings)%></a>
<a class="menuItem" href="/admin/info"><%=l(:label_information_plural)%></a>
</div>
<div id="menuTrackers" class="menu">
<a class="menuItem" href="/issue_statuses"><%=l(:label_issue_status_plural)%></a>
<a class="menuItem" href="/roles/workflow"><%=l(:label_workflow)%></a>
</div>
<div id="menuProjects" class="menu"><a class="menuItem" href="/projects/add"><%=l(:label_new)%></a></div>
<div id="menuUsers" class="menu"><a class="menuItem" href="/users/add"><%=l(:label_new)%></a></div>
<% end %> <% end %>
<% unless @project.nil? || @project.id.nil? %> <% unless @project.nil? || @project.id.nil? %>
@@ -91,8 +74,9 @@
<%= link_to l(:label_change_log), {:controller => 'projects', :action => 'changelog', :id => @project }, :class => "menuItem" %> <%= link_to l(:label_change_log), {:controller => 'projects', :action => 'changelog', :id => @project }, :class => "menuItem" %>
<%= link_to l(:label_roadmap), {:controller => 'projects', :action => 'roadmap', :id => @project }, :class => "menuItem" %> <%= link_to l(:label_roadmap), {:controller => 'projects', :action => 'roadmap', :id => @project }, :class => "menuItem" %>
<%= link_to l(:label_document_plural), {:controller => 'projects', :action => 'list_documents', :id => @project }, :class => "menuItem" %> <%= link_to l(:label_document_plural), {:controller => 'projects', :action => 'list_documents', :id => @project }, :class => "menuItem" %>
<%= link_to l(:label_member_plural), {:controller => 'projects', :action => 'list_members', :id => @project }, :class => "menuItem" %> <%= link_to l(:label_wiki), {:controller => 'wiki', :id => @project, :page => nil }, :class => "menuItem" if @project.wiki and !@project.wiki.new_record? %>
<%= link_to l(:label_attachment_plural), {:controller => 'projects', :action => 'list_files', :id => @project }, :class => "menuItem" %> <%= link_to l(:label_attachment_plural), {:controller => 'projects', :action => 'list_files', :id => @project }, :class => "menuItem" %>
<%= link_to l(:label_search), {:controller => 'projects', :action => 'search', :id => @project }, :class => "menuItem" %>
<%= link_to l(:label_repository), {:controller => 'repositories', :action => 'show', :id => @project}, :class => "menuItem" if @project.repository and !@project.repository.new_record? %> <%= link_to l(:label_repository), {:controller => 'repositories', :action => 'show', :id => @project}, :class => "menuItem" if @project.repository and !@project.repository.new_record? %>
<%= link_to_if_authorized l(:label_settings), {:controller => 'projects', :action => 'settings', :id => @project }, :class => "menuItem" %> <%= link_to_if_authorized l(:label_settings), {:controller => 'projects', :action => 'settings', :id => @project }, :class => "menuItem" %>
</div> </div>
@@ -114,8 +98,9 @@
<li><%= link_to l(:label_change_log), :controller => 'projects', :action => 'changelog', :id => @project %></li> <li><%= link_to l(:label_change_log), :controller => 'projects', :action => 'changelog', :id => @project %></li>
<li><%= link_to l(:label_roadmap), :controller => 'projects', :action => 'roadmap', :id => @project %></li> <li><%= link_to l(:label_roadmap), :controller => 'projects', :action => 'roadmap', :id => @project %></li>
<li><%= link_to l(:label_document_plural), :controller => 'projects', :action => 'list_documents', :id => @project %></li> <li><%= link_to l(:label_document_plural), :controller => 'projects', :action => 'list_documents', :id => @project %></li>
<li><%= link_to l(:label_member_plural), :controller => 'projects', :action => 'list_members', :id => @project %></li> <li><%= link_to l(:label_wiki), :controller => 'wiki', :id => @project, :page => nil if @project.wiki and !@project.wiki.new_record? %></li>
<li><%= link_to l(:label_attachment_plural), :controller => 'projects', :action => 'list_files', :id => @project %></li> <li><%= link_to l(:label_attachment_plural), :controller => 'projects', :action => 'list_files', :id => @project %></li>
<li><%= link_to l(:label_search), :controller => 'projects', :action => 'search', :id => @project %></li>
<li><%= link_to l(:label_repository), :controller => 'repositories', :action => 'show', :id => @project if @project.repository and !@project.repository.new_record? %></li> <li><%= link_to l(:label_repository), :controller => 'repositories', :action => 'show', :id => @project if @project.repository and !@project.repository.new_record? %></li>
<li><%= link_to_if_authorized l(:label_settings), :controller => 'projects', :action => 'settings', :id => @project %></li> <li><%= link_to_if_authorized l(:label_settings), :controller => 'projects', :action => 'settings', :id => @project %></li>
</ul> </ul>
@@ -133,7 +118,7 @@
<div id="content"> <div id="content">
<% if flash[:notice] %><p style="color: green"><%= flash[:notice] %></p><% end %> <% if flash[:notice] %><p style="color: green"><%= flash[:notice] %></p><% end %>
<%= @content_for_layout %> <%= yield %>
</div> </div>
<div id="footer"> <div id="footer">
@@ -0,0 +1,6 @@
<%= @added_to %>
<%= @attachments.size %> file aggiunti.
<% @attachments.each do |attachment | %>
- <%= attachment.filename %><% end %>
<%= @url %>
@@ -0,0 +1,6 @@
<%= @added_to %>
<%= @attachments.size %> ファイルが追加されました。
<% @attachments.each do |attachment | %>
- <%= attachment.filename %><% end %>
<%= @url %>
@@ -0,0 +1,6 @@
<%= @added_to %>
已添加<%= @attachments.size %> 个文件.
<% @attachments.each do |attachment | %>
- <%= attachment.filename %><% end %>
<%= @url %>
+1 -1
View File
@@ -1,4 +1,4 @@
A document has been added to <%= @document.project.name %> (<%= @document.category.name %>): Ein Dokument wurde hinzugefügt: <%= @document.project.name %> (<%= @document.category.name %>):
<%= l(:field_title) %>: <%= @document.title %> <%= l(:field_title) %>: <%= @document.title %>
http://<%= Setting.host_name %>/documents/show/<%= @document.id %> http://<%= Setting.host_name %>/documents/show/<%= @document.id %>
+4
View File
@@ -0,0 +1,4 @@
Un documento e' stato aggiunto a <%= @document.project.name %> (<%= @document.category.name %>):
<%= l(:field_title) %>: <%= @document.title %>
http://<%= Setting.host_name %>/documents/show/<%= @document.id %>
+4
View File
@@ -0,0 +1,4 @@
文書が <%= @document.project.name %> (<%= @document.category.name %>) に追加されました:
<%= l(:field_title) %>: <%= @document.title %>
http://<%= Setting.host_name %>/documents/show/<%= @document.id %>
+4
View File
@@ -0,0 +1,4 @@
有一个文档已被加入到项目中 <%= @document.project.name %> (<%= @document.category.name %>):
<%= l(:field_title) %>: <%= @document.title %>
http://<%= Setting.host_name %>/documents/show/<%= @document.id %>
+1 -1
View File
@@ -1,3 +1,3 @@
Issue #<%= @issue.id %> has been reported. Ticket Nr. #<%= @issue.id %> wurde erstellt.
---------------------------------------- ----------------------------------------
<%= render :file => "_issue", :use_full_path => true, :locals => { :issue => @issue } %> <%= render :file => "_issue", :use_full_path => true, :locals => { :issue => @issue } %>
+3
View File
@@ -0,0 +1,3 @@
E' stata segnalata l'anomalia #<%= @issue.id %>.
----------------------------------------
<%= render :file => "_issue", :use_full_path => true, :locals => { :issue => @issue } %>
+3
View File
@@ -0,0 +1,3 @@
問題 #<%= @issue.id %> が報告されました。
----------------------------------------
<%= render :file => "_issue", :use_full_path => true, :locals => { :issue => @issue } %>
+3
View File
@@ -0,0 +1,3 @@
任务 #<%= @issue.id %> 已报告
----------------------------------------
<%= render :file => "_issue", :use_full_path => true, :locals => { :issue => @issue } %>
+1 -1
View File
@@ -1,4 +1,4 @@
Issue #<%= @issue.id %> has been updated. Ticket #<%= @issue.id %> wurde aktualisiert.
<%= @journal.user.name %> <%= @journal.user.name %>
<% for detail in @journal.details %> <% for detail in @journal.details %>
<%= show_detail(detail, true) %> <%= show_detail(detail, true) %>
+8
View File
@@ -0,0 +1,8 @@
L'anomalia #<%= @issue.id %> e' stata aggiornata.
<%= @journal.user.name %>
<% for detail in @journal.details %>
<%= show_detail(detail, true) %>
<% end %>
<%= @journal.notes if @journal.notes? %>
----------------------------------------
<%= render :file => "_issue", :use_full_path => true, :locals => { :issue => @issue } %>
+8
View File
@@ -0,0 +1,8 @@
問題 #<%= @issue.id %> が更新されました。
<%= @journal.user.name %>
<% for detail in @journal.details %>
<%= show_detail(detail, true) %>
<% end %>
<%= @journal.notes if @journal.notes? %>
----------------------------------------
<%= render :file => "_issue", :use_full_path => true, :locals => { :issue => @issue } %>
+8
View File
@@ -0,0 +1,8 @@
任务 #<%= @issue.id %> 已更新。
<%= @journal.user.name %>
<% for detail in @journal.details %>
<%= show_detail(detail, true) %>
<% end %>
<%= @journal.notes if @journal.notes? %>
----------------------------------------
<%= render :file => "_issue", :use_full_path => true, :locals => { :issue => @issue } %>
+1 -1
View File
@@ -1,3 +1,3 @@
To change your password, use the following link: Benutzen Sie folgenden Link, um das Password zu ändern:
http://<%= Setting.host_name %>/account/lost_password?token=<%= @token.value %> http://<%= Setting.host_name %>/account/lost_password?token=<%= @token.value %>
+3
View File
@@ -0,0 +1,3 @@
Per cambiare la password, usate il seguente collegamento:
http://<%= Setting.host_name %>/account/lost_password?token=<%= @token.value %>
+3
View File
@@ -0,0 +1,3 @@
パスワードを変更するには、以下のリンクをたどってください:
http://<%= Setting.host_name %>/account/lost_password?token=<%= @token.value %>
+3
View File
@@ -0,0 +1,3 @@
请点击下面的链接更改您的口令:
http://<%= Setting.host_name %>/account/lost_password?token=<%= @token.value %>
+1 -1
View File
@@ -1,3 +1,3 @@
To activate your redMine account, use the following link: Um Ihren Account zu aktivieren, benutzen Sie folgenden Link:
http://<%= Setting.host_name %>/account/register?token=<%= @token.value %> http://<%= Setting.host_name %>/account/register?token=<%= @token.value %>
+3
View File
@@ -0,0 +1,3 @@
Per attivare la vostra utenza redMine, usate il seguente collegamento:
http://<%= Setting.host_name %>/account/register?token=<%= @token.value %>
+3
View File
@@ -0,0 +1,3 @@
redMine アカウントをアクティブにするには、以下のリンクをたどってください:
http://<%= Setting.host_name %>/account/register?token=<%= @token.value %>
+3
View File
@@ -0,0 +1,3 @@
请点击下面的链接激活您的redMine账号
http://<%= Setting.host_name %>/account/register?token=<%= @token.value %>
+2 -1
View File
@@ -35,7 +35,8 @@
<%= password_field_tag 'password', nil, :size => 25 %></p> <%= password_field_tag 'password', nil, :size => 25 %></p>
<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>
+2 -2
View File
@@ -4,7 +4,7 @@
@date_from = Date.today - (Date.today.cwday-1) @date_from = Date.today - (Date.today.cwday-1)
@date_to = Date.today + (7-Date.today.cwday) @date_to = Date.today + (7-Date.today.cwday)
@issues = Issue.find :all, @issues = Issue.find :all,
:conditions => ["issues.project_id in (#{@user.projects.collect{|m| m.id}.join(',')}) AND ((start_date>=? and start_date<=?) or (due_date>=? and due_date<=?))", @date_from, @date_to, @date_from, @date_to], :conditions => ["#{Issue.table_name}.project_id in (#{@user.projects.collect{|m| m.id}.join(',')}) AND ((start_date>=? and start_date<=?) or (due_date>=? and due_date<=?))", @date_from, @date_to, @date_from, @date_to],
:include => [:project, :tracker] unless @user.projects.empty? :include => [:project, :tracker] unless @user.projects.empty?
@issues ||= [] @issues ||= []
%> %>
@@ -35,7 +35,7 @@ while day <= @date_to
elsif day == i.due_date elsif day == i.due_date
image_tag('arrow_to.png') image_tag('arrow_to.png')
end %> end %>
<small><%= link_to "#{i.tracker.name} ##{i.id}", :controller => 'issues', :action => 'show', :id => i %>: <%=h i.subject.sub(/^(.{30}[^\s]*\s).*$/, '\1 (...)') %></small><br /> <small><%= link_to_issue i %>: <%=h i.subject.sub(/^(.{30}[^\s]*\s).*$/, '\1 (...)') %></small><br />
<% end %> <% end %>
</td> </td>
<%= '</tr><tr height="100">' if day.cwday >= 7 and day!=@date_to %> <%= '</tr><tr height="100">' if day.cwday >= 7 and day!=@date_to %>
+2 -2
View File
@@ -3,6 +3,6 @@
<%= render(:partial => 'documents/document', <%= render(:partial => 'documents/document',
:collection => Document.find(:all, :collection => Document.find(:all,
:limit => 10, :limit => 10,
:order => 'documents.created_on DESC', :order => "#{Document.table_name}.created_on DESC",
:conditions => "documents.project_id in (#{@user.projects.collect{|m| m.id}.join(',')})", :conditions => "#{Document.table_name}.project_id in (#{@user.projects.collect{|m| m.id}.join(',')})",
:include => [:project])) unless @user.projects.empty? %> :include => [:project])) unless @user.projects.empty? %>
@@ -1,9 +1,9 @@
<h3><%=l(:label_assigned_to_me_issues)%></h3> <h3><%=l(:label_assigned_to_me_issues)%></h3>
<% assigned_issues = Issue.find(:all, <% assigned_issues = Issue.find(:all,
:conditions => ["assigned_to_id=?", user.id], :conditions => ["assigned_to_id=? AND #{IssueStatus.table_name}.is_closed=?", user.id, false],
:limit => 10, :limit => 10,
:include => [ :status, :project, :tracker ], :include => [ :status, :project, :tracker ],
:order => 'issues.updated_on DESC') %> :order => "#{Issue.table_name}.updated_on DESC") %>
<%= render :partial => 'issues/list_simple', :locals => { :issues => assigned_issues } %> <%= render :partial => 'issues/list_simple', :locals => { :issues => assigned_issues } %>
<% if assigned_issues.length > 0 %> <% if assigned_issues.length > 0 %>
<p><%=lwr(:label_last_updates, assigned_issues.length)%></p> <p><%=lwr(:label_last_updates, assigned_issues.length)%></p>
@@ -3,7 +3,7 @@
:conditions => ["author_id=?", user.id], :conditions => ["author_id=?", user.id],
:limit => 10, :limit => 10,
:include => [ :status, :project, :tracker ], :include => [ :status, :project, :tracker ],
:order => 'issues.updated_on DESC') %> :order => "#{Issue.table_name}.updated_on DESC") %>
<%= render :partial => 'issues/list_simple', :locals => { :issues => reported_issues } %> <%= render :partial => 'issues/list_simple', :locals => { :issues => reported_issues } %>
<% if reported_issues.length > 0 %> <% if reported_issues.length > 0 %>
<p><%=lwr(:label_last_updates, reported_issues.length)%></p> <p><%=lwr(:label_last_updates, reported_issues.length)%></p>
+2 -2
View File
@@ -3,6 +3,6 @@
<%= render (:partial => 'news/news', <%= render (:partial => 'news/news',
:collection => News.find(:all, :collection => News.find(:all,
:limit => 10, :limit => 10,
:order => 'news.created_on DESC', :order => "#{News.table_name}.created_on DESC",
:conditions => "news.project_id in (#{@user.projects.collect{|m| m.id}.join(',')})", :conditions => "#{News.table_name}.project_id in (#{@user.projects.collect{|m| m.id}.join(',')})",
:include => [:project, :author])) unless @user.projects.empty? %> :include => [:project, :author])) unless @user.projects.empty? %>
+1 -1
View File
@@ -2,7 +2,7 @@
<div class="box"> <div class="box">
<p><%= f.text_field :title, :required => true, :size => 60 %></p> <p><%= f.text_field :title, :required => true, :size => 60 %></p>
<p><%= f.text_area :summary, :cols => 60, :rows => 2 %></p> <p><%= f.text_area :summary, :cols => 60, :rows => 2 %></p>
<p><%= f.text_area :description, :required => true, :cols => 60, :rows => 15 %></p> <p><%= f.text_area :description, :required => true, :cols => 60, :rows => 15, :class => 'wiki-edit' %></p>
</div> </div>
<% if Setting.text_formatting == 'textile' %> <% if Setting.text_formatting == 'textile' %>
+4 -5
View File
@@ -24,10 +24,9 @@
</div> </div>
<% if authorize_for 'news', 'add_comment' %> <% if authorize_for 'news', 'add_comment' %>
<% form_tag({:action => 'add_comment', :id => @news}) do %> <p><%= toggle_link l(:label_comment_add), "add_comment_form", :focus => "comment_comment" %></p>
<%= error_messages_for 'comment' %> <% form_tag({:action => 'add_comment', :id => @news}, :id => "add_comment_form", :style => "display:none;") do %>
<p><label for="comment_comment"><%= l(:label_comment_add) %></label><br /> <%= text_area 'comment', 'comment', :cols => 60, :rows => 6 %>
<%= text_area 'comment', 'comment', :cols => 60, :rows => 6 %></p> <p><%= submit_tag l(:button_add) %></p>
<%= submit_tag l(:button_add) %>
<% end %> <% end %>
<% end %> <% end %>
+20 -3
View File
@@ -2,13 +2,14 @@
<div class="box"> <div class="box">
<!--[form:project]--> <!--[form:project]-->
<p><%= f.text_field :name, :required => true %></p> <p><%= f.text_field :name, :required => true %><br /><em><%= l(:text_caracters_maximum, 30) %></em></p>
<% if admin_loggedin? and !@root_projects.empty? %> <% if admin_loggedin? and !@root_projects.empty? %>
<p><%= f.select :parent_id, (@root_projects.collect {|p| [p.name, p.id]}), { :include_blank => true } %></p> <p><%= f.select :parent_id, (@root_projects.collect {|p| [p.name, p.id]}), { :include_blank => true } %></p>
<% end %> <% end %>
<p><%= f.text_area :description, :required => true, :cols => 60, :rows => 3 %></p> <p><%= f.text_area :description, :required => true, :cols => 60, :rows => 3 %><br /><em><%= l(:text_caracters_maximum, 255) %></em></p>
<p><%= f.text_field :identifier, :required => true, :size => 15, :disabled => @project.identifier_frozen? %><br /><em><%= l(:text_length_between, 3, 12) %> <%= l(:text_project_identifier_info) unless @project.identifier_frozen? %></em></p>
<p><%= f.text_field :homepage, :size => 40 %></p> <p><%= f.text_field :homepage, :size => 40 %></p>
<p><%= f.check_box :is_public %></p> <p><%= f.check_box :is_public %></p>
@@ -30,12 +31,28 @@
<%= hidden_field_tag "repository_enabled", 0 %> <%= hidden_field_tag "repository_enabled", 0 %>
<div id="repository"> <div id="repository">
<% fields_for :repository, @project.repository, { :builder => TabularFormBuilder, :lang => current_language} do |repository| %> <% fields_for :repository, @project.repository, { :builder => TabularFormBuilder, :lang => current_language} do |repository| %>
<p><%= repository.text_field :url, :size => 60, :required => true %><br />(http://, https://, svn://, file:///)</p> <p><%= repository.text_field :url, :size => 60, :required => true, :disabled => (@project.repository && !@project.repository.root_url.blank?) %><br />(http://, https://, svn://, file:///)</p>
<p><%= repository.text_field :login, :size => 30 %></p>
<p><%= repository.password_field :password, :size => 30 %></p>
<% end %> <% end %>
</div> </div>
<%= javascript_tag "Element.hide('repository');" if @project.repository.nil? %> <%= javascript_tag "Element.hide('repository');" if @project.repository.nil? %>
</div> </div>
<div class="box">
<h3><%= check_box_tag "wiki_enabled", 1, !@project.wiki.nil?, :onclick => "Element.toggle('wiki');" %> <%= l(:label_wiki) %></h3>
<%= hidden_field_tag "wiki_enabled", 0 %>
<div id="wiki">
<% fields_for :wiki, @project.wiki, { :builder => TabularFormBuilder, :lang => current_language} do |wiki| %>
<p><%= wiki.text_field :start_page, :size => 60, :required => true %></p>
<% # content_tag("div", "", :id => "wiki_start_page_auto_complete", :class => "auto_complete") +
# auto_complete_field("wiki_start_page", { :url => { :controller => 'wiki', :action => 'auto_complete_for_wiki_page', :id => @project } })
%>
<% end %>
</div>
<%= javascript_tag "Element.hide('wiki');" if @project.wiki.nil? %>
</div>
<% content_for :header_tags do %> <% content_for :header_tags do %>
<%= javascript_include_tag 'calendar/calendar' %> <%= javascript_include_tag 'calendar/calendar' %>
<%= javascript_include_tag "calendar/lang/calendar-#{current_language}.js" %> <%= javascript_include_tag "calendar/lang/calendar-#{current_language}.js" %>

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