Compare commits

..

311 Commits

Author SHA1 Message Date
Jean-Philippe Lang
1fe90cbfb5 tagged version 0.8.0-RC1
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/tags/0.8.0-RC1@2108 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-12-07 12:39:53 +00:00
Jean-Philippe Lang
644f03b834 Set version to stable.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2107 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-12-07 10:01:10 +00:00
Jean-Philippe Lang
1154141ee7 Merge from trunk up to r2105.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2106 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-12-07 10:00:40 +00:00
Jean-Philippe Lang
bf42d9b706 0.8 branch added
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2099 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-12-06 22:41:47 +00:00
Jean-Philippe Lang
e2050d61fd Translations updates.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2098 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-12-06 22:40:50 +00:00
Jean-Philippe Lang
1dfe63093b Slight change to css so that gravatar is vertically centered on user's page.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2097 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-12-06 18:01:20 +00:00
Jean-Philippe Lang
472ec31662 Fixed: CVS connexion string may not contain @.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2096 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-12-06 17:40:54 +00:00
Jean-Philippe Lang
e66945cbd8 Fixes Darcs#cat with Postgresql.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2095 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-12-06 17:20:37 +00:00
Jean-Philippe Lang
8a0a25fceb Changes issue history headings.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2094 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-12-06 11:21:10 +00:00
Eric Davis
cf77c93cb4 Added several useful hooks to the Issue sidebar
* :view_issues_sidebar_issues_bottom
* :view_issues_sidebar_planning_bottom
* :view_issues_sidebar_queries_bottom


git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2093 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-12-06 00:51:03 +00:00
Eric Davis
956437b7fe Final refactoring on Query#sql_for_field to rename v to value
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2092 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-12-05 22:56:13 +00:00
Eric Davis
a464d26e73 Bit more refactoring on Query#sql_for_field to remove multiple returns
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2091 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-12-05 22:56:08 +00:00
Eric Davis
ea77929d62 Refactor: Extracted new method Query#sql_for_field from Query#statement in
order to clean up Query#statement.


git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2090 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-12-05 22:56:03 +00:00
Eric Davis
996b2a5c1d Added :view_issues_history_journal_bottom hook
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2089 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-12-05 21:03:55 +00:00
Jean-Philippe Lang
50794b08a9 Cross-project gantt and calendar (#1157).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2088 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-12-05 15:41:32 +00:00
Eric Davis
1b4f0c38ce Added :view_issues_edit_notes_bottom hook
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2087 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-12-03 23:18:12 +00:00
Eric Davis
5f9b1b0e0b Added :controller_issues_edit_before_save hook
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2086 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-12-03 23:18:07 +00:00
Jean-Philippe Lang
93c04f2666 Fixed: wrong digest for text files under Windows (#2264).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2085 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-12-02 17:57:13 +00:00
Jean-Philippe Lang
218e1bb267 Use style attribute for setting width of table cells in progress bars (#2267).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2084 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-12-02 17:29:52 +00:00
Jean-Philippe Lang
1ce2ed065a Fixed: activity broken by r2066 with postgresql (#2266).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2083 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-12-02 17:16:06 +00:00
Jean-Philippe Lang
d3386a5af7 Fixed: 404 when "Apply" clicked on activity page (#2251).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2082 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-12-01 17:27:44 +00:00
winterheart
5330ca9b8c ru.yml update
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2081 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-12-01 16:11:05 +00:00
winterheart
84247e7272 Translation updates (#2249, #2250, #2252, #2254)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2080 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-12-01 16:00:54 +00:00
Jean-Philippe Lang
e7b6a56a97 Replaces User.find_active with a named scope.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2079 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-30 16:57:56 +00:00
Jean-Philippe Lang
9572699301 Changes Portuguese decimal separator (#1372).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2078 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-30 16:34:39 +00:00
Jean-Philippe Lang
84e70634fb Adds To and Cc as watchers when submitting an issue by email (#2245).
Only works if the sender has the 'Add issue watchers' permission.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2077 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-30 16:00:45 +00:00
Jean-Philippe Lang
94899e0339 Adds --status option to rdm-mailhandler.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2076 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-30 15:51:44 +00:00
Jean-Philippe Lang
abacc2754e Adds status option to email integration rake tasks.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2075 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-30 14:57:46 +00:00
Jean-Philippe Lang
2fa8030afc Mail handler: check workflow for status set/change.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2074 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-30 14:55:45 +00:00
Jean-Philippe Lang
0b2299c7d1 Adds link to user's account on issue history.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2073 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-30 14:31:01 +00:00
Jean-Philippe Lang
5f3f2e756b Obfuscates email address on user's account page using javascript.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2072 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-30 14:23:57 +00:00
Jean-Philippe Lang
dfc7a94578 Slight changes to profile on account page and last connexion date added.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2071 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-30 13:42:15 +00:00
Jean-Philippe Lang
0399b85ab6 Adds links between account and user's activity pages.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2070 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-30 13:38:07 +00:00
Jean-Philippe Lang
9e5edfb728 Adds atom feed on user's account page.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2069 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-30 12:18:59 +00:00
Jean-Philippe Lang
b8b473244c Fixes activity atom link params (when not on first page).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2068 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-30 12:14:12 +00:00
Jean-Philippe Lang
6e777b7453 Makes activity view accept a user_id param to show user's activity (#1002).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2067 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-30 12:12:06 +00:00
Jean-Philippe Lang
fce4615f10 Display latest user's activity on account/show view.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2066 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-30 11:18:22 +00:00
winterheart
b5fcea9e74 Italian update (#2239)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2063 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-28 15:44:59 +00:00
Jean-Philippe Lang
5014b23c2a Fixed: inappropriate redirection to login or register page may occur (#2206). Eg. user clicks login link twice before logging in.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2062 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-27 20:15:45 +00:00
Jean-Philippe Lang
a6b6dc60f4 Typo in gloc:update task description (#2243).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2061 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-27 18:43:18 +00:00
Jean-Philippe Lang
9b1efcfaab Typo in lang files (#2241).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2060 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-27 18:41:40 +00:00
Jean-Philippe Lang
546b98a118 Adds a css class on menu items in order to apply item specific styles (eg. icons).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2059 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-27 18:04:48 +00:00
winterheart
7a441c1bc4 rake gloc:update, update for Serbian (#2232)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2057 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-26 17:32:56 +00:00
Jean-Philippe Lang
c54f15e35f Do not request blank LDAP attributes.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2056 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-25 19:33:41 +00:00
Jean-Philippe Lang
db37e578f2 Slight tests fixes.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2055 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-25 17:37:41 +00:00
Jean-Philippe Lang
9e1192a54d Fixed date filters accuracy with SQLite (#2221).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2054 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-23 16:40:35 +00:00
Jean-Philippe Lang
06266c8fec Extends child_pages macro to display child pages based on page parameter (#1975).
It can also be called from anywhere now (not only from wiki pages).

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2053 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-22 11:44:07 +00:00
winterheart
af7e586b06 removing BOM, sorting, #2169
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2052 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-21 16:40:11 +00:00
winterheart
0b94f4afa9 fix for Polish, #2215
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2051 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-21 16:35:00 +00:00
Jean-Philippe Lang
5a51412e6a Remove eclipse files
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2048 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-19 19:38:19 +00:00
winterheart
752d574b49 Typo in sv, #2213
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2047 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-19 15:52:09 +00:00
Jean-Philippe Lang
a303b5e2d5 Fixed: Printing long roadmap doesn't split across pages (#2203).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2046 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-18 21:11:25 +00:00
Jean-Philippe Lang
e61916ea91 SubversionAdapter#entries performance improvement.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2045 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-18 18:36:47 +00:00
Jean-Philippe Lang
86504acb4c Vietnamese language updated (#2125).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2044 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-18 17:22:28 +00:00
Jean-Philippe Lang
3fdc273252 Do not query multiple times git for branch (#1435).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2043 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-17 17:27:08 +00:00
Jean-Philippe Lang
9882217847 Adds Plugin#requires_redmine method so that plugin compatibility can be checked against current Redmine version (#2162).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2042 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-16 20:00:20 +00:00
Jean-Philippe Lang
5362a85f2b Adds url and author_url plugin attributes (#2162).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2041 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-16 17:12:02 +00:00
Jean-Philippe Lang
f9ce4ff559 Adds a few Plugin tests.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2040 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-16 16:08:25 +00:00
Jean-Philippe Lang
fefc6e6bec Adds .find and .all Plugin class methods.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2039 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-16 15:38:37 +00:00
Jean-Philippe Lang
97252c26ee Adds plugin id attribute.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2038 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-16 15:22:48 +00:00
Jean-Philippe Lang
94b5bbcb5d Moves plugin list to its own administration menu item.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2037 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-16 11:58:41 +00:00
Jean-Philippe Lang
9c09fd20fb Changes version naming rule (#2162).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2036 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-16 11:49:37 +00:00
winterheart
79b1347ba5 Translation updates (#2189, #2193)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2035 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-15 08:35:17 +00:00
winterheart
0cef17ce44 Polish update, #2188
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2034 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-14 15:33:27 +00:00
winterheart
d1b1f5977a Translation updates (#2168, #2172, #2176, #2178)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2033 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-14 15:18:13 +00:00
winterheart
33274b8d48 Missed %s in label, thank Martin Bächtold for reporting (#2186)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2032 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-14 15:00:23 +00:00
Jean-Philippe Lang
7c1d8ac00b Fixes #2170: user display format in application settings broken by r2010.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2031 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-13 16:43:39 +00:00
Jean-Philippe Lang
243027e63a Fixes #2171: issue pdf export broken by r2006.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2030 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-13 16:39:50 +00:00
Eric Davis
9d810f0059 Changed the CSS clear on journals so they will wrap around the revisions. #2165
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2029 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-13 01:07:58 +00:00
winterheart
eaedf9ed52 update for ru
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2028 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-12 15:34:11 +00:00
winterheart
c894f74c5e Populating new strings for sk.yml
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2027 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-12 15:23:52 +00:00
winterheart
8d8315d8f6 New file for sk (#2126)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2026 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-12 15:22:57 +00:00
winterheart
7f63f9a931 Populating new strings for zh.yml
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2025 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-12 15:18:55 +00:00
winterheart
27af58ef1a update for zh (#2151)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2024 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-12 15:17:47 +00:00
winterheart
fb4bab748f update for pt-br (#2164)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2023 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-12 15:13:49 +00:00
Jean-Philippe Lang
d771847078 Pluggable admin menu (patch #2031 by Yuki Sonoda with slight changes).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2022 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-11 18:10:21 +00:00
Jean-Philippe Lang
3f4defe482 Hungarian language file updated.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2021 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-11 16:49:20 +00:00
Jean-Philippe Lang
737bbb3d3d Less agressive Redcloth lang attribute parsing (#2091).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2020 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-11 16:26:05 +00:00
Jean-Philippe Lang
7a05f8ed66 Adds permissions to let users edit and/or delete their messages (#854, patch by Markus Knittig with slight changes).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2019 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-11 15:07:55 +00:00
Jean-Philippe Lang
cbacc71dff Turn ftps and sftp proto into links (#1514).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2018 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-11 14:24:06 +00:00
Jean-Philippe Lang
ad97165f1b Changes ruby bang path to #!/usr/bin/env ruby (#1876).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2017 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-11 13:54:10 +00:00
Jean-Philippe Lang
8be1597ad0 Updated pt-br and zh-tw lang files.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2016 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-11 13:49:07 +00:00
Jean-Philippe Lang
1169ff05af Documents Wiki page anchors (#1647).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2015 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-11 13:37:10 +00:00
Jean-Philippe Lang
876a9b6ffb Fixed: Trac milestone links not correctly converted (#2052).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2014 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-11 13:32:22 +00:00
Jean-Philippe Lang
d545ca7360 Fixed: Trac migration of ticket:123 or [ticket:34] do not work (#2053).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2013 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-11 13:28:48 +00:00
Jean-Philippe Lang
1802fa9348 Fixed: Trac migration of ticket:123 or [ticket:34] do not work (#2053).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2012 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-11 13:28:13 +00:00
Jean-Philippe Lang
29bad1dc95 Trac importer improvements (patch #2050 by Karl Heinz Marbaise).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2011 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-11 13:22:05 +00:00
Jean-Philippe Lang
6a8be88ad6 Sort users by their display names so that user dropdown lists are sorted alphabetically (#2015).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2010 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-11 12:59:28 +00:00
Jean-Philippe Lang
e8d0c26e43 Eager-load users.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2009 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-11 12:50:11 +00:00
Jean-Philippe Lang
4ed95ffff6 Fixes a typo in en.yml.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2008 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-11 12:07:03 +00:00
Jean-Philippe Lang
6f68a36679 Eager-load users.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2007 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-10 19:09:00 +00:00
Jean-Philippe Lang
79c33bbc83 Maps repository users to Redmine users (#1383).
Users with same username or email are automatically mapped. Mapping can be manually adjusted in repository settings. Multiple usernames can be mapped to the same Redmine user.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2006 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-10 18:59:06 +00:00
Jean-Philippe Lang
f6b2be81b9 Include GLoc in hook listener base class (#2112).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2005 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-10 12:23:54 +00:00
Jean-Philippe Lang
486ecc6661 Fixed: non-ASCII subversion path can't be displayed (patch #1993 by Chaoqun Zou).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2004 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-10 11:33:04 +00:00
Jean-Philippe Lang
5166213fd3 Hide Redmine version in atom feeds and pdf properties (#794).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2003 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-09 20:39:49 +00:00
Jean-Philippe Lang
57a4ee318a Link to activity view when displaying dates.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2002 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-09 17:56:20 +00:00
Jean-Philippe Lang
d63746ab3e Fixes activity date param.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2001 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-09 17:53:30 +00:00
Jean-Philippe Lang
7619c286cc Changes ApplicationHelper#gravatar_for_mail to #avatar that takes a User or a String (less code in views).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2000 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-09 14:52:16 +00:00
Jean-Philippe Lang
6157d20ec0 Git adapter: use commit time instead of author time (#2108).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1999 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-09 12:07:35 +00:00
winterheart
858eba2f1c populating new string, updates for ru.yml and sv.yml (#2126)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1998 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-09 00:29:20 +00:00
winterheart
365d92ebf4 #2126, initial support of Slovak, thank to Stanislav Pach for translation
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1997 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-08 22:34:41 +00:00
Jean-Philippe Lang
11b9ceb054 git path reverted.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1996 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-08 17:15:18 +00:00
Jean-Philippe Lang
9352bb527b Tells git to output dates in ISO format.
Fixes: Git Adapter date parsing ignores timezone (#2149).

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1995 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-08 15:50:51 +00:00
Jean-Philippe Lang
47475acdaf Fixed Bazaar shared repository browsing (#2101, patch #1685 by Dmitry Shaposhnik).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1994 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-08 15:28:00 +00:00
Jean-Philippe Lang
9ae6e60c26 Fixes syntax highlighting broken by r1930 (#2143).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1993 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-08 15:18:02 +00:00
Jean-Philippe Lang
077723c90a Do not use @:skip_relative_url_root@ to generate urls in Mailer (#2122).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1992 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-08 13:25:45 +00:00
Eric Davis
81eee10d5b Removing the custom Redmine hook in routes in favor of Engine's hook.
* Plugins' routes.rb are now added automatically to Redmine's routing,
  including the ability to override Redmine's default routing.

  Thank you to Jean-Baptiste Barth for the suggestion.  #2142


git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1991 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-08 00:12:43 +00:00
Jean-Philippe Lang
63c72fe87f Fixed: broken subject when submitting issue via email written in japanese (Patch #2059 by Go MAEDA).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1990 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-07 17:27:56 +00:00
Jean-Philippe Lang
006337fb0e Host setting should contain the path prefix (Redmine base URL) to properly generate links in emails that are sent offline (#2122).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1989 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-07 15:37:17 +00:00
Jean-Philippe Lang
4581baa5c0 Email address should be lowercased for gravatar (#2145).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1988 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-07 14:35:18 +00:00
Jean-Philippe Lang
203cf6333e French translation update.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1987 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-07 13:08:01 +00:00
winterheart
b7c1a5c0d5 Update for pl.yml, #1299
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1986 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-07 11:28:29 +00:00
winterheart
0ecca9d8f1 D'oh...
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1985 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-07 11:22:26 +00:00
winterheart
c58298835d refreshing vn.yml (#2125)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1984 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-07 11:20:25 +00:00
winterheart
42452a535f Intial support Vietnamese language (#2125), thanks to Kỳ Anh Huỳnh for work (now - really)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1983 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-07 11:12:12 +00:00
winterheart
d32b9b92cf Ooops, wrong.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1982 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-07 11:07:25 +00:00
winterheart
de31cd9ef6 Intial support Vietnamese language (#2125), thanks to Kỳ Anh Huỳnh for work
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1981 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-07 10:53:09 +00:00
winterheart
a9295a7740 #2127, #2129, #2130, #2135, translation updates. Thanks to all participants :)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1980 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-07 10:41:10 +00:00
Eric Davis
8b3a8ac1b4 Included Redmine::Hook::Helper to ActionController::Base so call_hook
is available in all controllers. #2111


git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1979 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-06 05:37:29 +00:00
Eric Davis
b1cb9cd3bd Added :view_projects_form plugin hook
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1978 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-04 18:27:13 +00:00
winterheart
76d33e1fb7 #2121, pt-br update
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1977 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-01 16:42:49 +00:00
winterheart
0dc6a41832 update ru.yml
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1976 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-10-31 14:58:05 +00:00
winterheart
34b86628a0 Update zh-tw, #2116
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1975 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-10-31 14:49:33 +00:00
winterheart
66b9a76c7e Update pt-rb, #2105
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1974 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-10-31 14:48:07 +00:00
winterheart
bbc43e71ee Populating new string with rake gloc:update
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1973 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-10-31 14:38:09 +00:00
Eric Davis
ba20a67873 Added an option to turn user Gravatars on or off
* Option can be found in Administration > General, called
  "Use Gravatar user icons"
* Defaulting Gravatars to off
* Added a helper gravatar_for_mail to check the setting before rendering
  the Gravatar.

  #1776


git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1972 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-10-31 00:41:28 +00:00
Eric Davis
048fa5bd77 Tighened up the gravator CSS in the issue div
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1971 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-10-31 00:19:48 +00:00
Eric Davis
3bc2a5cf2f Tweaking of the CSS for the gravatars. #1776
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1970 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-10-31 00:09:36 +00:00
Eric Davis
41dba2db86 Link the version name to VersionsController#show in the issue list.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1969 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-10-30 03:49:04 +00:00
Eric Davis
e85a9f8609 Fixed typo in an English string, 'View calender'
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1968 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-10-30 03:29:30 +00:00
Eric Davis
b84dd8ec83 Added gravatar image to the user's public account page
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1967 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-10-30 02:58:34 +00:00
Eric Davis
01fd8e685b Fixed a bug with using gravatar on a nil value.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1966 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-10-30 02:58:28 +00:00
Eric Davis
1a6595aa17 Reduced the size of the gravatar on the issue history
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1965 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-10-30 02:58:23 +00:00
Eric Davis
9373f429d0 styling tweaks for gravatars
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1964 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-10-30 02:58:16 +00:00
Eric Davis
b3442a7d39 styling tweaks for gravatars
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1963 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-10-30 02:58:10 +00:00
Eric Davis
ed314caf7d Gravatar support for issue detai, user grid, and activity stream
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1962 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-10-30 02:58:04 +00:00
winterheart
1399f3dd12 Update for ru.yml
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1961 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-10-28 23:31:14 +00:00
Eric Davis
7c99f8ad11 Added :view_timelog_edit_form_bottom hook to the timelog/edit form.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1960 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-10-28 20:29:38 +00:00
winterheart
67159c12c5 #2080, #2097, #2100 - ja, zh-tw, zh updates
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1959 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-10-28 16:08:19 +00:00
Jean-Philippe Lang
0f1787ea0d Fixed: Inline images don't work if file name has upper case letters or if image is in BMP format (#2102).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1958 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-10-28 10:43:34 +00:00
Jean-Philippe Lang
6cd4f0c07e Makes GLoc language global.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1957 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-10-27 12:34:01 +00:00
Jean-Philippe Lang
d143019dbb Adds back textile acronyms support (#2077).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1956 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-10-27 11:50:23 +00:00
Jean-Philippe Lang
a3b9a5aa5f Makes wiki text formatter pluggable.
Original patch #2025 by Yuki Sonoda slightly edited.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1955 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-10-27 11:08:29 +00:00
Jean-Philippe Lang
9b624fd1d6 Slight change to english string (#2088).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1954 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-10-26 15:17:26 +00:00
Jean-Philippe Lang
8236563d6b Check that git changeset is not in the database before creating it (#1419).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1953 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-10-25 10:23:29 +00:00
Jean-Philippe Lang
5f9f6ea2e1 Adds #delete_menu_item to the plugin API (#2087).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1952 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-10-25 09:55:31 +00:00
Jean-Philippe Lang
738fc579f0 Renames template ruby files to erb.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1951 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-10-25 09:35:51 +00:00
Eric Davis
0316fde06b Added the board's description below the board's name.
Thanks to Go MAEDA for the patch.  #2079


git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1950 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-10-25 04:46:21 +00:00
Eric Davis
bcde2f4dce Renamed the .rb files in the plugin_generator to end in .erb. The .rb was
causing rdoc to try to document them and fail.

* Updated the generator's manifest to use the new files
* Renamed template README to README.rdoc

  #2011


git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1949 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-10-25 04:37:31 +00:00
Eric Davis
48ae6f38e1 Added hook :view_repositories_show_contextual to allow adding items to the
repository's contextual menu.

  #2073


git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1948 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-10-25 04:21:57 +00:00
Jean-Philippe Lang
16eda4c5c9 Adds the ability to search for a project name or identifier on the administration projects list.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1947 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-10-24 17:12:39 +00:00
Jean-Philippe Lang
b4101c8b65 Adds functional test for user search.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1946 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-10-24 17:01:42 +00:00
Jean-Philippe Lang
d967507dcb Adds the ability to search for a user on the administration users list.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1945 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-10-24 16:59:15 +00:00
Jean-Philippe Lang
d1b5bc1e82 AuthSource list: display associated users count and disable 'Delete' buton if any (#2041).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1944 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-10-24 15:39:40 +00:00
Jean-Philippe Lang
6c54b0ba3b Makes permission screens localized (#2070).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1943 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-10-24 15:24:35 +00:00
winterheart
bdbc7b337f #1928 it.yml update
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1942 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-10-23 15:24:16 +00:00
Jean-Philippe Lang
ff449b197f Fixed: textile footnotes no longer work after r1113 (#974).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1941 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-10-18 11:25:27 +00:00
Jean-Philippe Lang
a0c29d8f99 link_to project homepage instead of auto_link (#1937).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1940 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-10-18 10:42:29 +00:00
Jean-Philippe Lang
58fbf5e66f Show the most recent file when displaying an inline image.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1939 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-10-18 10:18:21 +00:00
Jean-Philippe Lang
ccbe48d779 Adds 'Delete wiki pages attachments' permission.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1938 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-10-18 10:07:49 +00:00
winterheart
64e67f754d #2043, #2044, #2046, translation updates
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1937 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-10-18 10:03:50 +00:00
Eric Davis
6810972762 Added a plugin hook :routes that plugins can use to add and even override routes
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1936 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-10-17 23:30:37 +00:00
winterheart
e160a3eec1 Update for ru.yml
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1935 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-10-16 20:51:27 +00:00
winterheart
f16aae74dc #2036 update for hu.yml
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1934 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-10-16 20:50:50 +00:00
Jean-Philippe Lang
bc77cc2c5c Makes email address case-insensitive in MailHandler (#2032).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1933 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-10-16 19:13:43 +00:00
Nicolas Chuche
a4b07a36ed add plain text option for mail #2029
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1932 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-10-15 23:50:33 +00:00
Nicolas Chuche
1894bef48a bugfix to two failed tests
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1931 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-10-15 22:30:57 +00:00
Jean-Philippe Lang
2ed9aa13f3 Remove pre tag attributes.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1930 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-10-12 19:13:36 +00:00
winterheart
52e422da61 Patch #2005, nl.yml update
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1929 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-10-11 11:32:30 +00:00
winterheart
8b0016d71b Patch #2001, update for Polish language
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1928 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-10-07 17:41:16 +00:00
winterheart
df761e01fa #1992 update pt.yml, thanks to Pedro Araújo
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1927 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-10-06 15:00:56 +00:00
winterheart
07962e2840 Patch #1987, ca.yml update, thanks to Joan Duran for file
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1926 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-10-05 20:40:25 +00:00
winterheart
7fe7677cde #1988, update for ko.yml
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1925 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-10-05 19:30:58 +00:00
Jean-Philippe Lang
c4419e268f Escape image filename regexp (#1971).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1924 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-10-04 17:38:31 +00:00
Eric Davis
cf1ad65cd2 Added tests to cover IssueStatus.destroy and IssueStatus.check_integrity
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1923 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-10-02 03:23:35 +00:00
Eric Davis
193ed689fb Fixed a failing assertion in test_post_edit_with_attachment_only that would
occur when running the full test suite but not the functional test suite.


git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1922 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-10-02 02:40:29 +00:00
Eric Davis
f2f54a711d Adds :view_layouts_base_body_bottom hook
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1921 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-30 05:18:50 +00:00
Eric Davis
0092a54e3b Slight non-code change to test git sync
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1920 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-30 00:02:46 +00:00
Eric Davis
1ffa468240 Reverting slight non-code change
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1919 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-29 23:56:35 +00:00
Eric Davis
ae0ed7fd05 Slight non-code change
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1918 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-29 23:55:11 +00:00
Jean-Philippe Lang
b0e62e37ae Fixes Workflow.count_by_tracker_and_role.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1917 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-28 13:10:00 +00:00
Jean-Philippe Lang
3544d6398a Slight changes to the workflow setup screen.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1916 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-28 12:36:30 +00:00
Jean-Philippe Lang
5083e92772 Fixes workflow setup link on trackers list (follows r1914).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1915 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-28 12:20:47 +00:00
Jean-Philippe Lang
a37af7a226 Adds a workflow overview screen.
Workflow setup moved to a dedicated controller.

git-svn-id: http://redmine.rubyforge.org/svn/trunk@1914 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-28 12:03:17 +00:00
Jean-Philippe Lang
85711f1d54 Fixed: Status list on bulk edit form does not follow normal sequence (#1956).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1913 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-28 08:41:17 +00:00
Jean-Philippe Lang
050d0d046a Wrap 'Assigned to' column on the issue list (#1960).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1912 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-28 08:19:25 +00:00
Jean-Philippe Lang
925ef8f4f0 Fixed: the default status is lost when reordering issue statuses (#1955).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1911 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-28 08:05:55 +00:00
Jean-Philippe Lang
ad06bec6b6 Fixed: Latest news appear on the homepage for projects with the News module disabled (#1941).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1910 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-28 07:54:41 +00:00
winterheart
433f2a043e Fixed #1961, pt-br update
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1909 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-27 19:06:48 +00:00
Jean-Philippe Lang
394fc9c109 Fixed: cross-project issue list should not show issues of projects for which the issue tracking module was disabled.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1907 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-25 18:51:03 +00:00
Jean-Philippe Lang
21979cf68a Fixes back_url in login filter (#1900).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1905 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-24 17:33:02 +00:00
Jean-Philippe Lang
0177f732a2 Reverts r1903.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1904 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-24 17:32:49 +00:00
Jean-Philippe Lang
a3cf5dcded Fixes back_url in login filter (#1900).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1903 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-24 17:30:36 +00:00
winterheart
8bc89b6e73 Patch #1938, update for nl.yml
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1902 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-24 14:45:20 +00:00
Jean-Philippe Lang
2e7e26fbb4 Fixes html escaping.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1901 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-23 17:03:51 +00:00
Jean-Philippe Lang
9131cdf6b9 Truncate comments on changeset list.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1900 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-22 19:50:10 +00:00
winterheart
fbf2646426 #1921, pt translation
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1897 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-22 14:49:18 +00:00
Jean-Philippe Lang
edf6757a75 Typo in migration 97 name (#1929).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1896 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-21 20:38:36 +00:00
Jean-Philippe Lang
8cb75be0db Switch order of current and previous revisions in side-by-side diff (#1903).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1895 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-21 18:45:30 +00:00
Jean-Philippe Lang
b09a18972d Strip LDAP attribute names before saving (#1890).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1894 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-21 13:28:12 +00:00
Jean-Philippe Lang
8a356baf3e Unescape back_url param before calling redirect_to.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1893 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-21 12:45:22 +00:00
winterheart
e1c4659752 #1928, update for Italian language
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1892 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-21 12:32:16 +00:00
winterheart
72c9884dfc de.yml from #1745, thank to Sven Schuchmann and Thomas Löber for contribution
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1891 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-21 12:31:34 +00:00
Jean-Philippe Lang
7afdb01f00 Fixed: login filter providing incorrect back_url for Redmine installed in sub-directory (#1900).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1890 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-21 12:07:44 +00:00
Jean-Philippe Lang
1d542a5113 Fixes VersionTest class.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1889 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-21 08:54:50 +00:00
Jean-Philippe Lang
199428804f Fixed: invalid effective date (eg. 99999-01-01) causes an error on version edition screen.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1888 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-21 08:54:02 +00:00
Jean-Philippe Lang
6854827f1a Fixed: Roadmap crashes when a version has a due date > 2037.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1887 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-20 14:07:52 +00:00
winterheart
5452fd2f5a #1925, patch for lt.yml
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1885 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-19 16:33:08 +00:00
winterheart
701e00d865 #1924, update for da.yml
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1884 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-19 16:30:39 +00:00
winterheart
4e36bd2ce1 #1923, updated zh.yml
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1883 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-19 16:26:19 +00:00
winterheart
124ea898d5 patch #1922, update for nl.yml
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1882 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-19 16:23:04 +00:00
winterheart
07b69fd7fc fixed #1920, patch for Hungarian language
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1881 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-19 16:20:45 +00:00
winterheart
952ff4184e #1918, translation for zh-tw
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1880 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-19 16:17:35 +00:00
winterheart
5c449f844e update of ru.yml
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1879 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-19 16:15:49 +00:00
Jean-Philippe Lang
16e09bfd77 Adds watch/unwatch functionality at forum topic level (#1912).
Users who create/reply a topic are automatically added as watchers but are now able to unwatch the topic.

git-svn-id: http://redmine.rubyforge.org/svn/trunk@1878 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-19 15:32:52 +00:00
Jean-Philippe Lang
9e7bce6a94 Make --command option usable on Windows.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1877 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-17 19:47:36 +00:00
Jean-Philippe Lang
710ac5344d Slight change on git repository creation command.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1876 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-17 19:38:20 +00:00
Jean-Philippe Lang
36aeeb99d9 reposman: change #log arguments.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1875 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-17 19:18:31 +00:00
Jean-Philippe Lang
713ce06e40 Fixed custom query sidebar links broken by r1797 (#1899).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1873 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-17 17:23:08 +00:00
Jean-Philippe Lang
fcbd2b5228 Removes unused image references in stylesheets (#1914).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1872 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-17 17:18:05 +00:00
Jean-Philippe Lang
14b4afeec9 Fixed: http links containing parentheses fail to reder correctly (#1591). Patch by Paul Rivier.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1871 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-17 16:48:04 +00:00
Jean-Philippe Lang
3520961eae Render the commit changes list as a tree (#1896).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1870 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-17 16:39:23 +00:00
winterheart
0961cdab31 Patch #1909, updates for ru.yml
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1869 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-17 15:31:35 +00:00
Jean-Philippe Lang
a987649b1a mailhandler: fixes exit status and adds an explicit message if response code is 403.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1868 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-16 21:56:02 +00:00
Jean-Philippe Lang
9afaf26d66 Use RDoc.usage
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1867 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-16 21:54:53 +00:00
Nicolas Chuche
a07a6d4aa4 * reposman can create git repository with "--scm git" option
* light refactoring

git-svn-id: http://redmine.rubyforge.org/svn/trunk@1866 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-15 19:37:43 +00:00
winterheart
25ed19cb35 Catalan translation (#1822), thanks to Joan Duran for contribuition. Some strings has wrong quoting, I fixed that.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1865 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-15 16:07:30 +00:00
winterheart
1f862dbadb Minor typo, fixed #1897, thank Denis Tomashenko for reporting.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1864 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-15 15:22:53 +00:00
winterheart
3ef92341fd fixed #1905, patch for Hungarian language
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1863 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-15 15:19:51 +00:00
winterheart
225b6f4dc9 #1907, translation for zh
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1862 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-15 15:16:53 +00:00
winterheart
094520ab20 #1902, translation for zh-tw
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1861 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-15 15:14:34 +00:00
Nicolas Chuche
90dc93b81c bugfix
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1860 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-14 19:03:46 +00:00
winterheart
323a26030b sorting new string...
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1830 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-14 13:08:02 +00:00
winterheart
653d2aacbb fixed #1216, thank Antti Perkiömäki for reporting
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1829 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-14 12:59:12 +00:00
Jean-Philippe Lang
f6b05ea3fb Functional tests fail when run on their own (#1895).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1828 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-14 12:41:24 +00:00
Jean-Philippe Lang
3041041a62 Adds :view_wiki_edits permission to default roles.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1827 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-14 10:54:19 +00:00
winterheart
691b09fd72 Fixed #1810, one string left
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1826 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-13 19:43:27 +00:00
winterheart
f30a369d35 fixed #1839, #1814, #1747 and #1698
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1825 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-13 19:12:50 +00:00
winterheart
487ba400fc fixed #1597, thank to Alexandre da Silva for patience :)
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1824 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-13 19:05:14 +00:00
winterheart
2e0cd9f196 oops, we have newest #1849, sorry
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1823 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-13 18:55:54 +00:00
winterheart
59c961425e fixed #1832
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1822 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-13 18:52:27 +00:00
Jean-Philippe Lang
570d7e494e Fixed: unable to revert to a previous wiki page version.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1821 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-13 18:45:56 +00:00
Jean-Philippe Lang
1f1fd9e034 Expand RAILS_ROOT path on startup (#1892).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1820 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-13 18:32:37 +00:00
winterheart
35bb9307dc fixed #1818, some strings untranslated
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1819 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-13 18:12:19 +00:00
winterheart
47679629f4 patch #1639, some new strings untranslated
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1818 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-13 17:54:55 +00:00
Jean-Philippe Lang
06f4b371ee Adds Turkish translation by Ismail Sezen (#1866).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1817 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-13 17:25:01 +00:00
winterheart
c3040104ff fixed #1632, patch from Станислав Герман-Евтушенко
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1816 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-13 17:06:37 +00:00
Jean-Philippe Lang
455abea320 Adds a permission 'view wiki edits' so that wiki history can be hidden to certain users (#1154).
A migration automatically adds this permission to roles that were allowed to view wiki pages.

git-svn-id: http://redmine.rubyforge.org/svn/trunk@1815 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-13 16:45:01 +00:00
Jean-Philippe Lang
cc643ce932 Merged nbc branch @ r1812 (commit access permission and reposman improvements).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1814 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-13 16:31:11 +00:00
winterheart
6ef6459630 fixed #1635, merged changes to trunk, sorting all strings
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1813 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-13 16:30:03 +00:00
Jean-Philippe Lang
827e14eafe Mailer compatibility with Rails 2.1.1.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1811 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-13 15:33:46 +00:00
Jean-Philippe Lang
8fac9233de Engines compatibility with Rails 2.1.1 (#1886).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1810 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-13 15:33:12 +00:00
Jean-Philippe Lang
7385722441 Removes double quotes in commit link syntax (#1872).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1803 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-13 09:45:07 +00:00
Jean-Philippe Lang
b73141a153 Adds links to changesets atom feed on repository browser (#1873).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1802 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-12 17:17:25 +00:00
Jean-Philippe Lang
8d6b32645c Template error when user's timezone isn't set and UTC timestamps are used (#1889).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1801 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-11 17:45:21 +00:00
Jean-Philippe Lang
a592d6c40d Renames bundled RedCloth to RedCloth3 to avoid RedCloth 4 to be loaded instead (#1754).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1800 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-11 17:19:26 +00:00
Jean-Philippe Lang
fb151463eb Changes versions retrieval on gantt chart.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1799 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-11 17:08:00 +00:00
Jean-Philippe Lang
586f4e3831 Adds support for free ticket filtering and custom queries on Calendar.
ProjectsController#calendar moved to IssuesController.

git-svn-id: http://redmine.rubyforge.org/svn/trunk@1798 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-11 17:03:26 +00:00
Jean-Philippe Lang
2986afc05e Adds support for free ticket filtering and custom queries on Gantt chart.
ProjectsController#gantt moved to IssuesController.

git-svn-id: http://redmine.rubyforge.org/svn/trunk@1797 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-10 18:26:13 +00:00
Eric Davis
a8e3ddf382 Added context fields to the :view_projects_settings_members_table hooks
* :view_projects_settings_members_table_header now has :project
* :view_projects_settings_members_table_row now has :project
* :view_projects_settings_members_table_row has a fixed :member field


git-svn-id: http://redmine.rubyforge.org/svn/trunk@1796 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-10 01:49:51 +00:00
Eric Davis
de7d367820 Reverting commit r1748 again. r1786 pulled in in again
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1795 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-10 00:14:38 +00:00
Jean-Philippe Lang
f7acdd1afd Merged hooks branch @ r1785 into trunk.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1786 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-05 10:31:06 +00:00
Jean-Philippe Lang
4b9df2eac7 Fixes Repository#clear_changesets.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1783 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-09-04 19:35:08 +00:00
Jean-Philippe Lang
696d21f8c8 Adds cross-project time reports support (#994).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1778 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-08-31 16:34:54 +00:00
Jean-Philippe Lang
dbad26c87d Adds an option to generate sequential project identifiers.
Disabled by default, it can be enabled on the 'Projects' tab in application settings.

git-svn-id: http://redmine.rubyforge.org/svn/trunk@1777 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-08-31 12:59:57 +00:00
Nicolas Chuche
25b4139028 bug in read only access handling
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1776 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-08-31 12:11:49 +00:00
Jean-Philippe Lang
ed349ca942 Sligth changes to issue comments quoting links.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1773 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-08-28 19:04:04 +00:00
Jean-Philippe Lang
bfd0fb067a Adds posts quoting functionality (#1825).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1772 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-08-28 18:56:47 +00:00
Eric Davis
8f3a04ce69 Reverting commit r1748. Some environments are not allowing the cached file to
write to public, causing all JavaScript to fail.

Javascripts are now cached into a single file for downloads in production mode.

  #1186


git-svn-id: http://redmine.rubyforge.org/svn/trunk@1771 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-08-28 16:32:14 +00:00
Jean-Philippe Lang
6ad989f828 Adds checkboxes toggle links on permissions report.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1770 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-08-26 16:05:02 +00:00
Jean-Philippe Lang
5c6bee7f85 Simplified Chinese lang file update (#1809 by Chaoqun Zou).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1769 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-08-26 15:19:28 +00:00
Jean-Philippe Lang
d611339baa Fixes error with CVS+Postgresql and non-UTF8 commit logs (#917, #1659).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1768 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-08-26 12:28:15 +00:00
Jean-Philippe Lang
0cf15476a3 Adds support for commit logs reencoding to UTF-8 before insertion in the database (#834, #917, #1663).
Source encoding of commit logs can be selected in Application settings -> Repositories.

git-svn-id: http://redmine.rubyforge.org/svn/trunk@1767 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-08-26 12:13:15 +00:00
Jean-Philippe Lang
09eba46ec1 Fixed: document listing shows on "my page" when viewing documents is disabled for the role (#1772).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1766 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-08-26 11:08:45 +00:00
Jean-Philippe Lang
ea627ca98d One-click bulk edition using the issue list context menu within the same project (#1770).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1765 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-08-26 10:34:26 +00:00
Jean-Philippe Lang
999d47f986 Use example.net as domain in default configuration (#1762).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1764 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-08-25 16:35:20 +00:00
Jean-Philippe Lang
66a079f430 Changes help message about project identifiers (#1478).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1763 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-08-25 16:14:07 +00:00
Jean-Philippe Lang
e395d3e46d Fixes context menu with Opera (#1655).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1762 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-08-25 15:24:23 +00:00
Jean-Philippe Lang
954bddbadf Make the issue list context menu work under Mac OSX Leopard (#1485). Tested with FF3.0.1 and Safari 3.0.4 under OSX 10.5.2.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1761 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-08-25 15:09:31 +00:00
Jean-Philippe Lang
2042e3bbd9 Dots allowed in custom field name (#1723).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1760 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-08-25 14:37:44 +00:00
Jean-Philippe Lang
d93f96765b Adds support for file viewing with Darcs 2.0+ (patch #1799 by Ralph Lange slightly edited).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1759 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-08-25 14:33:30 +00:00
Jean-Philippe Lang
e339d0bcc0 Fixed: Issue updated_on is not updated when a user adds a note with no edit privilege.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1758 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-08-25 12:51:29 +00:00
Jean-Philippe Lang
dbf4438dda More detailed error message in log when scm command fails (#1682).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1757 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-08-25 12:31:53 +00:00
Jean-Philippe Lang
0d55652552 Fixes method name in AbstractAdapter.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1756 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-08-25 12:27:15 +00:00
Jean-Philippe Lang
6fc62d393c Fixed: invalid SQL query on User#destroy (#1781).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1755 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-08-25 11:55:40 +00:00
Jean-Philippe Lang
5ef0af6710 Fixed: Estimated time in issue's journal should be rounded to two decimals (#1793).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1754 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-08-25 11:43:48 +00:00
Jean-Philippe Lang
116091a1d2 Fixes platform determination under JRuby (#1804).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1753 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-08-25 11:01:37 +00:00
Eric Davis
2f3f2d8b12 Added the "Status:" keyword to the MailHandler for setting and changing an Issue status via email.
#1669


git-svn-id: http://redmine.rubyforge.org/svn/trunk@1751 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-08-20 06:21:06 +00:00
Eric Davis
6db8fa8ef7 Messages on a Board can now be sorted by the number of replies.
#1761


git-svn-id: http://redmine.rubyforge.org/svn/trunk@1750 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-08-20 06:14:44 +00:00
Eric Davis
af6b01f55d Hiding the View Differences button when a wiki page's history only has one version.
Patch contributed by Chaoqun Zou (#1743)


git-svn-id: http://redmine.rubyforge.org/svn/trunk@1749 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-08-20 05:09:13 +00:00
Eric Davis
a899904f63 Javascripts are now cached into a single file for downloads in production mode.
Thanks to Philippe Lafoucrière for the patch.  (#1186)


git-svn-id: http://redmine.rubyforge.org/svn/trunk@1748 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-08-20 04:26:46 +00:00
Jean-Philippe Lang
53078ca949 No warning about rcov each time a rake task is ran.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1747 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-08-15 18:44:46 +00:00
Eric Davis
910988133d Extracted rcov options and removed gems from the rcov report.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1745 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-08-13 04:20:23 +00:00
Eric Davis
fab5c1fdef Added rake tasks to generate rcov code coverage reports. rake -T test:coverage to see them all
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1744 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-08-13 04:20:16 +00:00
Eric Davis
ca2449d9cf Added missing documentation for setting up the Darcs test repository.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1743 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-08-13 03:54:54 +00:00
Jean-Philippe Lang
41d44c5285 Adds user count in status drop down on admin user list.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1735 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-08-11 21:10:24 +00:00
Jean-Philippe Lang
d47400aa8d Adds Lock/Unlock/Activate link on user edit screen.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1734 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-08-11 21:02:36 +00:00
Jean-Philippe Lang
988f8d65fa Adds 'Edit' link on account/show for admin users.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1733 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-08-11 20:55:17 +00:00
Eric Davis
a3e729a263 Added doc/README_FOR_APP so RDoc can be built. (#1769)
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1732 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-08-11 20:49:52 +00:00
Jean-Philippe Lang
237a3f52a2 Fixes custom fields display order at several places (#1768).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1731 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-08-11 18:24:39 +00:00
Jean-Philippe Lang
1631019026 Allow same name for custom fields on different object types.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1730 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-08-11 18:09:54 +00:00
Jean-Philippe Lang
1650920339 Adds links to forum messages using message#id syntax (#1756).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1729 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-08-10 22:18:23 +00:00
Jean-Philippe Lang
ab4873b83d Quote ids for attachment association since Trac's attachment.id is varchar (#1759).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1728 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-08-10 21:35:03 +00:00
Jean-Philippe Lang
2fdf4426cd Moves @layout 'base'@ to ApplicationController.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1727 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-08-10 15:22:54 +00:00
Jean-Philippe Lang
361138e16d Slight change to engines to let plugins override views.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1723 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-08-07 19:59:02 +00:00
Jean-Philippe Lang
2e4e9b88d2 Fixes rdm-mailhandler SSL support (#1724).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1715 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-08-03 11:07:16 +00:00
Jean-Philippe Lang
91a5fa32d7 Fixes non-matching html tag (#1734).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1714 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-08-03 09:30:56 +00:00
Jean-Philippe Lang
d1a504f768 Fixed: logtime entry duplicated when edited from parent project (#1728).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1713 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-08-03 09:27:27 +00:00
Jean-Philippe Lang
a332e0a4fe Adds permissions for viewing the watcher list and adding new watchers on the issue detail view (#398).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1712 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-08-03 09:14:43 +00:00
Jean-Philippe Lang
6034067d86 Fixed: activity atom feed broken by r1701 (#1703).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1711 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-07-30 18:33:35 +00:00
Jean-Philippe Lang
3197814c62 Fixed: RedCloth#block_markdown_rule freezes when parsing many hyphen marks (#1704).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1710 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-07-30 18:28:01 +00:00
335 changed files with 14581 additions and 6239 deletions

View File

@@ -15,11 +15,19 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class AWSProjectWithRepository < ActionWebService::Struct
member :id, :int
member :identifier, :string
member :name, :string
member :is_public, :bool
member :repository, Repository
end
class SysApi < ActionWebService::API::Base
api_method :projects,
api_method :projects_with_repository_enabled,
:expects => [],
:returns => [[Project]]
:returns => [[AWSProjectWithRepository]]
api_method :repository_created,
:expects => [:string, :string],
:expects => [:string, :string, :string],
:returns => [:int]
end

View File

@@ -1,5 +1,5 @@
# redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang
# Redmine - project management software
# Copyright (C) 2006-2008 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -16,7 +16,6 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class AccountController < ApplicationController
layout 'base'
helper :custom_fields
include CustomFieldsHelper
@@ -25,13 +24,17 @@ class AccountController < ApplicationController
# Show user's account
def show
@user = User.find_active(params[:id])
@custom_values = @user.custom_values.find(:all, :include => :custom_field)
@user = User.active.find(params[:id])
@custom_values = @user.custom_values
# show only public projects and private projects that the logged in user is also a member of
@memberships = @user.memberships.select do |membership|
membership.project.is_public? || (User.current.member_of?(membership.project))
end
events = Redmine::Activity::Fetcher.new(User.current, :author => @user).events(nil, nil, :limit => 10)
@events_by_day = events.group_by(&:event_date)
rescue ActiveRecord::RecordNotFound
render_404
end

View File

@@ -16,7 +16,6 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class AdminController < ApplicationController
layout 'base'
before_filter :require_admin
helper :sort
@@ -30,22 +29,30 @@ class AdminController < ApplicationController
sort_init 'name', 'asc'
sort_update
@status = params[:status] ? params[:status].to_i : 0
conditions = nil
conditions = ["status=?", @status] unless @status == 0
@status = params[:status] ? params[:status].to_i : 1
c = ARCondition.new(@status == 0 ? "status <> 0" : ["status = ?", @status])
@project_count = Project.count(:conditions => conditions)
unless params[:name].blank?
name = "%#{params[:name].strip.downcase}%"
c << ["LOWER(identifier) LIKE ? OR LOWER(name) LIKE ?", name, name]
end
@project_count = Project.count(:conditions => c.conditions)
@project_pages = Paginator.new self, @project_count,
per_page_option,
params['page']
@projects = Project.find :all, :order => sort_clause,
:conditions => conditions,
:conditions => c.conditions,
:limit => @project_pages.items_per_page,
:offset => @project_pages.current.offset
render :action => "projects", :layout => false if request.xhr?
end
def plugins
@plugins = Redmine::Plugin.all
end
# Loads the default configuration
# (roles, trackers, statuses, workflow, enumerations)
def default_configuration
@@ -81,6 +88,5 @@ class AdminController < ApplicationController
:file_repository_writable => File.writable?(Attachment.storage_path),
:rmagick_available => Object.const_defined?(:Magick)
}
@plugins = Redmine::Plugin.registered_plugins
end
end

View File

@@ -16,8 +16,11 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require 'uri'
require 'cgi'
class ApplicationController < ActionController::Base
layout 'base'
before_filter :user_setup, :check_if_login_required, :set_localization
filter_parameter_logging :password
@@ -43,7 +46,7 @@ class ApplicationController < ActionController::Base
def find_current_user
if session[:user_id]
# existing session
(User.find_active(session[:user_id]) rescue nil)
(User.active.find(session[:user_id]) rescue nil)
elsif cookies[:autologin] && Setting.autologin?
# auto-login feature
User.find_by_autologin_key(cookies[:autologin])
@@ -79,7 +82,7 @@ class ApplicationController < ActionController::Base
def require_login
if !User.current.logged?
redirect_to :controller => "account", :action => "login", :back_url => request.request_uri
redirect_to :controller => "account", :action => "login", :back_url => url_for(params)
return false
end
true
@@ -93,11 +96,15 @@ class ApplicationController < ActionController::Base
end
true
end
def deny_access
User.current.logged? ? render_403 : require_login
end
# Authorize the user for the requested action
def authorize(ctrl = params[:controller], action = params[:action])
allowed = User.current.allowed_to?({:controller => ctrl, :action => action}, @project)
allowed ? true : (User.current.logged? ? render_403 : require_login)
allowed ? true : deny_access
end
# make sure that the user is a member of the project (or admin) if project is private
@@ -117,11 +124,11 @@ class ApplicationController < ActionController::Base
end
def redirect_back_or_default(default)
back_url = params[:back_url]
back_url = CGI.unescape(params[:back_url].to_s)
if !back_url.blank?
uri = URI.parse(back_url)
# do not redirect user to another host
if uri.relative? || (uri.host == request.host)
# do not redirect user to another host or to the login or register page
if (uri.relative? || (uri.host == request.host)) && !uri.path.match(%r{/(login|account/register)})
redirect_to(back_url) and return
end
end

View File

@@ -16,7 +16,6 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class AttachmentsController < ApplicationController
layout 'base'
before_filter :find_project
def show

View File

@@ -16,7 +16,6 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class AuthSourcesController < ApplicationController
layout 'base'
before_filter :require_admin
def index

View File

@@ -16,7 +16,6 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class BoardsController < ApplicationController
layout 'base'
before_filter :find_project, :authorize
helper :messages

View File

@@ -16,7 +16,6 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class CustomFieldsController < ApplicationController
layout 'base'
before_filter :require_admin
def index

View File

@@ -16,7 +16,6 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class DocumentsController < ApplicationController
layout 'base'
before_filter :find_project, :only => [:index, :new]
before_filter :find_document, :except => [:index, :new]
before_filter :authorize

View File

@@ -16,7 +16,6 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class EnumerationsController < ApplicationController
layout 'base'
before_filter :require_admin
def index

View File

@@ -16,7 +16,6 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class IssueCategoriesController < ApplicationController
layout 'base'
menu_item :settings
before_filter :find_project, :authorize

View File

@@ -16,7 +16,6 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class IssueRelationsController < ApplicationController
layout 'base'
before_filter :find_project, :authorize
def new

View File

@@ -16,7 +16,6 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class IssueStatusesController < ApplicationController
layout 'base'
before_filter :require_admin
verify :method => :post, :only => [ :destroy, :create, :update, :move ],

View File

@@ -1,5 +1,5 @@
# redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang
# Redmine - project management software
# Copyright (C) 2006-2008 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -16,14 +16,13 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class IssuesController < ApplicationController
layout 'base'
menu_item :new_issue, :only => :new
before_filter :find_issue, :only => [:show, :edit, :reply, :destroy_attachment]
before_filter :find_issues, :only => [:bulk_edit, :move, :destroy]
before_filter :find_project, :only => [:new, :update_form, :preview]
before_filter :authorize, :except => [:index, :changes, :preview, :update_form, :context_menu]
before_filter :find_optional_project, :only => [:index, :changes]
before_filter :authorize, :except => [:index, :changes, :gantt, :calendar, :preview, :update_form, :context_menu]
before_filter :find_optional_project, :only => [:index, :changes, :gantt, :calendar]
accept_key_auth :index, :changes
helper :journals
@@ -177,6 +176,9 @@ class IssuesController < ApplicationController
@time_entry.attributes = params[:time_entry]
attachments = attach_files(@issue, params[:attachments])
attachments.each {|a| journal.details << JournalDetail.new(:property => 'attachment', :prop_key => a.id, :value => a.filename)}
call_hook(:controller_issues_edit_before_save, { :params => params, :issue => @issue, :time_entry => @time_entry, :journal => journal})
if (@time_entry.hours.nil? || @time_entry.valid?) && @issue.save
# Log spend time
if current_role.allowed_to?(:log_time)
@@ -223,6 +225,7 @@ class IssuesController < ApplicationController
assigned_to = (params[:assigned_to_id].blank? || params[:assigned_to_id] == 'none') ? nil : User.find_by_id(params[:assigned_to_id])
category = (params[:category_id].blank? || params[:category_id] == 'none') ? nil : @project.issue_categories.find_by_id(params[:category_id])
fixed_version = (params[:fixed_version_id].blank? || params[:fixed_version_id] == 'none') ? nil : @project.versions.find_by_id(params[:fixed_version_id])
unsaved_issue_ids = []
@issues.each do |issue|
journal = issue.init_journal(User.current, params[:notes])
@@ -233,9 +236,7 @@ class IssuesController < ApplicationController
issue.start_date = params[:start_date] unless params[:start_date].blank?
issue.due_date = params[:due_date] unless params[:due_date].blank?
issue.done_ratio = params[:done_ratio] unless params[:done_ratio].blank?
Redmine::Plugin::Hook::Manager.call_hook(:issue_bulk_edit_save, {:params => params, :issue => issue })
call_hook(:controller_issues_bulk_edit_before_save, { :params => params, :issue => issue })
# Don't save any change to the issue if the user is not authorized to apply the requested status
if (status.nil? || (issue.status.new_status_allowed_to?(status, current_role, issue.tracker) && issue.status = status)) && issue.save
# Send notification for each issue (if changed)
@@ -250,12 +251,12 @@ class IssuesController < ApplicationController
else
flash[:error] = l(:notice_failed_to_save_issues, unsaved_issue_ids.size, @issues.size, '#' + unsaved_issue_ids.join(', #'))
end
redirect_to :controller => 'issues', :action => 'index', :project_id => @project
redirect_to(params[:back_to] || {:controller => 'issues', :action => 'index', :project_id => @project})
return
end
# Find potential statuses the user could be allowed to switch issues to
@available_statuses = Workflow.find(:all, :include => :new_status,
:conditions => {:role_id => current_role.id}).collect(&:new_status).compact.uniq
:conditions => {:role_id => current_role.id}).collect(&:new_status).compact.uniq.sort
end
def move
@@ -324,25 +325,85 @@ class IssuesController < ApplicationController
redirect_to :action => 'show', :id => @issue
end
def gantt
@gantt = Redmine::Helpers::Gantt.new(params)
retrieve_query
if @query.valid?
events = []
# Issues that have start and due dates
events += Issue.find(:all,
:order => "start_date, due_date",
:include => [:tracker, :status, :assigned_to, :priority, :project],
:conditions => ["(#{@query.statement}) AND (((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)", @gantt.date_from, @gantt.date_to, @gantt.date_from, @gantt.date_to, @gantt.date_from, @gantt.date_to]
)
# Issues that don't have a due date but that are assigned to a version with a date
events += Issue.find(:all,
:order => "start_date, effective_date",
:include => [:tracker, :status, :assigned_to, :priority, :project, :fixed_version],
:conditions => ["(#{@query.statement}) AND (((start_date>=? and start_date<=?) or (effective_date>=? and effective_date<=?) or (start_date<? and effective_date>?)) and start_date is not null and due_date is null and effective_date is not null)", @gantt.date_from, @gantt.date_to, @gantt.date_from, @gantt.date_to, @gantt.date_from, @gantt.date_to]
)
# Versions
events += Version.find(:all, :include => :project,
:conditions => ["(#{@query.project_statement}) AND effective_date BETWEEN ? AND ?", @gantt.date_from, @gantt.date_to])
@gantt.events = events
end
respond_to do |format|
format.html { render :template => "issues/gantt.rhtml", :layout => !request.xhr? }
format.png { send_data(@gantt.to_image, :disposition => 'inline', :type => 'image/png', :filename => "#{@project.identifier}-gantt.png") } if @gantt.respond_to?('to_image')
format.pdf { send_data(render(:template => "issues/gantt.rfpdf", :layout => false), :type => 'application/pdf', :filename => "#{@project.nil? ? '' : "#{@project.identifier}-" }gantt.pdf") }
end
end
def calendar
if params[:year] and params[:year].to_i > 1900
@year = params[:year].to_i
if params[:month] and params[:month].to_i > 0 and params[:month].to_i < 13
@month = params[:month].to_i
end
end
@year ||= Date.today.year
@month ||= Date.today.month
@calendar = Redmine::Helpers::Calendar.new(Date.civil(@year, @month, 1), current_language, :month)
retrieve_query
if @query.valid?
events = []
events += Issue.find(:all,
:include => [:tracker, :status, :assigned_to, :priority, :project],
:conditions => ["(#{@query.statement}) AND ((start_date BETWEEN ? AND ?) OR (due_date BETWEEN ? AND ?))", @calendar.startdt, @calendar.enddt, @calendar.startdt, @calendar.enddt]
)
events += Version.find(:all, :include => :project,
:conditions => ["(#{@query.project_statement}) AND effective_date BETWEEN ? AND ?", @calendar.startdt, @calendar.enddt])
@calendar.events = events
end
render :layout => false if request.xhr?
end
def context_menu
@issues = Issue.find_all_by_id(params[:ids], :include => :project)
if (@issues.size == 1)
@issue = @issues.first
@allowed_statuses = @issue.new_statuses_allowed_to(User.current)
@assignables = @issue.assignable_users
@assignables << @issue.assigned_to if @issue.assigned_to && !@assignables.include?(@issue.assigned_to)
end
projects = @issues.collect(&:project).compact.uniq
@project = projects.first if projects.size == 1
@can = {:edit => (@project && User.current.allowed_to?(:edit_issues, @project)),
:log_time => (@project && User.current.allowed_to?(:log_time, @project)),
:update => (@issue && (User.current.allowed_to?(:edit_issues, @project) || (User.current.allowed_to?(:change_status, @project) && !@allowed_statuses.empty?))),
:update => (@project && (User.current.allowed_to?(:edit_issues, @project) || (User.current.allowed_to?(:change_status, @project) && @allowed_statuses && !@allowed_statuses.empty?))),
:move => (@project && User.current.allowed_to?(:move_issues, @project)),
:copy => (@issue && @project.trackers.include?(@issue.tracker) && User.current.allowed_to?(:add_issues, @project)),
:delete => (@project && User.current.allowed_to?(:delete_issues, @project))
}
if @project
@assignables = @project.assignable_users
@assignables << @issue.assigned_to if @issue && @issue.assigned_to && !@assignables.include?(@issue.assigned_to)
end
@priorities = Enumeration.get_values('IPRI').reverse
@statuses = IssueStatus.find(:all, :order => 'position')
@back = request.env['HTTP_REFERER']
@@ -392,9 +453,9 @@ private
end
def find_optional_project
return true unless params[:project_id]
@project = Project.find(params[:project_id])
authorize
@project = Project.find(params[:project_id]) unless params[:project_id].blank?
allowed = User.current.allowed_to?({:controller => params[:controller], :action => params[:action]}, @project, :global => true)
allowed ? true : deny_access
rescue ActiveRecord::RecordNotFound
render_404
end

View File

@@ -16,7 +16,6 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class JournalsController < ApplicationController
layout 'base'
before_filter :find_journal
def edit

View File

@@ -16,7 +16,6 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class MembersController < ApplicationController
layout 'base'
before_filter :find_member, :except => :new
before_filter :find_project, :only => :new
before_filter :authorize

View File

@@ -16,20 +16,21 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class MessagesController < ApplicationController
layout 'base'
menu_item :boards
before_filter :find_board, :only => [:new, :preview]
before_filter :find_message, :except => [:new, :preview]
before_filter :authorize, :except => :preview
before_filter :authorize, :except => [:preview, :edit, :destroy]
verify :method => :post, :only => [ :reply, :destroy ], :redirect_to => { :action => :show }
verify :xhr => true, :only => :quote
helper :watchers
helper :attachments
include AttachmentsHelper
# Show a topic and its replies
def show
@replies = @topic.children
@replies = @topic.children.find(:all, :include => [:author, :attachments, {:board => :project}])
@replies.reverse! if User.current.wants_comments_in_reverse_order?
@reply = Message.new(:subject => "RE: #{@message.subject}")
render :action => "show", :layout => false if request.xhr?
@@ -64,7 +65,8 @@ class MessagesController < ApplicationController
# Edit a message
def edit
if params[:message] && User.current.allowed_to?(:edit_messages, @project)
render_403 and return false unless @message.editable_by?(User.current)
if params[:message]
@message.locked = params[:message]['locked']
@message.sticky = params[:message]['sticky']
end
@@ -77,12 +79,27 @@ class MessagesController < ApplicationController
# Delete a messages
def destroy
render_403 and return false unless @message.destroyable_by?(User.current)
@message.destroy
redirect_to @message.parent.nil? ?
{ :controller => 'boards', :action => 'show', :project_id => @project, :id => @board } :
{ :action => 'show', :id => @message.parent }
end
def quote
user = @message.author
text = @message.content
content = "#{ll(Setting.default_language, :text_user_wrote, user)}\\n> "
content << text.to_s.strip.gsub(%r{<pre>((.|\s)*?)</pre>}m, '[...]').gsub('"', '\"').gsub(/(\r?\n|\r\n?)/, "\\n> ") + "\\n\\n"
render(:update) { |page|
page.<< "$('message_content').value = \"#{content}\";"
page.show 'reply'
page << "Form.Element.focus('message_content');"
page << "Element.scrollTo('reply');"
page << "$('message_content').scrollTop = $('message_content').scrollHeight - $('message_content').clientHeight;"
}
end
def preview
message = @board.messages.find_by_id(params[:id])
@attachements = message.attachments if message

View File

@@ -16,11 +16,10 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class MyController < ApplicationController
helper :issues
layout 'base'
before_filter :require_login
helper :issues
BLOCKS = { 'issuesassignedtome' => :label_assigned_to_me_issues,
'issuesreportedbyme' => :label_reported_issues,
'issueswatched' => :label_watched_issues,

View File

@@ -16,7 +16,6 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class NewsController < ApplicationController
layout 'base'
before_filter :find_news, :except => [:new, :index, :preview]
before_filter :find_project, :only => [:new, :preview]
before_filter :authorize, :except => [:index, :preview]

View File

@@ -16,7 +16,6 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class ProjectsController < ApplicationController
layout 'base'
menu_item :overview
menu_item :activity, :only => :activity
menu_item :roadmap, :only => :roadmap
@@ -28,7 +27,7 @@ class ProjectsController < ApplicationController
before_filter :find_optional_project, :only => :activity
before_filter :authorize, :except => [ :index, :list, :add, :archive, :unarchive, :destroy, :activity ]
before_filter :require_admin, :only => [ :add, :archive, :unarchive, :destroy ]
accept_key_auth :activity, :calendar
accept_key_auth :activity
helper :sort
include SortHelper
@@ -70,6 +69,7 @@ class ProjectsController < ApplicationController
:order => 'name')
@project = Project.new(params[:project])
if request.get?
@project.identifier = Project.next_identifier if Setting.sequential_project_identifiers?
@project.trackers = Tracker.all
@project.is_public = Setting.default_projects_public?
@project.enabled_module_names = Redmine::AccessControl.available_project_modules
@@ -221,16 +221,19 @@ class ProjectsController < ApplicationController
@days = Setting.activity_days_default.to_i
if params[:from]
begin; @date_to = params[:from].to_date; rescue; end
begin; @date_to = params[:from].to_date + 1; rescue; end
end
@date_to ||= Date.today + 1
@date_from = @date_to - @days
@with_subprojects = params[:with_subprojects].nil? ? Setting.display_subprojects_issues? : (params[:with_subprojects] == '1')
@author = (params[:user_id].blank? ? nil : User.active.find(params[:user_id]))
@activity = Redmine::Activity::Fetcher.new(User.current, :project => @project, :with_subprojects => @with_subprojects)
@activity = Redmine::Activity::Fetcher.new(User.current, :project => @project,
:with_subprojects => @with_subprojects,
:author => @author)
@activity.scope_select {|t| !params["show_#{t}"].nil?}
@activity.default_scope! if @activity.scope.empty?
@activity.scope = (@author.nil? ? :default : :all) if @activity.scope.empty?
events = @activity.events(@date_from, @date_to)
@@ -240,101 +243,18 @@ class ProjectsController < ApplicationController
render :layout => false if request.xhr?
}
format.atom {
title = (@scope.size == 1) ? l("label_#{@scope.first.singularize}_plural") : l(:label_activity)
title = l(:label_activity)
if @author
title = @author.name
elsif @activity.scope.size == 1
title = l("label_#{@activity.scope.first.singularize}_plural")
end
render_feed(events, :title => "#{@project || Setting.app_title}: #{title}")
}
end
end
def calendar
@trackers = @project.rolled_up_trackers
retrieve_selected_tracker_ids(@trackers)
if params[:year] and params[:year].to_i > 1900
@year = params[:year].to_i
if params[:month] and params[:month].to_i > 0 and params[:month].to_i < 13
@month = params[:month].to_i
end
end
@year ||= Date.today.year
@month ||= Date.today.month
@calendar = Redmine::Helpers::Calendar.new(Date.civil(@year, @month, 1), current_language, :month)
@with_subprojects = params[:with_subprojects].nil? ? Setting.display_subprojects_issues? : (params[:with_subprojects] == '1')
events = []
@project.issues_with_subprojects(@with_subprojects) do
events += Issue.find(:all,
:include => [:tracker, :status, :assigned_to, :priority, :project],
:conditions => ["((start_date BETWEEN ? AND ?) OR (due_date BETWEEN ? AND ?)) AND #{Issue.table_name}.tracker_id IN (#{@selected_tracker_ids.join(',')})", @calendar.startdt, @calendar.enddt, @calendar.startdt, @calendar.enddt]
) unless @selected_tracker_ids.empty?
events += Version.find(:all, :include => :project,
:conditions => ["effective_date BETWEEN ? AND ?", @calendar.startdt, @calendar.enddt])
end
@calendar.events = events
render :layout => false if request.xhr?
end
def gantt
@trackers = @project.rolled_up_trackers
retrieve_selected_tracker_ids(@trackers)
if params[:year] and params[:year].to_i >0
@year_from = params[:year].to_i
if params[:month] and params[:month].to_i >=1 and params[:month].to_i <= 12
@month_from = params[:month].to_i
else
@month_from = 1
end
else
@month_from ||= Date.today.month
@year_from ||= Date.today.year
end
zoom = (params[:zoom] || User.current.pref[:gantt_zoom]).to_i
@zoom = (zoom > 0 && zoom < 5) ? zoom : 2
months = (params[:months] || User.current.pref[:gantt_months]).to_i
@months = (months > 0 && months < 25) ? months : 6
# Save gantt paramters as user preference (zoom and months count)
if (User.current.logged? && (@zoom != User.current.pref[:gantt_zoom] || @months != User.current.pref[:gantt_months]))
User.current.pref[:gantt_zoom], User.current.pref[:gantt_months] = @zoom, @months
User.current.preference.save
end
@date_from = Date.civil(@year_from, @month_from, 1)
@date_to = (@date_from >> @months) - 1
@with_subprojects = params[:with_subprojects].nil? ? Setting.display_subprojects_issues? : (params[:with_subprojects] == '1')
@events = []
@project.issues_with_subprojects(@with_subprojects) do
# Issues that have start and due dates
@events += Issue.find(:all,
:order => "start_date, due_date",
:include => [:tracker, :status, :assigned_to, :priority, :project],
: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?
# Issues that don't have a due date but that are assigned to a version with a date
@events += Issue.find(:all,
:order => "start_date, effective_date",
:include => [:tracker, :status, :assigned_to, :priority, :project, :fixed_version],
:conditions => ["(((start_date>=? and start_date<=?) or (effective_date>=? and effective_date<=?) or (start_date<? and effective_date>?)) and start_date is not null and due_date is null and effective_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?
@events += Version.find(:all, :include => :project,
:conditions => ["effective_date BETWEEN ? AND ?", @date_from, @date_to])
end
@events.sort! {|x,y| x.start_date <=> y.start_date }
if params[:format]=='pdf'
@options_for_rfpdf ||= {}
@options_for_rfpdf[:file_name] = "#{@project.identifier}-gantt.pdf"
render :template => "projects/gantt.rfpdf", :layout => false
elsif params[:format]=='png' && respond_to?('gantt_image')
image = gantt_image(@events, @date_from, @months, @zoom)
image.format = 'PNG'
send_data(image.to_blob, :disposition => 'inline', :type => 'image/png', :filename => "#{@project.identifier}-gantt.png")
else
render :template => "projects/gantt.rhtml"
end
rescue ActiveRecord::RecordNotFound
render_404
end
private

View File

@@ -16,7 +16,6 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class QueriesController < ApplicationController
layout 'base'
menu_item :issues
before_filter :find_query, :except => :new
before_filter :find_optional_project, :only => :new

View File

@@ -16,7 +16,6 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class ReportsController < ApplicationController
layout 'base'
menu_item :issues
before_filter :find_project, :authorize

View File

@@ -23,7 +23,6 @@ class ChangesetNotFound < Exception; end
class InvalidRevisionParam < Exception; end
class RepositoriesController < ApplicationController
layout 'base'
menu_item :repository
before_filter :find_repository, :except => :edit
before_filter :find_project, :only => :edit
@@ -45,6 +44,20 @@ class RepositoriesController < ApplicationController
render(:update) {|page| page.replace_html "tab-content-repository", :partial => 'projects/settings/repository'}
end
def committers
@committers = @repository.committers
@users = @project.users
additional_user_ids = @committers.collect(&:last).collect(&:to_i) - @users.collect(&:id)
@users += User.find_all_by_id(additional_user_ids) unless additional_user_ids.empty?
@users.compact!
@users.sort!
if request.post?
@repository.committer_ids = params[:committers]
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'committers', :id => @project
end
end
def destroy
@repository.destroy
redirect_to :controller => 'projects', :action => 'settings', :id => @project, :tab => 'repository'
@@ -85,7 +98,8 @@ class RepositoriesController < ApplicationController
params['page']
@changesets = @repository.changesets.find(:all,
:limit => @changeset_pages.items_per_page,
:offset => @changeset_pages.current.offset)
:offset => @changeset_pages.current.offset,
:include => :user)
respond_to do |format|
format.html { render :layout => false if request.xhr? }
@@ -119,11 +133,6 @@ class RepositoriesController < ApplicationController
def revision
@changeset = @repository.changesets.find_by_revision(@rev)
raise ChangesetNotFound unless @changeset
@changes_count = @changeset.changes.size
@changes_pages = Paginator.new self, @changes_count, 150, params['page']
@changes = @changeset.changes.find(:all,
:limit => @changes_pages.items_per_page,
:offset => @changes_pages.current.offset)
respond_to do |format|
format.html

View File

@@ -16,7 +16,6 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class RolesController < ApplicationController
layout 'base'
before_filter :require_admin
verify :method => :post, :only => [ :destroy, :move ],
@@ -80,27 +79,6 @@ class RolesController < ApplicationController
redirect_to :action => 'list'
end
def workflow
@role = Role.find_by_id(params[:role_id])
@tracker = Tracker.find_by_id(params[:tracker_id])
if request.post?
Workflow.destroy_all( ["role_id=? and tracker_id=?", @role.id, @tracker.id])
(params[:issue_status] || []).each { |old, news|
news.each { |new|
@role.workflows.build(:tracker_id => @tracker.id, :old_status_id => old, :new_status_id => new)
}
}
if @role.save
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'workflow', :role_id => @role, :tracker_id => @tracker
end
end
@roles = Role.find(:all, :order => 'builtin, position')
@trackers = Tracker.find(:all, :order => 'position')
@statuses = IssueStatus.find(:all, :order => 'position')
end
def report
@roles = Role.find(:all, :order => 'builtin, position')
@permissions = Redmine::AccessControl.permissions.select { |p| !p.public? }

View File

@@ -16,8 +16,6 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class SearchController < ApplicationController
layout 'base'
before_filter :find_optional_project
helper :messages

View File

@@ -5,20 +5,19 @@
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class SettingsController < ApplicationController
layout 'base'
before_filter :require_admin
def index
edit
render :action => 'edit'
@@ -40,17 +39,21 @@ class SettingsController < ApplicationController
@options = {}
@options[:user_format] = User::USER_FORMATS.keys.collect {|f| [User.current.name(f), f.to_s] }
@deliveries = ActionMailer::Base.perform_deliveries
@guessed_host_and_path = request.host_with_port
@guessed_host_and_path << ('/'+ request.relative_url_root.gsub(%r{^\/}, '')) unless request.relative_url_root.blank?
end
def plugin
plugin_id = params[:id].to_sym
@plugin = Redmine::Plugin.registered_plugins[plugin_id]
@plugin = Redmine::Plugin.find(params[:id])
if request.post?
Setting["plugin_#{plugin_id}"] = params[:settings]
Setting["plugin_#{@plugin.id}"] = params[:settings]
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'plugin', :id => params[:id]
redirect_to :action => 'plugin', :id => @plugin.id
end
@partial = @plugin.settings[:partial]
@settings = Setting["plugin_#{plugin_id}"]
@settings = Setting["plugin_#{@plugin.id}"]
rescue Redmine::PluginNotFound
render_404
end
end

View File

@@ -23,18 +23,17 @@ class SysController < ActionController::Base
before_invocation :check_enabled
# Returns the projects list, with their repositories
def projects
Project.find(:all, :include => :repository)
def projects_with_repository_enabled
Project.has_module(:repository).find(:all, :include => :repository, :order => 'identifier')
end
# Registers a repository for the given project identifier
# (Subversion specific)
def repository_created(identifier, url)
def repository_created(identifier, vendor, url)
project = Project.find_by_identifier(identifier)
# Do not create the repository if the project has already one
return 0 unless project && project.repository.nil?
logger.debug "Repository for #{project.name} was created"
repository = Repository.factory('Subversion', :project => project, :url => url)
repository = Repository.factory(vendor, :project => project, :url => url)
repository.save
repository.id || 0
end

View File

@@ -16,9 +16,9 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class TimelogController < ApplicationController
layout 'base'
menu_item :issues
before_filter :find_project, :authorize
before_filter :find_project, :authorize, :only => [:edit, :destroy]
before_filter :find_optional_project, :only => [:report, :details]
verify :method => :post, :only => :destroy, :redirect_to => { :action => :details }
@@ -54,11 +54,12 @@ class TimelogController < ApplicationController
}
# Add list and boolean custom fields as available criterias
@project.all_issue_custom_fields.select {|cf| %w(list bool).include? cf.field_format }.each do |cf|
custom_fields = (@project.nil? ? IssueCustomField.for_all : @project.all_issue_custom_fields)
custom_fields.select {|cf| %w(list bool).include? cf.field_format }.each do |cf|
@available_criterias["cf_#{cf.id}"] = {:sql => "(SELECT c.value FROM #{CustomValue.table_name} c WHERE c.custom_field_id = #{cf.id} AND c.customized_type = 'Issue' AND c.customized_id = #{Issue.table_name}.id)",
:format => cf.field_format,
:label => cf.name}
end
end if @project
# Add list and boolean time entry custom fields
TimeEntryCustomField.find(:all).select {|cf| %w(list bool).include? cf.field_format }.each do |cf|
@@ -84,9 +85,10 @@ class TimelogController < ApplicationController
sql << " FROM #{TimeEntry.table_name}"
sql << " LEFT JOIN #{Issue.table_name} ON #{TimeEntry.table_name}.issue_id = #{Issue.table_name}.id"
sql << " LEFT JOIN #{Project.table_name} ON #{TimeEntry.table_name}.project_id = #{Project.table_name}.id"
sql << " WHERE (%s)" % @project.project_condition(Setting.display_subprojects_issues?)
sql << " AND (%s)" % Project.allowed_to_condition(User.current, :view_time_entries)
sql << " AND spent_on BETWEEN '%s' AND '%s'" % [ActiveRecord::Base.connection.quoted_date(@from.to_time), ActiveRecord::Base.connection.quoted_date(@to.to_time)]
sql << " WHERE"
sql << " (%s) AND" % @project.project_condition(Setting.display_subprojects_issues?) if @project
sql << " (%s) AND" % Project.allowed_to_condition(User.current, :view_time_entries)
sql << " (spent_on BETWEEN '%s' AND '%s')" % [ActiveRecord::Base.connection.quoted_date(@from.to_time), ActiveRecord::Base.connection.quoted_date(@to.to_time)]
sql << " GROUP BY #{sql_group_by}, tyear, tmonth, tweek, spent_on"
@hours = ActiveRecord::Base.connection.select_all(sql)
@@ -139,8 +141,13 @@ class TimelogController < ApplicationController
sort_update
cond = ARCondition.new
cond << (@issue.nil? ? @project.project_condition(Setting.display_subprojects_issues?) :
["#{TimeEntry.table_name}.issue_id = ?", @issue.id])
if @project.nil?
cond << Project.allowed_to_condition(User.current, :view_time_entries)
elsif @issue.nil?
cond << @project.project_condition(Setting.display_subprojects_issues?)
else
cond << ["#{TimeEntry.table_name}.issue_id = ?", @issue.id]
end
retrieve_date_range
cond << ['spent_on BETWEEN ? AND ?', @from, @to]
@@ -187,7 +194,7 @@ class TimelogController < ApplicationController
@time_entry.attributes = params[:time_entry]
if request.post? and @time_entry.save
flash[:notice] = l(:notice_successful_update)
redirect_to(params[:back_url].blank? ? {:action => 'details', :project_id => @time_entry.project} : params[:back_url])
redirect_back_or_default :action => 'details', :project_id => @time_entry.project
return
end
end
@@ -198,7 +205,7 @@ class TimelogController < ApplicationController
@time_entry.destroy
flash[:notice] = l(:notice_successful_delete)
redirect_to :back
rescue RedirectBackError
rescue ::ActionController::RedirectBackError
redirect_to :action => 'details', :project_id => @time_entry.project
end
@@ -220,6 +227,16 @@ private
render_404
end
def find_optional_project
if !params[:issue_id].blank?
@issue = Issue.find(params[:issue_id])
@project = @issue.project
elsif !params[:project_id].blank?
@project = Project.find(params[:project_id])
end
deny_access unless User.current.allowed_to?(:view_time_entries, @project, :global => true)
end
# Retrieves the date range based on predefined ranges or specific from/to param dates
def retrieve_date_range
@free_period = false
@@ -262,7 +279,7 @@ private
end
@from, @to = @to, @from if @from && @to && @from > @to
@from ||= (TimeEntry.minimum(:spent_on, :include => :project, :conditions => @project.project_condition(Setting.display_subprojects_issues?)) || Date.today) - 1
@to ||= (TimeEntry.maximum(:spent_on, :include => :project, :conditions => @project.project_condition(Setting.display_subprojects_issues?)) || Date.today)
@from ||= (TimeEntry.minimum(:spent_on, :include => :project, :conditions => Project.allowed_to_condition(User.current, :view_time_entries)) || Date.today) - 1
@to ||= (TimeEntry.maximum(:spent_on, :include => :project, :conditions => Project.allowed_to_condition(User.current, :view_time_entries)) || Date.today)
end
end

View File

@@ -16,7 +16,6 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class TrackersController < ApplicationController
layout 'base'
before_filter :require_admin
def index

View File

@@ -16,7 +16,6 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class UsersController < ApplicationController
layout 'base'
before_filter :require_admin
helper :sort
@@ -34,15 +33,19 @@ class UsersController < ApplicationController
sort_update
@status = params[:status] ? params[:status].to_i : 1
conditions = "status <> 0"
conditions = ["status=?", @status] unless @status == 0
c = ARCondition.new(@status == 0 ? "status <> 0" : ["status = ?", @status])
unless params[:name].blank?
name = "%#{params[:name].strip.downcase}%"
c << ["LOWER(login) LIKE ? OR LOWER(firstname) LIKE ? OR LOWER(lastname) LIKE ?", name, name, name]
end
@user_count = User.count(:conditions => conditions)
@user_count = User.count(:conditions => c.conditions)
@user_pages = Paginator.new self, @user_count,
per_page_option,
params['page']
@users = User.find :all,:order => sort_clause,
:conditions => conditions,
:conditions => c.conditions,
:limit => @user_pages.items_per_page,
:offset => @user_pages.current.offset

View File

@@ -16,7 +16,6 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class VersionsController < ApplicationController
layout 'base'
menu_item :roadmap
before_filter :find_project, :authorize
@@ -34,7 +33,7 @@ class VersionsController < ApplicationController
@version.destroy
redirect_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
rescue
flash[:error] = "Unable to delete version"
flash[:error] = l(:notice_unable_delete_version)
redirect_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
end

View File

@@ -16,31 +16,38 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class WatchersController < ApplicationController
layout 'base'
before_filter :require_login, :find_project, :check_project_privacy
before_filter :find_project
before_filter :require_login, :check_project_privacy, :only => [:watch, :unwatch]
before_filter :authorize, :only => :new
def add
user = User.current
@watched.add_watcher(user)
verify :method => :post,
:only => [ :watch, :unwatch ],
:render => { :nothing => true, :status => :method_not_allowed }
def watch
set_watcher(User.current, true)
end
def unwatch
set_watcher(User.current, false)
end
def new
@watcher = Watcher.new(params[:watcher])
@watcher.watchable = @watched
@watcher.save if request.post?
respond_to do |format|
format.html { redirect_to :back }
format.js { render(:update) {|page| page.replace_html 'watcher', watcher_link(@watched, user)} }
format.js do
render :update do |page|
page.replace_html 'watchers', :partial => 'watchers/watchers', :locals => {:watched => @watched}
end
end
end
rescue RedirectBackError
rescue ::ActionController::RedirectBackError
render :text => 'Watcher added.', :layout => true
end
def remove
user = User.current
@watched.remove_watcher(user)
respond_to do |format|
format.html { redirect_to :back }
format.js { render(:update) {|page| page.replace_html 'watcher', watcher_link(@watched, user)} }
end
rescue RedirectBackError
render :text => 'Watcher removed.', :layout => true
end
private
def find_project
klass = Object.const_get(params[:object_type].camelcase)
@@ -50,4 +57,14 @@ private
rescue
render_404
end
def set_watcher(user, watching)
@watched.set_watcher(user, watching)
respond_to do |format|
format.html { redirect_to :back }
format.js { render(:update) {|page| page.replace_html 'watcher', watcher_link(@watched, user)} }
end
rescue ::ActionController::RedirectBackError
render :text => (watching ? 'Watcher added.' : 'Watcher removed.'), :layout => true
end
end

View File

@@ -16,7 +16,6 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class WelcomeController < ApplicationController
layout 'base'
def index
@news = News.latest User.current

View File

@@ -18,7 +18,6 @@
require 'diff'
class WikiController < ApplicationController
layout 'base'
before_filter :find_wiki, :authorize
verify :method => :post, :only => [:destroy, :destroy_attachment, :protect], :redirect_to => { :action => :index }
@@ -39,6 +38,11 @@ class WikiController < ApplicationController
end
return
end
if params[:version] && !User.current.allowed_to?(:view_wiki_edits, @project)
# Redirects user to the current version if he's not allowed to view previous versions
redirect_to :version => nil
return
end
@content = @page.content_for_version(params[:version])
if params[:export] == 'html'
export = render_to_string :action => 'export', :layout => false
@@ -59,10 +63,13 @@ class WikiController < ApplicationController
@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?
@content.text = initial_page_content(@page) if @content.text.blank?
# don't keep previous comment
@content.comments = nil
if request.post?
if request.get?
# To prevent StaleObjectError exception when reverting to a previous version
@content.version = @page.content.version
else
if !@page.new_record? && @content.text == params[:content][:text]
# don't save if text wasn't changed
redirect_to :action => 'index', :id => @project, :page => @page.title
@@ -201,4 +208,11 @@ private
def editable?(page = @page)
page.editable_by?(User.current)
end
# Returns the default content of a new wiki page
def initial_page_content(page)
helper = Redmine::WikiFormatting.helper_for(Setting.text_formatting)
extend helper unless self.instance_of?(helper)
helper.instance_method(:initial_page_content).bind(self).call(page)
end
end

View File

@@ -16,7 +16,6 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class WikisController < ApplicationController
layout 'base'
menu_item :settings
before_filter :find_project, :authorize

View File

@@ -0,0 +1,45 @@
# Redmine - project management software
# Copyright (C) 2006-2008 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class WorkflowsController < ApplicationController
before_filter :require_admin
def index
@workflow_counts = Workflow.count_by_tracker_and_role
end
def edit
@role = Role.find_by_id(params[:role_id])
@tracker = Tracker.find_by_id(params[:tracker_id])
if request.post?
Workflow.destroy_all( ["role_id=? and tracker_id=?", @role.id, @tracker.id])
(params[:issue_status] || []).each { |old, news|
news.each { |new|
@role.workflows.build(:tracker_id => @tracker.id, :old_status_id => old, :new_status_id => new)
}
}
if @role.save
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'edit', :role_id => @role, :tracker_id => @tracker
end
end
@roles = Role.find(:all, :order => 'builtin, position')
@trackers = Tracker.find(:all, :order => 'position')
@statuses = IssueStatus.find(:all, :order => 'position')
end
end

View File

@@ -17,7 +17,7 @@
module AdminHelper
def project_status_options_for_select(selected)
options_for_select([[l(:label_all), "*"],
options_for_select([[l(:label_all), ''],
[l(:status_active), 1]], selected)
end
end

View File

@@ -5,26 +5,31 @@
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require 'coderay'
require 'coderay/helpers/file_type'
require 'forwardable'
module ApplicationHelper
include Redmine::WikiFormatting::Macros::Definitions
include GravatarHelper::PublicMethods
extend Forwardable
def_delegators :wiki_helper, :wikitoolbar_for, :heads_for_wiki_formatter
def current_role
@current_role ||= User.current.role_for_project(@project)
end
# Return true if user is authorized for controller/action, otherwise false
def authorize_for(controller, action)
User.current.allowed_to?({:controller => controller, :action => action}, @project)
@@ -35,18 +40,24 @@ module ApplicationHelper
link_to(name, options, html_options, *parameters_for_method_reference) if authorize_for(options[:controller] || params[:controller], options[:action])
end
# Display a link to remote if user is authorized
def link_to_remote_if_authorized(name, options = {}, html_options = nil)
url = options[:url] || {}
link_to_remote(name, options, html_options) if authorize_for(url[:controller] || params[:controller], url[:action])
end
# Display a link to user's account page
def link_to_user(user)
user ? link_to(user, :controller => 'account', :action => 'show', :id => user) : 'Anonymous'
(user && !user.anonymous?) ? link_to(user, :controller => 'account', :action => 'show', :id => user) : 'Anonymous'
end
def link_to_issue(issue, options={})
options[:class] ||= ''
options[:class] << ' issue'
options[:class] << ' closed' if issue.closed?
link_to "#{issue.tracker.name} ##{issue.id}", {:controller => "issues", :action => "show", :id => issue}, options
end
# Generates a link to an attachment.
# Options:
# * :text - Link text (default to attachment filename)
@@ -54,70 +65,115 @@ module ApplicationHelper
def link_to_attachment(attachment, options={})
text = options.delete(:text) || attachment.filename
action = options.delete(:download) ? 'download' : 'show'
link_to(h(text), {:controller => 'attachments', :action => action, :id => attachment, :filename => attachment.filename }, options)
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 = {})
html_options.symbolize_keys!
tag(:input, html_options.merge({
:type => "image", :src => image_path(name),
:onclick => (html_options[:onclick] ? "#{html_options[:onclick]}; " : "") + "#{function};"
tag(:input, html_options.merge({
:type => "image", :src => image_path(name),
:onclick => (html_options[:onclick] ? "#{html_options[:onclick]}; " : "") + "#{function};"
}))
end
def prompt_to_remote(name, text, param, url, html_options = {})
html_options[:onclick] = "promptToRemote('#{text}', '#{param}', '#{url_for(url)}'); return false;"
link_to name, {}, html_options
end
def format_date(date)
return nil unless date
# "Setting.date_format.size < 2" is a temporary fix (content of date_format setting changed)
@date_format ||= (Setting.date_format.blank? || Setting.date_format.size < 2 ? l(:general_fmt_date) : Setting.date_format)
date.strftime(@date_format)
end
def format_time(time, include_date = true)
return nil unless time
time = time.to_time if time.is_a?(String)
zone = User.current.time_zone
local = zone ? time.in_time_zone(zone) : (time.utc? ? time.utc_to_local : time)
local = zone ? time.in_time_zone(zone) : (time.utc? ? time.localtime : time)
@date_format ||= (Setting.date_format.blank? || Setting.date_format.size < 2 ? l(:general_fmt_date) : Setting.date_format)
@time_format ||= (Setting.time_format.blank? ? l(:general_fmt_time) : Setting.time_format)
include_date ? local.strftime("#{@date_format} #{@time_format}") : local.strftime(@time_format)
end
def format_activity_title(text)
h(truncate_single_line(text, 100))
end
def format_activity_day(date)
date == Date.today ? l(:label_today).titleize : format_date(date)
end
def format_activity_description(text)
h(truncate(text.to_s, 250).gsub(%r{<(pre|code)>.*$}m, '...'))
end
def distance_of_date_in_words(from_date, to_date = 0)
from_date = from_date.to_date if from_date.respond_to?(:to_date)
to_date = to_date.to_date if to_date.respond_to?(:to_date)
distance_in_days = (to_date - from_date).abs
lwr(:actionview_datehelper_time_in_words_day, distance_in_days)
end
def due_date_distance_in_words(date)
if date
l((date < Date.today ? :label_roadmap_overdue : :label_roadmap_due_in), distance_of_date_in_words(Date.today, date))
end
end
def render_page_hierarchy(pages, node=nil)
content = ''
if pages[node]
content << "<ul class=\"pages-hierarchy\">\n"
pages[node].each do |page|
content << "<li>"
content << link_to(h(page.pretty_title), {:controller => 'wiki', :action => 'index', :id => page.project, :page => page.title},
:title => (page.respond_to?(:updated_on) ? l(:label_updated_time, distance_of_time_in_words(Time.now, page.updated_on)) : nil))
content << "\n" + render_page_hierarchy(pages, page.id) if pages[page.id]
content << "</li>\n"
end
content << "</ul>\n"
end
content
end
# Truncates and returns the string as a single line
def truncate_single_line(string, *args)
truncate(string, *args).gsub(%r{[\r\n]+}m, ' ')
end
def html_hours(text)
text.gsub(%r{(\d+)\.(\d+)}, '<span class="hours hours-int">\1</span><span class="hours hours-dec">.\2</span>')
end
def authoring(created, author)
time_tag = content_tag('acronym', distance_of_time_in_words(Time.now, created), :title => format_time(created))
def authoring(created, author, options={})
time_tag = @project.nil? ? content_tag('acronym', distance_of_time_in_words(Time.now, created), :title => format_time(created)) :
link_to(distance_of_time_in_words(Time.now, created),
{:controller => 'projects', :action => 'activity', :id => @project, :from => created.to_date},
:title => format_time(created))
author_tag = (author.is_a?(User) && !author.anonymous?) ? link_to(h(author), :controller => 'account', :action => 'show', :id => author) : h(author || 'Anonymous')
l(:label_added_time_by, author_tag, time_tag)
l(options[:label] || :label_added_time_by, author_tag, time_tag)
end
def l_or_humanize(s)
l_has_string?("label_#{s}".to_sym) ? l("label_#{s}".to_sym) : s.to_s.humanize
def l_or_humanize(s, options={})
k = "#{options[:prefix]}#{s}".to_sym
l_has_string?(k) ? l(k) : s.to_s.humanize
end
def day_name(day)
l(:general_day_names).split(',')[day-1]
end
def month_name(month)
l(:actionview_datehelper_select_month_names).split(',')[month-1]
end
@@ -126,7 +182,7 @@ module ApplicationHelper
type = CodeRay::FileType[name]
type ? CodeRay.scan(content, type).html : h(content)
end
def to_path_param(path)
path.to_s.split(%r{[/\\]}).select {|p| !p.blank?}
end
@@ -136,51 +192,51 @@ module ApplicationHelper
url_param = params.dup
# don't reuse params if filters are present
url_param.clear if url_param.has_key?(:set_filter)
html = ''
html << link_to_remote(('&#171; ' + l(:label_previous)),
html = ''
html << link_to_remote(('&#171; ' + l(:label_previous)),
{:update => 'content',
:url => url_param.merge(page_param => paginator.current.previous),
:complete => 'window.scrollTo(0,0)'},
{:href => url_for(:params => url_param.merge(page_param => paginator.current.previous))}) + ' ' if paginator.current.previous
html << (pagination_links_each(paginator, options) do |n|
link_to_remote(n.to_s,
link_to_remote(n.to_s,
{:url => {:params => url_param.merge(page_param => n)},
:update => 'content',
:complete => 'window.scrollTo(0,0)'},
{:href => url_for(:params => url_param.merge(page_param => n))})
end || '')
html << ' ' + link_to_remote((l(:label_next) + ' &#187;'),
html << ' ' + link_to_remote((l(:label_next) + ' &#187;'),
{:update => 'content',
:url => url_param.merge(page_param => paginator.current.next),
:complete => 'window.scrollTo(0,0)'},
{:href => url_for(:params => url_param.merge(page_param => paginator.current.next))}) if paginator.current.next
unless count.nil?
html << [" (#{paginator.current.first_item}-#{paginator.current.last_item}/#{count})", per_page_links(paginator.items_per_page)].compact.join(' | ')
end
html
html
end
def per_page_links(selected=nil)
url_param = params.dup
url_param.clear if url_param.has_key?(:set_filter)
links = Setting.per_page_options_array.collect do |n|
n == selected ? n : link_to_remote(n, {:update => "content", :url => params.dup.merge(:per_page => n)},
n == selected ? n : link_to_remote(n, {:update => "content", :url => params.dup.merge(:per_page => n)},
{:href => url_for(url_param.merge(:per_page => n))})
end
links.size > 1 ? l(:label_display_per_page, links.join(', ')) : nil
end
def breadcrumb(*args)
elements = args.flatten
elements.any? ? content_tag('p', args.join(' &#187; ') + ' &#187; ', :class => 'breadcrumb') : nil
end
def html_title(*args)
if args.empty?
title = []
@@ -215,17 +271,18 @@ module ApplicationHelper
raise ArgumentError, 'invalid arguments to textilizable'
end
return '' if text.blank?
only_path = options.delete(:only_path) == false ? false : true
# when using an image link, try to use an attachment, if possible
attachments = options[:attachments] || (obj && obj.respond_to?(:attachments) ? obj.attachments : nil)
if attachments
text = text.gsub(/!((\<|\=|\>)?(\([^\)]+\))?(\[[^\]]+\])?(\{[^\}]+\})?)(\S+\.(gif|jpg|jpeg|png))!/) do |m|
attachments = attachments.sort_by(&:created_on).reverse
text = text.gsub(/!((\<|\=|\>)?(\([^\)]+\))?(\[[^\]]+\])?(\{[^\}]+\})?)(\S+\.(bmp|gif|jpg|jpeg|png))!/i) do |m|
style = $1
filename = $6
rf = Regexp.new(filename, Regexp::IGNORECASE)
rf = Regexp.new(Regexp.escape(filename), Regexp::IGNORECASE)
# search for the picture in attachments
if found = attachments.detect { |att| att.filename =~ rf }
image_url = url_for :only_path => only_path, :controller => 'attachments', :action => 'download', :id => found
@@ -237,10 +294,8 @@ module ApplicationHelper
end
end
end
text = (Setting.text_formatting == 'textile') ?
Redmine::WikiFormatting.to_html(text) { |macro, args| exec_macro(macro, obj, args) } :
simple_format(auto_link(h(text)))
text = Redmine::WikiFormatting.to_html(Setting.text_formatting, text) { |macro, args| exec_macro(macro, obj, args) }
# different methods for formatting wiki links
case options[:wiki_links]
@@ -253,11 +308,11 @@ module ApplicationHelper
else
format_wiki_link = Proc.new {|project, title, anchor| url_for(:only_path => only_path, :controller => 'wiki', :action => 'index', :id => project, :page => title, :anchor => anchor) }
end
project = options[:project] || @project || (obj && obj.respond_to?(:project) ? obj.project : nil)
# Wiki links
#
#
# Examples:
# [[mypage]]
# [[mypage|mytext]]
@@ -275,7 +330,7 @@ module ApplicationHelper
page = $2
title ||= $1 if page.blank?
end
if link_project && link_project.wiki
# extract anchor
anchor = nil
@@ -296,7 +351,7 @@ module ApplicationHelper
end
# Redmine links
#
#
# Examples:
# Issues:
# #52 -> Link to issue #52
@@ -319,7 +374,9 @@ module ApplicationHelper
# source:some/file#L120 -> Link to line 120 of the file
# source:some/file@52#L120 -> Link to line 120 of the file's revision 52
# export:some/file -> Force the download of the file
text = text.gsub(%r{([\s\(,\-\>]|^)(!)?(attachment|document|version|commit|source|export)?((#|r)(\d+)|(:)([^"\s<>][^\s<>]*?|"[^"]+?"))(?=(?=[[:punct:]]\W)|\s|<|$)}) do |m|
# Forum messages:
# message#1218 -> Link to message with id 1218
text = text.gsub(%r{([\s\(,\-\>]|^)(!)?(attachment|document|version|commit|source|export|message)?((#|r)(\d+)|(:)([^"\s<>][^\s<>]*?|"[^"]+?"))(?=(?=[[:punct:]]\W)|\s|<|$)}) do |m|
leading, esc, prefix, sep, oid = $1, $2, $3, $5 || $7, $6 || $8
link = nil
if esc.nil?
@@ -333,7 +390,7 @@ module ApplicationHelper
oid = oid.to_i
case prefix
when nil
if issue = Issue.find_by_id(oid, :include => [:project, :status], :conditions => Project.visible_by(User.current))
if issue = Issue.find_by_id(oid, :include => [:project, :status], :conditions => Project.visible_by(User.current))
link = link_to("##{oid}", {:only_path => only_path, :controller => 'issues', :action => 'show', :id => oid},
:class => (issue.closed? ? 'issue closed' : 'issue'),
:title => "#{truncate(issue.subject, 100)} (#{issue.status.name})")
@@ -349,6 +406,16 @@ module ApplicationHelper
link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
:class => 'version'
end
when 'message'
if message = Message.find_by_id(oid, :include => [:parent, {:board => :project}], :conditions => Project.visible_by(User.current))
link = link_to h(truncate(message.subject, 60)), {:only_path => only_path,
:controller => 'messages',
:action => 'show',
:board_id => message.board,
:id => message.root,
:anchor => (message.parent ? "message-#{message.id}" : nil)},
:class => 'message'
end
end
elsif sep == ':'
# removes the double quotes if any
@@ -391,10 +458,10 @@ module ApplicationHelper
end
leading + (link || "#{prefix}#{sep}#{oid}")
end
text
end
# Same as Rails' simple_format helper without using paragraphs
def simple_format_without_paragraph(text)
text.to_s.
@@ -402,7 +469,7 @@ module ApplicationHelper
gsub(/\n\n+/, "<br /><br />"). # 2+ newline -> 2 br
gsub(/([^\n]\n)(?=[^\n])/, '\1<br />') # 1 newline -> br
end
def error_messages_for(object_name, options = {})
options = options.symbolize_keys
object = instance_variable_get("@#{object_name}")
@@ -420,14 +487,14 @@ module ApplicationHelper
end
# retrieve custom values error messages
if object.errors[:custom_values]
object.custom_values.each do |v|
object.custom_values.each do |v|
v.errors.each do |attr, msg|
next if msg.nil?
msg = msg.first if msg.is_a? Array
full_messages << "&#171; " + v.custom_field.name + " &#187; " + l(msg)
end
end
end
end
content_tag("div",
content_tag(
options[:header_tag] || "span", lwr(:gui_validation_error, full_messages.length) + ":"
@@ -439,34 +506,34 @@ module ApplicationHelper
""
end
end
def lang_options_for_select(blank=true)
(blank ? [["(auto)", ""]] : []) +
(blank ? [["(auto)", ""]] : []) +
GLoc.valid_languages.collect{|lang| [ ll(lang.to_s, :general_lang_name), lang.to_s]}.sort{|x,y| x.last <=> y.last }
end
def label_tag_for(name, option_tags = nil, options = {})
label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "")
content_tag("label", label_text)
end
def labelled_tabular_form_for(name, object, options, &proc)
options[:html] ||= {}
options[:html][:class] = 'tabular' unless options[:html].has_key?(:class)
form_for(name, object, options.merge({ :builder => TabularFormBuilder, :lang => current_language}), &proc)
end
def back_url_hidden_field_tag
back_url = params[:back_url] || request.env['HTTP_REFERER']
hidden_field_tag('back_url', back_url) unless back_url.blank?
end
def check_all_links(form_name)
link_to_function(l(:button_check_all), "checkAll('#{form_name}', true)") +
" | " +
link_to_function(l(:button_uncheck_all), "checkAll('#{form_name}', false)")
link_to_function(l(:button_uncheck_all), "checkAll('#{form_name}', false)")
end
def progress_bar(pcts, options={})
pcts = [pcts, pcts] unless pcts.is_a?(Array)
pcts[1] = pcts[1] - pcts[0]
@@ -475,13 +542,13 @@ module ApplicationHelper
legend = options[:legend] || ''
content_tag('table',
content_tag('tr',
(pcts[0] > 0 ? content_tag('td', '', :width => "#{pcts[0].floor}%;", :class => 'closed') : '') +
(pcts[1] > 0 ? content_tag('td', '', :width => "#{pcts[1].floor}%;", :class => 'done') : '') +
(pcts[2] > 0 ? content_tag('td', '', :width => "#{pcts[2].floor}%;", :class => 'todo') : '')
(pcts[0] > 0 ? content_tag('td', '', :style => "width: #{pcts[0].floor}%;", :class => 'closed') : '') +
(pcts[1] > 0 ? content_tag('td', '', :style => "width: #{pcts[1].floor}%;", :class => 'done') : '') +
(pcts[2] > 0 ? content_tag('td', '', :style => "width: #{pcts[2].floor}%;", :class => 'todo') : '')
), :class => 'progress', :style => "width: #{width};") +
content_tag('p', legend, :class => 'pourcent')
end
def context_menu_link(name, url, options={})
options[:class] ||= ''
if options.delete(:selected)
@@ -497,7 +564,7 @@ module ApplicationHelper
end
link_to name, url, options
end
def calendar_for(field_id)
include_calendar_headers_tags
image_tag("calendar.png", {:id => "#{field_id}_trigger",:class => "calendar-trigger"}) +
@@ -515,26 +582,36 @@ module ApplicationHelper
end
end
end
def wikitoolbar_for(field_id)
return '' unless Setting.text_formatting == 'textile'
help_link = l(:setting_text_formatting) + ': ' +
link_to(l(:label_help), compute_public_path('wiki_syntax', 'help', 'html'),
:onclick => "window.open(\"#{ compute_public_path('wiki_syntax', 'help', 'html') }\", \"\", \"resizable=yes, location=no, width=300, height=640, menubar=no, status=no, scrollbars=yes\"); return false;")
javascript_include_tag('jstoolbar/jstoolbar') +
javascript_include_tag("jstoolbar/lang/jstoolbar-#{current_language}") +
javascript_tag("var toolbar = new jsToolBar($('#{field_id}')); toolbar.setHelpLink('#{help_link}'); toolbar.draw();")
end
def content_for(name, content = nil, &block)
@has_content ||= {}
@has_content[name] = true
super(name, content, &block)
end
def has_content?(name)
(@has_content && @has_content[name]) || false
end
# Returns the avatar image tag for the given +user+ if avatars are enabled
# +user+ can be a User or a string that will be scanned for an email address (eg. 'joe <joe@foo.bar>')
def avatar(user, options = { })
if Setting.gravatar_enabled?
email = nil
if user.respond_to?(:mail)
email = user.mail
elsif user.to_s =~ %r{<(.+?)>}
email = $1
end
return gravatar(email.to_s.downcase, options) unless email.blank? rescue nil
end
end
private
def wiki_helper
helper = Redmine::WikiFormatting.helper_for(Setting.text_formatting)
extend helper
return self
end
end

View File

@@ -47,7 +47,7 @@ module IfpdfHelper
@font_for_content = 'Arial'
@font_for_footer = 'Helvetica'
end
SetCreator("redMine #{Redmine::VERSION}")
SetCreator(Redmine::Info.app_name)
SetFont(@font_for_content)
end

View File

@@ -75,6 +75,9 @@ module IssuesHelper
when 'fixed_version_id'
v = Version.find_by_id(detail.value) and value = v.name if detail.value
v = Version.find_by_id(detail.old_value) and old_value = v.name if detail.old_value
when 'estimated_hours'
value = "%0.02f" % detail.value.to_f unless detail.value.blank?
old_value = "%0.02f" % detail.old_value.to_f unless detail.old_value.blank?
end
when 'cf'
custom_field = CustomField.find_by_id(detail.prop_key)
@@ -86,9 +89,8 @@ module IssuesHelper
when 'attachment'
label = l(:label_attachment)
end
call_hook(:helper_issues_show_detail_after_setting, {:detail => detail, :label => label, :value => value, :old_value => old_value })
Redmine::Plugin::Hook::Manager.call_hook(:issues_helper_show_details, {:detail => detail, :label => label, :value => value, :old_value => old_value })
label ||= detail.prop_key
value ||= detail.value
old_value ||= detail.old_value

View File

@@ -21,12 +21,12 @@ module JournalsHelper
editable = journal.editable_by?(User.current)
links = []
if !journal.notes.blank?
links << link_to_remote(image_tag('comment.png'),
{ :url => {:controller => 'issues', :action => 'reply', :id => journal.journalized, :journal_id => journal} },
:title => l(:button_quote)) if options[:reply_links]
links << link_to_in_place_notes_editor(image_tag('edit.png'), "journal-#{journal.id}-notes",
{ :controller => 'journals', :action => 'edit', :id => journal },
:title => l(:button_edit)) if editable
links << link_to_remote(image_tag('comment.png'),
{ :url => {:controller => 'issues', :action => 'reply', :id => journal.journalized, :journal_id => journal} },
:title => l(:button_reply)) if options[:reply_links]
end
content << content_tag('div', links.join(' '), :class => 'contextual') unless links.empty?
content << textilizable(journal, :notes)

View File

@@ -21,18 +21,6 @@ module ProjectsHelper
link_to h(version.name), { :controller => 'versions', :action => 'show', :id => version }, options
end
def format_activity_title(text)
h(truncate_single_line(text, 100))
end
def format_activity_day(date)
date == Date.today ? l(:label_today).titleize : format_date(date)
end
def format_activity_description(text)
h(truncate(text.to_s, 250).gsub(%r{<(pre|code)>.*$}m, '...'))
end
def project_settings_tabs
tabs = [{:name => 'info', :action => :edit_project, :partial => 'projects/edit', :label => :label_information_plural},
{:name => 'modules', :action => :select_project_modules, :partial => 'projects/settings/modules', :label => :label_module_plural},
@@ -45,154 +33,4 @@ module ProjectsHelper
]
tabs.select {|tab| User.current.allowed_to?(tab[:action], @project)}
end
# Generates a gantt image
# Only defined if RMagick is avalaible
def gantt_image(events, date_from, months, zoom)
date_to = (date_from >> months)-1
show_weeks = zoom > 1
show_days = zoom > 2
subject_width = 320
header_heigth = 18
# width of one day in pixels
zoom = zoom*2
g_width = (date_to - date_from + 1)*zoom
g_height = 20 * events.length + 20
headers_heigth = (show_weeks ? 2*header_heigth : header_heigth)
height = g_height + headers_heigth
imgl = Magick::ImageList.new
imgl.new_image(subject_width+g_width+1, height)
gc = Magick::Draw.new
# Subjects
top = headers_heigth + 20
gc.fill('black')
gc.stroke('transparent')
gc.stroke_width(1)
events.each do |i|
gc.text(4, top + 2, (i.is_a?(Issue) ? i.subject : i.name))
top = top + 20
end
# Months headers
month_f = date_from
left = subject_width
months.times do
width = ((month_f >> 1) - month_f) * zoom
gc.fill('white')
gc.stroke('grey')
gc.stroke_width(1)
gc.rectangle(left, 0, left + width, height)
gc.fill('black')
gc.stroke('transparent')
gc.stroke_width(1)
gc.text(left.round + 8, 14, "#{month_f.year}-#{month_f.month}")
left = left + width
month_f = month_f >> 1
end
# Weeks headers
if show_weeks
left = subject_width
height = header_heigth
if date_from.cwday == 1
# date_from is monday
week_f = date_from
else
# find next monday after date_from
week_f = date_from + (7 - date_from.cwday + 1)
width = (7 - date_from.cwday + 1) * zoom
gc.fill('white')
gc.stroke('grey')
gc.stroke_width(1)
gc.rectangle(left, header_heigth, left + width, 2*header_heigth + g_height-1)
left = left + width
end
while week_f <= date_to
width = (week_f + 6 <= date_to) ? 7 * zoom : (date_to - week_f + 1) * zoom
gc.fill('white')
gc.stroke('grey')
gc.stroke_width(1)
gc.rectangle(left.round, header_heigth, left.round + width, 2*header_heigth + g_height-1)
gc.fill('black')
gc.stroke('transparent')
gc.stroke_width(1)
gc.text(left.round + 2, header_heigth + 14, week_f.cweek.to_s)
left = left + width
week_f = week_f+7
end
end
# Days details (week-end in grey)
if show_days
left = subject_width
height = g_height + header_heigth - 1
wday = date_from.cwday
(date_to - date_from + 1).to_i.times do
width = zoom
gc.fill(wday == 6 || wday == 7 ? '#eee' : 'white')
gc.stroke('grey')
gc.stroke_width(1)
gc.rectangle(left, 2*header_heigth, left + width, 2*header_heigth + g_height-1)
left = left + width
wday = wday + 1
wday = 1 if wday > 7
end
end
# border
gc.fill('transparent')
gc.stroke('grey')
gc.stroke_width(1)
gc.rectangle(0, 0, subject_width+g_width, headers_heigth)
gc.stroke('black')
gc.rectangle(0, 0, subject_width+g_width, g_height+ headers_heigth-1)
# content
top = headers_heigth + 20
gc.stroke('transparent')
events.each do |i|
if i.is_a?(Issue)
i_start_date = (i.start_date >= date_from ? i.start_date : date_from )
i_end_date = (i.due_date <= date_to ? i.due_date : date_to )
i_done_date = i.start_date + ((i.due_date - i.start_date+1)*i.done_ratio/100).floor
i_done_date = (i_done_date <= date_from ? date_from : i_done_date )
i_done_date = (i_done_date >= date_to ? date_to : i_done_date )
i_late_date = [i_end_date, Date.today].min if i_start_date < Date.today
i_left = subject_width + ((i_start_date - date_from)*zoom).floor
i_width = ((i_end_date - i_start_date + 1)*zoom).floor # total width of the issue
d_width = ((i_done_date - i_start_date)*zoom).floor # done width
l_width = i_late_date ? ((i_late_date - i_start_date+1)*zoom).floor : 0 # delay width
gc.fill('grey')
gc.rectangle(i_left, top, i_left + i_width, top - 6)
gc.fill('red')
gc.rectangle(i_left, top, i_left + l_width, top - 6) if l_width > 0
gc.fill('blue')
gc.rectangle(i_left, top, i_left + d_width, top - 6) if d_width > 0
gc.fill('black')
gc.text(i_left + i_width + 5,top + 1, "#{i.status.name} #{i.done_ratio}%")
else
i_left = subject_width + ((i.start_date - date_from)*zoom).floor
gc.fill('green')
gc.rectangle(i_left, top, i_left + 6, top - 6)
gc.fill('black')
gc.text(i_left + 11, top + 1, i.name)
end
top = top + 20
end
# today red line
if Date.today >= date_from and Date.today <= date_to
gc.stroke('red')
x = (Date.today-date_from+1)*zoom + subject_width
gc.line(x, headers_heigth, x, headers_heigth + g_height-1)
end
gc.draw(imgl)
imgl
end if Object.const_defined?(:Magick)
end

View File

@@ -44,6 +44,8 @@ module QueriesHelper
link_to(h(value), :controller => 'issues', :action => 'show', :id => issue)
when :done_ratio
progress_bar(value, :width => '80px')
when :fixed_version
link_to(h(value), { :controller => 'versions', :action => 'show', :id => issue.fixed_version_id })
else
h(value)
end

View File

@@ -22,6 +22,12 @@ module RepositoriesHelper
txt.to_s[0,8]
end
def truncate_at_line_break(text, length = 255)
if text
text.gsub(%r{^(.{#{length}}[^\n]*)\n.+$}m, '\\1...')
end
end
def render_properties(properties)
unless properties.nil? || properties.empty?
content = ''
@@ -32,6 +38,74 @@ module RepositoriesHelper
end
end
def render_changeset_changes
changes = @changeset.changes.find(:all, :limit => 1000, :order => 'path').collect do |change|
case change.action
when 'A'
# Detects moved/copied files
if !change.from_path.blank?
change.action = @changeset.changes.detect {|c| c.action == 'D' && c.path == change.from_path} ? 'R' : 'C'
end
change
when 'D'
@changeset.changes.detect {|c| c.from_path == change.path} ? nil : change
else
change
end
end.compact
tree = { }
changes.each do |change|
p = tree
dirs = change.path.to_s.split('/').select {|d| !d.blank?}
dirs.each do |dir|
p[:s] ||= {}
p = p[:s]
p[dir] ||= {}
p = p[dir]
end
p[:c] = change
end
render_changes_tree(tree[:s])
end
def render_changes_tree(tree)
return '' if tree.nil?
output = ''
output << '<ul>'
tree.keys.sort.each do |file|
s = !tree[file][:s].nil?
c = tree[file][:c]
style = 'change'
style << ' folder' if s
style << " change-#{c.action}" if c
text = h(file)
unless c.nil?
path_param = to_path_param(@repository.relative_path(c.path))
text = link_to(text, :controller => 'repositories',
:action => 'entry',
:id => @project,
:path => path_param,
:rev => @changeset.revision) unless s || c.action == 'D'
text << " - #{c.revision}" unless c.revision.blank?
text << ' (' + link_to('diff', :controller => 'repositories',
:action => 'diff',
:id => @project,
:path => path_param,
:rev => @changeset.revision) + ') ' if c.action == 'M'
text << ' ' + content_tag('span', c.from_path, :class => 'copied-from') unless c.from_path.blank?
end
output << "<li class='#{style}'>#{text}</li>"
output << render_changes_tree(tree[file][:s]) if s
end
output << '</ul>'
output
end
def to_utf8(str)
return str if /\A[\r\n\t\x20-\x7e]*\Z/n.match(str) # for us-ascii
@encodings ||= Setting.repositories_encodings.split(',').collect(&:strip)

View File

@@ -19,6 +19,7 @@ module SettingsHelper
def administration_settings_tabs
tabs = [{:name => 'general', :partial => 'settings/general', :label => :label_general},
{:name => 'authentication', :partial => 'settings/authentication', :label => :label_authentication},
{:name => 'projects', :partial => 'settings/projects', :label => :label_project_plural},
{:name => 'issues', :partial => 'settings/issues', :label => :label_issue_tracking},
{:name => 'notifications', :partial => 'settings/notifications', :label => l(:field_mail_notification)},
{:name => 'mail_handler', :partial => 'settings/mail_handler', :label => l(:label_incoming_emails)},

View File

@@ -16,6 +16,14 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
module TimelogHelper
def render_timelog_breadcrumb
links = []
links << link_to(l(:label_project_all), {:project_id => nil, :issue_id => nil})
links << link_to(h(@project), {:project_id => @project, :issue_id => nil}) if @project
links << link_to_issue(@issue) if @issue
breadcrumb links
end
def activity_collection_for_select_options
activities = Enumeration::get_values('ACTI')
collection = []

View File

@@ -16,11 +16,12 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
module UsersHelper
def status_options_for_select(selected)
def users_status_options_for_select(selected)
user_count_by_status = User.count(:group => 'status').to_hash
options_for_select([[l(:label_all), ''],
[l(:status_active), 1],
[l(:status_registered), 2],
[l(:status_locked), 3]], selected)
["#{l(:status_active)} (#{user_count_by_status[1].to_i})", 1],
["#{l(:status_registered)} (#{user_count_by_status[2].to_i})", 2],
["#{l(:status_locked)} (#{user_count_by_status[3].to_i})", 3]], selected)
end
# Options for the new membership projects combo-box

View File

@@ -24,7 +24,7 @@ module WatchersHelper
return '' unless user && user.logged? && object.respond_to?('watched_by?')
watched = object.watched_by?(user)
url = {:controller => 'watchers',
:action => (watched ? 'remove' : 'add'),
:action => (watched ? 'unwatch' : 'watch'),
:object_type => object.class.to_s.underscore,
:object_id => object.id}
link_to_remote((watched ? l(:button_unwatch) : l(:button_watch)),
@@ -33,4 +33,9 @@ module WatchersHelper
:class => (watched ? 'icon icon-fav' : 'icon icon-fav-off'))
end
# Returns a comma separated list of users watching the given object
def watchers_list(object)
object.watcher_users.collect {|u| content_tag('span', link_to_user(u), :class => 'user') }.join(",\n")
end
end

View File

@@ -16,22 +16,6 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
module WikiHelper
def render_page_hierarchy(pages, node=nil)
content = ''
if pages[node]
content << "<ul class=\"pages-hierarchy\">\n"
pages[node].each do |page|
content << "<li>"
content << link_to(h(page.pretty_title), {:action => 'index', :page => page.title},
:title => (page.respond_to?(:updated_on) ? l(:label_updated_time, distance_of_time_in_words(Time.now, page.updated_on)) : nil))
content << "\n" + render_page_hierarchy(pages, page.id) if pages[page.id]
content << "</li>\n"
end
content << "</ul>\n"
end
content
end
def html_diff(wdiff)
words = wdiff.words.collect{|word| h(word)}

View File

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

View File

@@ -30,12 +30,14 @@ class Attachment < ActiveRecord::Base
acts_as_activity_provider :type => 'files',
:permission => :view_files,
:author_key => :author_id,
:find_options => {:select => "#{Attachment.table_name}.*",
:joins => "LEFT JOIN #{Version.table_name} ON #{Attachment.table_name}.container_type='Version' AND #{Version.table_name}.id = #{Attachment.table_name}.container_id " +
"LEFT JOIN #{Project.table_name} ON #{Version.table_name}.project_id = #{Project.table_name}.id"}
acts_as_activity_provider :type => 'documents',
:permission => :view_documents,
:author_key => :author_id,
:find_options => {:select => "#{Attachment.table_name}.*",
:joins => "LEFT JOIN #{Document.table_name} ON #{Attachment.table_name}.container_type='Document' AND #{Document.table_name}.id = #{Attachment.table_name}.container_id " +
"LEFT JOIN #{Project.table_name} ON #{Document.table_name}.project_id = #{Project.table_name}.id"}
@@ -70,7 +72,7 @@ class Attachment < ActiveRecord::Base
File.open(diskfile, "wb") do |f|
f.write(@temp_file.read)
end
self.digest = Digest::MD5.hexdigest(File.read(diskfile))
self.digest = self.class.digest(diskfile)
end
# Don't save the content type if it's longer than the authorized length
if self.content_type && self.content_type.length > 255
@@ -131,4 +133,11 @@ private
end
df
end
# Returns the MD5 digest of the file at given path
def self.digest(filename)
File.open(filename, 'rb') do |f|
Digest::MD5.hexdigest(f.read)
end
end
end

View File

@@ -38,7 +38,8 @@ class AuthSource < ActiveRecord::Base
begin
logger.debug "Authenticating '#{login}' against '#{source.name}'" if logger && logger.debug?
attrs = source.authenticate(login, password)
rescue
rescue => e
logger.error "Error during authentication: #{e.message}"
attrs = nil
end
return attrs if attrs

View File

@@ -25,6 +25,8 @@ class AuthSourceLdap < AuthSource
validates_length_of :attr_login, :attr_firstname, :attr_lastname, :attr_mail, :maximum => 30, :allow_nil => true
validates_numericality_of :port, :only_integer => true
before_validation :strip_ldap_attributes
def after_initialize
self.port = 389 if self.port == 0
end
@@ -71,7 +73,14 @@ class AuthSourceLdap < AuthSource
"LDAP"
end
private
private
def strip_ldap_attributes
[:attr_login, :attr_firstname, :attr_lastname, :attr_mail].each do |attr|
write_attribute(attr, read_attribute(attr).strip) unless read_attribute(attr).nil?
end
end
def initialize_ldap_con(ldap_user, ldap_password)
options = { :host => self.host,
:port => self.port,
@@ -82,6 +91,8 @@ private
end
def self.get_attr(entry, attr_name)
entry[attr_name].is_a?(Array) ? entry[attr_name].first : entry[attr_name]
if !attr_name.blank?
entry[attr_name].is_a?(Array) ? entry[attr_name].first : entry[attr_name]
end
end
end

View File

@@ -1,5 +1,5 @@
# redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang
# Redmine - project management software
# Copyright (C) 2006-2008 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -15,15 +15,17 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require 'iconv'
class Changeset < ActiveRecord::Base
belongs_to :repository
belongs_to :user
has_many :changes, :dependent => :delete_all
has_and_belongs_to_many :issues
acts_as_event :title => Proc.new {|o| "#{l(:label_revision)} #{o.revision}" + (o.comments.blank? ? '' : (': ' + o.comments))},
:description => :comments,
:datetime => :committed_on,
:author => :committer,
:url => Proc.new {|o| {:controller => 'repositories', :action => 'revision', :id => o.repository.project_id, :rev => o.revision}}
acts_as_searchable :columns => 'comments',
@@ -32,6 +34,7 @@ class Changeset < ActiveRecord::Base
:date_column => 'committed_on'
acts_as_activity_provider :timestamp => "#{table_name}.committed_on",
:author_key => :user_id,
:find_options => {:include => {:repository => :project}}
validates_presence_of :repository_id, :revision, :committed_on, :commit_date
@@ -43,7 +46,7 @@ class Changeset < ActiveRecord::Base
end
def comments=(comment)
write_attribute(:comments, comment.strip)
write_attribute(:comments, Changeset.normalize_comments(comment))
end
def committed_on=(date)
@@ -55,6 +58,14 @@ class Changeset < ActiveRecord::Base
repository.project
end
def author
user || committer.to_s.split('<').first
end
def before_create
self.user = repository.find_committer_user(committer)
end
def after_create
scan_comment_for_issue_ids
end
@@ -94,12 +105,11 @@ class Changeset < ActiveRecord::Base
issue.reload
# don't change the status is the issue is closed
next if issue.status.is_closed?
user = committer_user || User.anonymous
csettext = "r#{self.revision}"
if self.scmid && (! (csettext =~ /^r[0-9]+$/))
csettext = "commit:\"#{self.scmid}\""
end
journal = issue.init_journal(user, l(:text_status_changed_by_changeset, csettext))
journal = issue.init_journal(user || User.anonymous, l(:text_status_changed_by_changeset, csettext))
issue.status = fix_status
issue.done_ratio = done_ratio if done_ratio
issue.save
@@ -111,16 +121,6 @@ class Changeset < ActiveRecord::Base
self.issues = referenced_issues.uniq
end
# Returns the Redmine User corresponding to the committer
def committer_user
if committer && committer.strip =~ /^([^<]+)(<(.*)>)?$/
username, email = $1.strip, $3
u = User.find_by_login(username)
u ||= User.find_by_mail(email) unless email.blank?
u
end
end
# Returns the previous changeset
def previous
@@ -131,4 +131,25 @@ class Changeset < ActiveRecord::Base
def next
@next ||= Changeset.find(:first, :conditions => ['id > ? AND repository_id = ?', self.id, self.repository_id], :order => 'id ASC')
end
# Strips and reencodes a commit log before insertion into the database
def self.normalize_comments(str)
to_utf8(str.to_s.strip)
end
private
def self.to_utf8(str)
return str if /\A[\r\n\t\x20-\x7e]*\Z/n.match(str) # for us-ascii
encoding = Setting.commit_logs_encoding.to_s.strip
unless encoding.blank? || encoding == 'UTF-8'
begin
return Iconv.conv('UTF-8', encoding, str)
rescue Iconv::Failure
# do nothing here
end
end
str
end
end

View File

@@ -30,9 +30,9 @@ class CustomField < ActiveRecord::Base
}.freeze
validates_presence_of :name, :field_format
validates_uniqueness_of :name
validates_uniqueness_of :name, :scope => :type
validates_length_of :name, :maximum => 30
validates_format_of :name, :with => /^[\w\s\'\-]*$/i
validates_format_of :name, :with => /^[\w\s\.\'\-]*$/i
validates_inclusion_of :field_format, :in => FIELD_FORMATS.keys
def initialize(attributes = nil)
@@ -66,7 +66,7 @@ class CustomField < ActiveRecord::Base
# to move in project_custom_field
def self.for_all
find(:all, :conditions => ["is_for_all=?", true])
find(:all, :conditions => ["is_for_all=?", true], :order => 'position')
end
def type_name

View File

@@ -42,7 +42,8 @@ class Issue < ActiveRecord::Base
acts_as_event :title => Proc.new {|o| "#{o.tracker.name} ##{o.id}: #{o.subject}"},
:url => Proc.new {|o| {:controller => 'issues', :action => 'show', :id => o.id}}
acts_as_activity_provider :find_options => {:include => [:project, :author, :tracker]}
acts_as_activity_provider :find_options => {:include => [:project, :author, :tracker]},
:author_key => :author_id
validates_presence_of :subject, :description, :priority, :project, :tracker, :author, :status
validates_length_of :subject, :maximum => 255
@@ -184,6 +185,8 @@ class Issue < ActiveRecord::Base
@issue_before_change.status = self.status
@custom_values_before_change = {}
self.custom_values.each {|c| @custom_values_before_change.store c.custom_field_id, c.value }
# Make sure updated_on is updated when adding a note.
updated_on_will_change!
@current_journal
end

View File

@@ -25,8 +25,8 @@ class IssueStatus < ActiveRecord::Base
validates_length_of :name, :maximum => 30
validates_format_of :name, :with => /^[\w\s\'\-]*$/i
def before_save
IssueStatus.update_all "is_default=#{connection.quoted_false}" if self.is_default?
def after_save
IssueStatus.update_all("is_default=#{connection.quoted_false}", ['id <> ?', id]) if self.is_default?
end
# Returns the default status for new issues

View File

@@ -33,6 +33,7 @@ class Journal < ActiveRecord::Base
acts_as_activity_provider :type => 'issues',
:permission => :view_issues,
:author_key => :user_id,
:find_options => {:include => [{:issue => :project}, :details, :user],
:conditions => "#{Journal.table_name}.journalized_type = 'Issue' AND" +
" (#{JournalDetail.table_name}.prop_key = 'status_id' OR #{Journal.table_name}.notes <> '')"}

View File

@@ -31,13 +31,15 @@ class MailHandler < ActionMailer::Base
@@handler_options[:allow_override] ||= []
# Project needs to be overridable if not specified
@@handler_options[:allow_override] << 'project' unless @@handler_options[:issue].has_key?(:project)
# Status overridable by default
@@handler_options[:allow_override] << 'status' unless @@handler_options[:issue].has_key?(:status)
super email
end
# Processes incoming emails
def receive(email)
@email = email
@user = User.find_active(:first, :conditions => {:mail => email.from.first})
@user = User.active.find(:first, :conditions => ["LOWER(mail) = ?", email.from.first.to_s.strip.downcase])
unless @user
# Unknown user => the email is ignored
# TODO: ability to create the user's account
@@ -76,16 +78,25 @@ class MailHandler < ActionMailer::Base
tracker = (get_keyword(:tracker) && project.trackers.find_by_name(get_keyword(:tracker))) || project.trackers.find(:first)
category = (get_keyword(:category) && project.issue_categories.find_by_name(get_keyword(:category)))
priority = (get_keyword(:priority) && Enumeration.find_by_opt_and_name('IPRI', get_keyword(:priority)))
status = (get_keyword(:status) && IssueStatus.find_by_name(get_keyword(:status)))
# check permission
raise UnauthorizedAction unless user.allowed_to?(:add_issues, project)
issue = Issue.new(:author => user, :project => project, :tracker => tracker, :category => category, :priority => priority)
issue.subject = email.subject.chomp
# check workflow
if status && issue.new_statuses_allowed_to(user).include?(status)
issue.status = status
end
issue.subject = email.subject.chomp.toutf8
issue.description = email.plain_text_body.chomp
issue.save!
add_attachments(issue)
logger.info "MailHandler: issue ##{issue.id} created by #{user}" if logger && logger.info
# send notification before adding watchers since they were cc'ed
Mailer.deliver_issue_add(issue) if Setting.notified_events.include?('issue_added')
# add To and Cc as watchers
add_watchers(issue)
issue
end
@@ -100,13 +111,21 @@ class MailHandler < ActionMailer::Base
# Adds a note to an existing issue
def receive_issue_update(issue_id)
status = (get_keyword(:status) && IssueStatus.find_by_name(get_keyword(:status)))
issue = Issue.find_by_id(issue_id)
return unless issue
# check permission
raise UnauthorizedAction unless user.allowed_to?(:add_issue_notes, issue.project) || user.allowed_to?(:edit_issues, issue.project)
raise UnauthorizedAction unless status.nil? || user.allowed_to?(:edit_issues, issue.project)
# add the note
journal = issue.init_journal(user, email.plain_text_body.chomp)
add_attachments(issue)
# check workflow
if status && issue.new_statuses_allowed_to(user).include?(status)
issue.status = status
end
issue.save!
logger.info "MailHandler: issue ##{issue.id} updated by #{user}" if logger && logger.info
Mailer.deliver_issue_edit(journal) if Setting.notified_events.include?('issue_updated')
@@ -124,6 +143,18 @@ class MailHandler < ActionMailer::Base
end
end
# Adds To and Cc as watchers of the given object if the sender has the
# appropriate permission
def add_watchers(obj)
if user.allowed_to?("add_#{obj.class.name.underscore}_watchers".to_sym, obj.project)
addresses = [email.to, email.cc].flatten.compact.uniq.collect {|a| a.strip.downcase}
unless addresses.empty?
watchers = User.active.find(:all, :conditions => ['LOWER(mail) IN (?)', addresses])
watchers.each {|w| obj.add_watcher(w)}
end
end
end
def get_keyword(attr)
if @@handler_options[:allow_override].include?(attr.to_s) && email.plain_text_body =~ /^#{attr}:[ \t]*(.+)$/i
$1.strip

View File

@@ -5,12 +5,12 @@
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
@@ -19,15 +19,15 @@ class Mailer < ActionMailer::Base
helper :application
helper :issues
helper :custom_fields
include ActionController::UrlWriter
def issue_add(issue)
def issue_add(issue)
redmine_headers 'Project' => issue.project.identifier,
'Issue-Id' => issue.id,
'Issue-Author' => issue.author.login
redmine_headers 'Issue-Assignee' => issue.assigned_to.login if issue.assigned_to
recipients issue.recipients
recipients issue.recipients
subject "[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}] (#{issue.status.name}) #{issue.subject}"
body :issue => issue,
:issue_url => url_for(:controller => 'issues', :action => 'show', :id => issue)
@@ -50,7 +50,7 @@ class Mailer < ActionMailer::Base
:journal => journal,
:issue_url => url_for(:controller => 'issues', :action => 'show', :id => issue)
end
def reminder(user, issues, days)
set_language_if_valid user.language
recipients user.mail
@@ -59,7 +59,7 @@ class Mailer < ActionMailer::Base
:days => days,
:issues_url => url_for(:controller => 'issues', :action => 'index', :set_filter => 1, :assigned_to_id => user.id, :sort_key => 'issues.due_date', :sort_order => 'asc')
end
def document_added(document)
redmine_headers 'Project' => document.project.identifier
recipients document.project.recipients
@@ -67,7 +67,7 @@ class Mailer < ActionMailer::Base
body :document => document,
:document_url => url_for(:controller => 'documents', :action => 'show', :id => document)
end
def attachments_added(attachments)
container = attachments.first.container
added_to = ''
@@ -104,7 +104,7 @@ class Mailer < ActionMailer::Base
body :message => message,
:message_url => url_for(:controller => 'messages', :action => 'show', :board_id => message.board_id, :id => message.root)
end
def account_information(user, password)
set_language_if_valid user.language
recipients user.mail
@@ -113,10 +113,10 @@ class Mailer < ActionMailer::Base
:password => password,
:login_url => url_for(:controller => 'account', :action => 'login')
end
def account_activation_request(user)
# Send the email to all active administrators
recipients User.find_active(:all, :conditions => {:admin => true}).collect { |u| u.mail }.compact
recipients User.active.find(:all, :conditions => {:admin => true}).collect { |u| u.mail }.compact
subject l(:mail_subject_account_activation_request, Setting.app_title)
body :user => user,
:url => url_for(:controller => 'users', :action => 'index', :status => User::STATUS_REGISTERED, :sort_key => 'created_on', :sort_order => 'desc')
@@ -128,7 +128,7 @@ class Mailer < ActionMailer::Base
subject l(:mail_subject_lost_password, Setting.app_title)
body :token => token,
:url => url_for(:controller => 'account', :action => 'lost_password', :token => token.value)
end
end
def register(token)
set_language_if_valid(token.user.language)
@@ -137,7 +137,7 @@ class Mailer < ActionMailer::Base
body :token => token,
:url => url_for(:controller => 'account', :action => 'activate', :token => token.value)
end
def test(user)
set_language_if_valid(user.language)
recipients user.mail
@@ -148,12 +148,12 @@ class Mailer < ActionMailer::Base
# Overrides default deliver! method to prevent from sending an email
# with no recipient, cc or bcc
def deliver!(mail = @mail)
return false if (recipients.nil? || recipients.empty?) &&
return false if (recipients.nil? || recipients.empty?) &&
(cc.nil? || cc.empty?) &&
(bcc.nil? || bcc.empty?)
super
end
# Sends reminders to issue assignees
# Available options:
# * :days => how many days in the future to remind about (defaults to 7)
@@ -163,13 +163,13 @@ class Mailer < ActionMailer::Base
days = options[:days] || 7
project = options[:project] ? Project.find(options[:project]) : nil
tracker = options[:tracker] ? Tracker.find(options[:tracker]) : nil
s = ARCondition.new ["#{IssueStatus.table_name}.is_closed = ? AND #{Issue.table_name}.due_date <= ?", false, days.day.from_now.to_date]
s << "#{Issue.table_name}.assigned_to_id IS NOT NULL"
s << "#{Project.table_name}.status = #{Project::STATUS_ACTIVE}"
s << "#{Issue.table_name}.project_id = #{project.id}" if project
s << "#{Issue.table_name}.tracker_id = #{tracker.id}" if tracker
issues_by_assignee = Issue.find(:all, :include => [:status, :assigned_to, :project, :tracker],
:conditions => s.conditions
).group_by(&:assigned_to)
@@ -183,19 +183,24 @@ class Mailer < ActionMailer::Base
super
set_language_if_valid Setting.default_language
from Setting.mail_from
default_url_options[:host] = Setting.host_name
# URL options
h = Setting.host_name
h = h.to_s.gsub(%r{\/.*$}, '') unless ActionController::AbstractRequest.relative_url_root.blank?
default_url_options[:host] = h
default_url_options[:protocol] = Setting.protocol
# Common headers
headers 'X-Mailer' => 'Redmine',
'X-Redmine-Host' => Setting.host_name,
'X-Redmine-Site' => Setting.app_title
end
# Appends a Redmine header field (name is prepended with 'X-Redmine-')
def redmine_headers(h)
h.each { |k,v| headers["X-Redmine-#{k}"] = v }
end
# Overrides the create_mail method
def create_mail
# Removes the current user from the recipients and cc
@@ -209,17 +214,32 @@ class Mailer < ActionMailer::Base
bcc([recipients, cc].flatten.compact.uniq)
recipients []
cc []
end
end
super
end
# Renders a message with the corresponding layout
def render_message(method_name, body)
layout = method_name.match(%r{text\.html\.(rhtml|rxml)}) ? 'layout.text.html.rhtml' : 'layout.text.plain.rhtml'
body[:content_for_layout] = render(:file => method_name, :body => body)
ActionView::Base.new(template_root, body, self).render(:file => "mailer/#{layout}")
ActionView::Base.new(template_root, body, self).render(:file => "mailer/#{layout}", :use_full_path => true)
end
# for the case of plain text only
def body(*params)
value = super(*params)
if Setting.plain_text_mail?
templates = Dir.glob("#{template_path}/#{@template}.text.plain.{rhtml,erb}")
unless String === @body or templates.empty?
template = File.basename(templates.first)
@body[:content_for_layout] = render(:file => template, :body => @body)
@body = ActionView::Base.new(template_root, @body, self).render(:file => "mailer/layout.text.plain.rhtml", :use_full_path => true)
return @body
end
end
return value
end
# Makes partial rendering work with Rails 1.2 (retro-compatibility)
def self.controller_path
''

View File

@@ -32,12 +32,16 @@ class Message < ActiveRecord::Base
:url => Proc.new {|o| {:controller => 'messages', :action => 'show', :board_id => o.board_id}.merge(o.parent_id.nil? ? {:id => o.id} :
{:id => o.parent_id, :anchor => "message-#{o.id}"})}
acts_as_activity_provider :find_options => {:include => [{:board => :project}, :author]}
acts_as_activity_provider :find_options => {:include => [{:board => :project}, :author]},
:author_key => :author_id
acts_as_watchable
attr_protected :locked, :sticky
validates_presence_of :subject, :content
validates_length_of :subject, :maximum => 255
after_create :add_author_as_watcher
def validate_on_create
# Can not reply to a locked topic
errors.add_to_base 'Topic is locked' if root.locked? && self != root
@@ -68,4 +72,18 @@ class Message < ActiveRecord::Base
def project
board.project
end
def editable_by?(usr)
usr && usr.logged? && (usr.allowed_to?(:edit_messages, project) || (self.author == usr && usr.allowed_to?(:edit_own_messages, project)))
end
def destroyable_by?(usr)
usr && usr.logged? && (usr.allowed_to?(:delete_messages, project) || (self.author == usr && usr.allowed_to?(:delete_own_messages, project)))
end
private
def add_author_as_watcher
Watcher.create(:watchable => self.root, :user => author)
end
end

View File

@@ -17,8 +17,9 @@
class MessageObserver < ActiveRecord::Observer
def after_create(message)
# send notification to the authors of the thread
recipients = ([message.root] + message.root.children).collect {|m| m.author.mail if m.author && m.author.active?}
recipients = []
# send notification to the topic watchers
recipients += message.root.watcher_recipients
# send notification to the board watchers
recipients += message.board.watcher_recipients
# send notification to project members who want to be notified

View File

@@ -1,5 +1,5 @@
# redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang
# Redmine - project management software
# Copyright (C) 2006-2008 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -26,10 +26,11 @@ class News < ActiveRecord::Base
acts_as_searchable :columns => ['title', "#{table_name}.description"], :include => :project
acts_as_event :url => Proc.new {|o| {:controller => 'news', :action => 'show', :id => o.id}}
acts_as_activity_provider :find_options => {:include => [:project, :author]}
acts_as_activity_provider :find_options => {:include => [:project, :author]},
:author_key => :author_id
# returns latest news for projects visible by user
def self.latest(user=nil, count=5)
find(:all, :limit => count, :conditions => Project.visible_by(user), :include => [ :author, :project ], :order => "#{News.table_name}.created_on DESC")
def self.latest(user = User.current, count = 5)
find(:all, :limit => count, :conditions => Project.allowed_to_condition(user, :view_news), :include => [ :author, :project ], :order => "#{News.table_name}.created_on DESC")
end
end

View File

@@ -62,6 +62,8 @@ class Project < ActiveRecord::Base
validates_format_of :identifier, :with => /^[a-z0-9\-]*$/
before_destroy :delete_all_members
named_scope :has_module, lambda { |mod| { :conditions => ["#{Project.table_name}.id IN (SELECT em.project_id FROM #{EnabledModule.table_name} em WHERE em.name=?)", mod.to_s] } }
def identifier=(identifier)
super unless identifier_frozen?
@@ -106,6 +108,12 @@ class Project < ActiveRecord::Base
def self.allowed_to_condition(user, permission, options={})
statements = []
base_statement = "#{Project.table_name}.status=#{Project::STATUS_ACTIVE}"
if perm = Redmine::AccessControl.permission(permission)
unless perm.project_module.nil?
# If the permission belongs to a project module, make sure the module is enabled
base_statement << " AND EXISTS (SELECT em.id FROM #{EnabledModule.table_name} em WHERE em.name='#{perm.project_module}' AND em.project_id=#{Project.table_name}.id)"
end
end
if options[:project]
project_statement = "#{Project.table_name}.id = #{options[:project].id}"
project_statement << " OR #{Project.table_name}.parent_id = #{options[:project].id}" if options[:with_subprojects]
@@ -198,7 +206,7 @@ class Project < ActiveRecord::Base
# Returns an array of all custom fields enabled for project issues
# (explictly associated custom fields and custom fields enabled for all projects)
def all_issue_custom_fields
@all_issue_custom_fields ||= (IssueCustomField.for_all + issue_custom_fields).uniq
@all_issue_custom_fields ||= (IssueCustomField.for_all + issue_custom_fields).uniq.sort
end
def project
@@ -238,6 +246,12 @@ class Project < ActiveRecord::Base
enabled_modules << EnabledModule.new(:name => name.to_s)
end
end
# Returns an auto-generated project identifier based on the last identifier used
def self.next_identifier
p = Project.find(:first, :order => 'created_on DESC')
p.nil? ? nil : p.identifier.to_s.succ
end
protected
def validate

View File

@@ -1,5 +1,5 @@
# redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang
# Redmine - project management software
# Copyright (C) 2006-2008 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -254,9 +254,8 @@ class Query < ActiveRecord::Base
def has_default_columns?
column_names.nil? || column_names.empty?
end
def statement
# project/subprojects clause
def project_statement
project_clauses = []
if project && !@project.active_children.empty?
ids = [project.id]
@@ -274,12 +273,15 @@ class Query < ActiveRecord::Base
elsif Setting.display_subprojects_issues?
ids += project.child_ids
end
project_clauses << "#{Issue.table_name}.project_id IN (%s)" % ids.join(',')
project_clauses << "#{Project.table_name}.id IN (%s)" % ids.join(',')
elsif project
project_clauses << "#{Issue.table_name}.project_id = %d" % project.id
project_clauses << "#{Project.table_name}.id = %d" % project.id
end
project_clauses << Project.visible_by(User.current)
project_clauses << Project.allowed_to_condition(User.current, :view_issues)
project_clauses.join(' AND ')
end
def statement
# filters clauses
filters_clauses = []
filters.each_key do |field|
@@ -307,60 +309,69 @@ class Query < ActiveRecord::Base
v.push(User.current.logged? ? User.current.id.to_s : "0") if v.delete("me")
end
case operator_for field
when "="
sql = sql + "#{db_table}.#{db_field} IN (" + v.collect{|val| "'#{connection.quote_string(val)}'"}.join(",") + ")"
when "!"
sql = sql + "(#{db_table}.#{db_field} IS NULL OR #{db_table}.#{db_field} NOT IN (" + v.collect{|val| "'#{connection.quote_string(val)}'"}.join(",") + "))"
when "!*"
sql = sql + "#{db_table}.#{db_field} IS NULL"
sql << " OR #{db_table}.#{db_field} = ''" if is_custom_filter
when "*"
sql = sql + "#{db_table}.#{db_field} IS NOT NULL"
sql << " AND #{db_table}.#{db_field} <> ''" if is_custom_filter
when ">="
sql = sql + "#{db_table}.#{db_field} >= #{v.first.to_i}"
when "<="
sql = sql + "#{db_table}.#{db_field} <= #{v.first.to_i}"
when "o"
sql = sql + "#{IssueStatus.table_name}.is_closed=#{connection.quoted_false}" if field == "status_id"
when "c"
sql = sql + "#{IssueStatus.table_name}.is_closed=#{connection.quoted_true}" if field == "status_id"
when ">t-"
sql = sql + "#{db_table}.#{db_field} BETWEEN '%s' AND '%s'" % [connection.quoted_date((Date.today - v.first.to_i).to_time), connection.quoted_date((Date.today + 1).to_time)]
when "<t-"
sql = sql + "#{db_table}.#{db_field} <= '%s'" % connection.quoted_date((Date.today - v.first.to_i).to_time)
when "t-"
sql = sql + "#{db_table}.#{db_field} BETWEEN '%s' AND '%s'" % [connection.quoted_date((Date.today - v.first.to_i).to_time), connection.quoted_date((Date.today - v.first.to_i + 1).to_time)]
when ">t+"
sql = sql + "#{db_table}.#{db_field} >= '%s'" % connection.quoted_date((Date.today + v.first.to_i).to_time)
when "<t+"
sql = sql + "#{db_table}.#{db_field} BETWEEN '%s' AND '%s'" % [connection.quoted_date(Date.today.to_time), connection.quoted_date((Date.today + v.first.to_i + 1).to_time)]
when "t+"
sql = sql + "#{db_table}.#{db_field} BETWEEN '%s' AND '%s'" % [connection.quoted_date((Date.today + v.first.to_i).to_time), connection.quoted_date((Date.today + v.first.to_i + 1).to_time)]
when "t"
sql = sql + "#{db_table}.#{db_field} BETWEEN '%s' AND '%s'" % [connection.quoted_date(Date.today.to_time), connection.quoted_date((Date.today+1).to_time)]
when "w"
from = l(:general_first_day_of_week) == '7' ?
# week starts on sunday
((Date.today.cwday == 7) ? Time.now.at_beginning_of_day : Time.now.at_beginning_of_week - 1.day) :
# week starts on monday (Rails default)
Time.now.at_beginning_of_week
sql = sql + "#{db_table}.#{db_field} BETWEEN '%s' AND '%s'" % [connection.quoted_date(from), connection.quoted_date(from + 7.days)]
when "~"
sql = sql + "#{db_table}.#{db_field} LIKE '%#{connection.quote_string(v.first)}%'"
when "!~"
sql = sql + "#{db_table}.#{db_field} NOT LIKE '%#{connection.quote_string(v.first)}%'"
end
sql = sql + sql_for_field(field, v, db_table, db_field, is_custom_filter)
sql << ')'
filters_clauses << sql
end if filters and valid?
(project_clauses + filters_clauses).join(' AND ')
(filters_clauses << project_statement).join(' AND ')
end
private
# Helper method to generate the WHERE sql for a +field+ with a +value+
def sql_for_field(field, value, db_table, db_field, is_custom_filter)
sql = ''
case operator_for field
when "="
sql = "#{db_table}.#{db_field} IN (" + value.collect{|val| "'#{connection.quote_string(val)}'"}.join(",") + ")"
when "!"
sql = "(#{db_table}.#{db_field} IS NULL OR #{db_table}.#{db_field} NOT IN (" + value.collect{|val| "'#{connection.quote_string(val)}'"}.join(",") + "))"
when "!*"
sql = "#{db_table}.#{db_field} IS NULL"
sql << " OR #{db_table}.#{db_field} = ''" if is_custom_filter
when "*"
sql = "#{db_table}.#{db_field} IS NOT NULL"
sql << " AND #{db_table}.#{db_field} <> ''" if is_custom_filter
when ">="
sql = "#{db_table}.#{db_field} >= #{value.first.to_i}"
when "<="
sql = "#{db_table}.#{db_field} <= #{value.first.to_i}"
when "o"
sql = "#{IssueStatus.table_name}.is_closed=#{connection.quoted_false}" if field == "status_id"
when "c"
sql = "#{IssueStatus.table_name}.is_closed=#{connection.quoted_true}" if field == "status_id"
when ">t-"
sql = date_range_clause(db_table, db_field, - value.first.to_i, 0)
when "<t-"
sql = date_range_clause(db_table, db_field, nil, - value.first.to_i)
when "t-"
sql = date_range_clause(db_table, db_field, - value.first.to_i, - value.first.to_i)
when ">t+"
sql = date_range_clause(db_table, db_field, value.first.to_i, nil)
when "<t+"
sql = date_range_clause(db_table, db_field, 0, value.first.to_i)
when "t+"
sql = date_range_clause(db_table, db_field, value.first.to_i, value.first.to_i)
when "t"
sql = date_range_clause(db_table, db_field, 0, 0)
when "w"
from = l(:general_first_day_of_week) == '7' ?
# week starts on sunday
((Date.today.cwday == 7) ? Time.now.at_beginning_of_day : Time.now.at_beginning_of_week - 1.day) :
# week starts on monday (Rails default)
Time.now.at_beginning_of_week
sql = "#{db_table}.#{db_field} BETWEEN '%s' AND '%s'" % [connection.quoted_date(from), connection.quoted_date(from + 7.days)]
when "~"
sql = "#{db_table}.#{db_field} LIKE '%#{connection.quote_string(value.first)}%'"
when "!~"
sql = "#{db_table}.#{db_field} NOT LIKE '%#{connection.quote_string(value.first)}%'"
end
return sql
end
def add_custom_fields_filters(custom_fields)
@available_filters ||= {}
@@ -380,4 +391,16 @@ class Query < ActiveRecord::Base
@available_filters["cf_#{field.id}"] = options.merge({ :name => field.name })
end
end
# Returns a SQL clause for a date or datetime field.
def date_range_clause(table, field, from, to)
s = []
if from
s << ("#{table}.#{field} > '%s'" % [connection.quoted_date((Date.yesterday + from).to_time.end_of_day)])
end
if to
s << ("#{table}.#{field} <= '%s'" % [connection.quoted_date((Date.today + to).to_time.end_of_day)])
end
s.join(' AND ')
end
end

View File

@@ -78,7 +78,7 @@ class Repository < ActiveRecord::Base
# Default behaviour: we search in cached changesets
def changesets_for_path(path)
path = "/#{path}" unless path.starts_with?('/')
Change.find(:all, :include => :changeset,
Change.find(:all, :include => {:changeset => :user},
:conditions => ["repository_id = ? AND path = ?", id, path],
:order => "committed_on DESC, #{Changeset.table_name}.id DESC").collect(&:changeset)
end
@@ -96,6 +96,45 @@ class Repository < ActiveRecord::Base
self.changesets.each(&:scan_comment_for_issue_ids)
end
# Returns an array of committers usernames and associated user_id
def committers
@committers ||= Changeset.connection.select_rows("SELECT DISTINCT committer, user_id FROM #{Changeset.table_name} WHERE repository_id = #{id}")
end
# Maps committers username to a user ids
def committer_ids=(h)
if h.is_a?(Hash)
committers.each do |committer, user_id|
new_user_id = h[committer]
if new_user_id && (new_user_id.to_i != user_id.to_i)
new_user_id = (new_user_id.to_i > 0 ? new_user_id.to_i : nil)
Changeset.update_all("user_id = #{ new_user_id.nil? ? 'NULL' : new_user_id }", ["repository_id = ? AND committer = ?", id, committer])
end
end
@committers = nil
true
else
false
end
end
# Returns the Redmine User corresponding to the given +committer+
# It will return nil if the committer is not yet mapped and if no User
# with the same username or email was found
def find_committer_user(committer)
if committer
c = changesets.find(:first, :conditions => {:committer => committer}, :include => :user)
if c && c.user
c.user
elsif committer.strip =~ /^([^<]+)(<(.*)>)?$/
username, email = $1.strip, $3
u = User.find_by_login(username)
u ||= User.find_by_mail(email) unless email.blank?
u
end
end
end
# fetch new changesets for all repositories
# can be called periodically by an external script
# eg. ruby script/runner "Repository.fetch_changesets"
@@ -134,6 +173,7 @@ class Repository < ActiveRecord::Base
def clear_changesets
connection.delete("DELETE FROM changes WHERE changes.changeset_id IN (SELECT changesets.id FROM changesets WHERE changesets.repository_id = #{id})")
connection.delete("DELETE FROM changesets_issues WHERE changesets_issues.changeset_id IN (SELECT changesets.id FROM changesets WHERE changesets.repository_id = #{id})")
connection.delete("DELETE FROM changesets WHERE changesets.repository_id = #{id}")
end
end

View File

@@ -109,7 +109,7 @@ class Repository::Cvs < Repository
cs = changesets.find(:first, :conditions=>{
:committed_on=>revision.time-time_delta..revision.time+time_delta,
:committer=>revision.author,
:comments=>revision.message
:comments=>Changeset.normalize_comments(revision.message)
})
# create a new changeset....

View File

@@ -28,6 +28,11 @@ class Repository::Darcs < Repository
'Darcs'
end
def entry(path=nil, identifier=nil)
patch = identifier.nil? ? nil : changesets.find_by_revision(identifier)
scm.entry(path, patch.nil? ? nil : patch.scmid)
end
def entries(path=nil, identifier=nil)
patch = identifier.nil? ? nil : changesets.find_by_revision(identifier)
entries = scm.entries(path, patch.nil? ? nil : patch.scmid)
@@ -46,6 +51,11 @@ class Repository::Darcs < Repository
entries
end
def cat(path, identifier=nil)
patch = identifier.nil? ? nil : changesets.find_by_revision(identifier.to_s)
scm.cat(path, patch.nil? ? nil : patch.scmid)
end
def diff(path, rev, rev_to)
patch_from = changesets.find_by_revision(rev)
return nil if patch_from.nil?

View File

@@ -30,7 +30,7 @@ class Repository::Git < Repository
end
def changesets_for_path(path)
Change.find(:all, :include => :changeset,
Change.find(:all, :include => {:changeset => :user},
:conditions => ["repository_id = ? AND path = ?", id, path],
:order => "committed_on DESC, #{Changeset.table_name}.revision DESC").collect(&:changeset)
end
@@ -45,20 +45,22 @@ class Repository::Git < Repository
unless changesets.find_by_scmid(scm_revision)
scm.revisions('', db_revision, nil, :reverse => true) do |revision|
transaction do
changeset = Changeset.create(:repository => self,
:revision => revision.identifier,
:scmid => revision.scmid,
:committer => revision.author,
:committed_on => revision.time,
:comments => 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])
if changesets.find_by_scmid(revision.scmid.to_s).nil?
transaction do
changeset = Changeset.create!(:repository => self,
:revision => revision.identifier,
:scmid => revision.scmid,
:committer => revision.author,
:committed_on => revision.time,
:comments => 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
end

View File

@@ -32,7 +32,7 @@ class Repository::Subversion < Repository
def changesets_for_path(path)
revisions = scm.revisions(path)
revisions ? changesets.find_all_by_revision(revisions.collect(&:identifier), :order => "committed_on DESC") : []
revisions ? changesets.find_all_by_revision(revisions.collect(&:identifier), :order => "committed_on DESC", :include => :user) : []
end
# Returns a path relative to the url of the repository

View File

@@ -19,6 +19,11 @@ class Role < ActiveRecord::Base
# Built-in roles
BUILTIN_NON_MEMBER = 1
BUILTIN_ANONYMOUS = 2
named_scope :builtin, lambda { |*args|
compare = 'not' if args.first == true
{ :conditions => "#{compare} builtin = 0" }
}
before_destroy :check_deletable
has_many :workflows, :dependent => :delete_all do
@@ -36,7 +41,7 @@ class Role < ActiveRecord::Base
has_many :members
acts_as_list
serialize :permissions
serialize :permissions, Array
attr_protected :builtin
validates_presence_of :name
@@ -49,9 +54,32 @@ class Role < ActiveRecord::Base
end
def permissions=(perms)
perms = perms.collect {|p| p.to_sym unless p.blank? }.compact if perms
perms = perms.collect {|p| p.to_sym unless p.blank? }.compact.uniq if perms
write_attribute(:permissions, perms)
end
def add_permission!(*perms)
self.permissions = [] unless permissions.is_a?(Array)
permissions_will_change!
perms.each do |p|
p = p.to_sym
permissions << p unless permissions.include?(p)
end
save!
end
def remove_permission!(*perms)
return unless permissions.is_a?(Array)
permissions_will_change!
perms.each { |p| permissions.delete(p.to_sym) }
save!
end
# Returns true if the role has the given permission
def has_permission?(perm)
!permissions.nil? && permissions.include?(perm.to_sym)
end
def <=>(role)
position <=> role.position

View File

@@ -33,12 +33,51 @@ class Setting < ActiveRecord::Base
'%H:%M',
'%I:%M %p'
]
ENCODINGS = %w(US-ASCII
windows-1250
windows-1251
windows-1252
windows-1253
windows-1254
windows-1255
windows-1256
windows-1257
windows-1258
windows-31j
ISO-2022-JP
ISO-2022-KR
ISO-8859-1
ISO-8859-2
ISO-8859-3
ISO-8859-4
ISO-8859-5
ISO-8859-6
ISO-8859-7
ISO-8859-8
ISO-8859-9
ISO-8859-13
ISO-8859-15
KOI8-R
UTF-8
UTF-16
UTF-16BE
UTF-16LE
EUC-JP
Shift_JIS
GB18030
GBK
ISCII91
EUC-KR
Big5
Big5-HKSCS
TIS-620)
cattr_accessor :available_settings
@@available_settings = YAML::load(File.open("#{RAILS_ROOT}/config/settings.yml"))
Redmine::Plugin.registered_plugins.each do |id, plugin|
Redmine::Plugin.all.each do |plugin|
next unless plugin.settings
@@available_settings["plugin_#{id}"] = {'default' => plugin.settings[:default], 'serialized' => true}
@@available_settings["plugin_#{plugin.id}"] = {'default' => plugin.settings[:default], 'serialized' => true}
end
validates_uniqueness_of :name

View File

@@ -33,13 +33,18 @@ class User < ActiveRecord::Base
:username => '#{login}'
}
has_many :memberships, :class_name => 'Member', :include => [ :project, :role ], :conditions => "#{Project.table_name}.status=#{Project::STATUS_ACTIVE}", :order => "#{Project.table_name}.name", :dependent => :delete_all
has_many :memberships, :class_name => 'Member', :include => [ :project, :role ], :conditions => "#{Project.table_name}.status=#{Project::STATUS_ACTIVE}", :order => "#{Project.table_name}.name"
has_many :members, :dependent => :delete_all
has_many :projects, :through => :memberships
has_many :issue_categories, :foreign_key => 'assigned_to_id', :dependent => :nullify
has_many :changesets, :dependent => :nullify
has_one :preference, :dependent => :destroy, :class_name => 'UserPreference'
has_one :rss_token, :dependent => :destroy, :class_name => 'Token', :conditions => "action='feeds'"
belongs_to :auth_source
# Active non-anonymous users scope
named_scope :active, :conditions => "#{User.table_name}.status = #{STATUS_ACTIVE}"
acts_as_customizable
attr_accessor :password, :password_confirmation
@@ -69,17 +74,10 @@ class User < ActiveRecord::Base
# update hashed_password if password was set
self.hashed_password = User.hash_password(self.password) if self.password
end
def self.active
with_scope :find => { :conditions => [ "status = ?", STATUS_ACTIVE ] } do
yield
end
end
def self.find_active(*args)
active do
find(*args)
end
def reload(*args)
@name = nil
super
end
# Returns the user that matches provided login and password, or nil
@@ -118,8 +116,11 @@ class User < ActiveRecord::Base
# Return user's full name for display
def name(formatter = nil)
f = USER_FORMATS[formatter || Setting.user_format] || USER_FORMATS[:firstname_lastname]
eval '"' + f + '"'
if formatter
eval('"' + (USER_FORMATS[formatter] || USER_FORMATS[:firstname_lastname]) + '"')
else
@name ||= eval('"' + (USER_FORMATS[Setting.user_format] || USER_FORMATS[:firstname_lastname]) + '"')
end
end
def active?
@@ -143,7 +144,7 @@ class User < ActiveRecord::Base
end
def time_zone
self.pref.time_zone.nil? ? nil : TimeZone[self.pref.time_zone]
@time_zone ||= (self.pref.time_zone.blank? ? nil : TimeZone[self.pref.time_zone])
end
def wants_comments_in_reverse_order?
@@ -178,14 +179,9 @@ class User < ActiveRecord::Base
token && (token.created_on > Setting.autologin.to_i.day.ago) && token.user.active? ? token.user : nil
end
# Sort users by their display names
def <=>(user)
if user.nil?
-1
elsif lastname.to_s.downcase == user.lastname.to_s.downcase
firstname.to_s.downcase <=> user.firstname.to_s.downcase
else
lastname.to_s.downcase <=> user.lastname.to_s.downcase
end
self.to_s.downcase <=> user.to_s.downcase
end
def to_s
@@ -242,7 +238,7 @@ class User < ActiveRecord::Base
elsif options[:global]
# authorize if user has at least one role that has this permission
roles = memberships.collect {|m| m.role}.uniq
roles.detect {|r| r.allowed_to?(action)}
roles.detect {|r| r.allowed_to?(action)} || (self.logged? ? Role.non_member.allowed_to?(action) : Role.anonymous.allowed_to?(action))
else
false
end

View File

@@ -24,7 +24,7 @@ class Version < ActiveRecord::Base
validates_presence_of :name
validates_uniqueness_of :name, :scope => [:project_id]
validates_length_of :name, :maximum => 60
validates_format_of :effective_date, :with => /^\d{4}-\d{2}-\d{2}$/, :message => :activerecord_error_not_a_date, :allow_nil => true
validates_format_of :effective_date, :with => /^\d{4}-\d{2}-\d{2}$/, :message => 'activerecord_error_not_a_date', :allow_nil => true
def start_date
effective_date

View File

@@ -19,5 +19,12 @@ class Watcher < ActiveRecord::Base
belongs_to :watchable, :polymorphic => true
belongs_to :user
validates_presence_of :user
validates_uniqueness_of :user_id, :scope => [:watchable_type, :watchable_id]
protected
def validate
errors.add :user_id, :activerecord_error_invalid unless user.nil? || user.active?
end
end

View File

@@ -43,6 +43,25 @@ class Wiki < ActiveRecord::Base
page
end
# Finds a page by title
# The given string can be of one of the forms: "title" or "project:title"
# Examples:
# Wiki.find_page("bar", project => foo)
# Wiki.find_page("foo:bar")
def self.find_page(title, options = {})
project = options[:project]
if title.to_s =~ %r{^([^\:]+)\:(.*)$}
project_identifier, title = $1, $2
project = Project.find_by_identifier(project_identifier) || Project.find_by_name(project_identifier)
end
if project && project.wiki
page = project.wiki.find_page(title)
if page && page.content
page
end
end
end
# turn a string into a valid page title
def self.titleize(title)
# replace spaces with _ and remove unwanted caracters

View File

@@ -35,9 +35,10 @@ class WikiContent < ActiveRecord::Base
:type => 'wiki-page',
:url => Proc.new {|o| {:controller => 'wiki', :id => o.page.wiki.project_id, :page => o.page.title, :version => o.version}}
acts_as_activity_provider :type => 'wiki_pages',
acts_as_activity_provider :type => 'wiki_edits',
:timestamp => "#{WikiContent.versioned_table_name}.updated_on",
:permission => :view_wiki_pages,
:author_key => "#{WikiContent.versioned_table_name}.author_id",
:permission => :view_wiki_edits,
:find_options => {:select => "#{WikiContent.versioned_table_name}.updated_on, #{WikiContent.versioned_table_name}.comments, " +
"#{WikiContent.versioned_table_name}.#{WikiContent.version_column}, #{WikiPage.table_name}.title, " +
"#{WikiContent.versioned_table_name}.page_id, #{WikiContent.versioned_table_name}.author_id, " +

View File

@@ -21,4 +21,23 @@ class Workflow < ActiveRecord::Base
belongs_to :new_status, :class_name => 'IssueStatus', :foreign_key => 'new_status_id'
validates_presence_of :role, :old_status, :new_status
# Returns workflow transitions count by tracker and role
def self.count_by_tracker_and_role
counts = connection.select_all("SELECT role_id, tracker_id, count(id) AS c FROM #{Workflow.table_name} GROUP BY role_id, tracker_id")
roles = Role.find(:all, :order => 'builtin, position')
trackers = Tracker.find(:all, :order => 'position')
result = []
trackers.each do |tracker|
t = []
roles.each do |role|
row = counts.detect {|c| c['role_id'] == role.id.to_s && c['tracker_id'] == tracker.id.to_s}
t << [role, (row.nil? ? 0 : row['c'].to_i)]
end
result << [tracker, t]
end
result
end
end

View File

@@ -1,16 +1,24 @@
<h2><%=h @user.name %></h2>
<div class="contextual">
<%= link_to(l(:button_edit), {:controller => 'users', :action => 'edit', :id => @user}, :class => 'icon icon-edit') if User.current.admin? %>
</div>
<p>
<%= mail_to(h(@user.mail)) unless @user.pref.hide_mail %>
<h2><%= avatar @user %> <%=h @user.name %></h2>
<div class="splitcontentleft">
<ul>
<li><%=l(:label_registered_on)%>: <%= format_date(@user.created_on) %></li>
<% for custom_value in @custom_values %>
<% if !custom_value.value.empty? %>
<% unless @user.pref.hide_mail %>
<li><%=l(:field_mail)%>: <%= mail_to(h(@user.mail), nil, :encode => 'javascript') %></li>
<% end %>
<% for custom_value in @custom_values %>
<% if !custom_value.value.empty? %>
<li><%= custom_value.custom_field.name%>: <%=h show_value(custom_value) %></li>
<% end %>
<% end %>
<% end %>
<% end %>
<li><%=l(:label_registered_on)%>: <%= format_date(@user.created_on) %></li>
<% unless @user.last_login_on.nil? %>
<li><%=l(:field_last_login_on)%>: <%= format_date(@user.last_login_on) %></li>
<% end %>
</ul>
</p>
<% unless @memberships.empty? %>
<h3><%=l(:label_project_plural)%></h3>
@@ -21,8 +29,41 @@
<% end %>
</ul>
<% end %>
</div>
<div class="splitcontentright">
<% unless @events_by_day.empty? %>
<h3><%= link_to l(:label_activity), :controller => 'projects', :action => 'activity', :user_id => @user, :from => @events_by_day.keys.first %></h3>
<h3><%=l(:label_activity)%></h3>
<p>
<%=l(:label_reported_issues)%>: <%= Issue.count(:conditions => ["author_id=?", @user.id]) %>
</p>
</p>
<div id="activity">
<% @events_by_day.keys.sort.reverse.each do |day| %>
<h4><%= format_activity_day(day) %></h4>
<dl>
<% @events_by_day[day].sort {|x,y| y.event_datetime <=> x.event_datetime }.each do |e| -%>
<dt class="<%= e.event_type %>">
<span class="time"><%= format_time(e.event_datetime, false) %></span>
<%= content_tag('span', h(e.project), :class => 'project') %>
<%= link_to format_activity_title(e.event_title), e.event_url %></dt>
<dd><span class="description"><%= format_activity_description(e.event_description) %></span></dd>
<% end -%>
</dl>
<% end -%>
</div>
<p class="other-formats">
<%= l(:label_export_to) %>
<%= link_to 'Atom', {:controller => 'projects', :action => 'activity', :user_id => @user, :format => :atom, :key => User.current.rss_key}, :class => 'feed' %>
</p>
<% content_for :header_tags do %>
<%= auto_discovery_link_tag(:atom, :controller => 'projects', :action => 'activity', :user_id => @user, :format => :atom, :key => User.current.rss_key) %>
<% end %>
<% end %>
</div>
<% html_title @user.name %>

View File

@@ -19,7 +19,7 @@
<p class="icon22 icon22-tracker">
<%= link_to l(:label_tracker_plural), :controller => 'trackers' %> |
<%= link_to l(:label_issue_status_plural), :controller => 'issue_statuses' %> |
<%= link_to l(:label_workflow), :controller => 'roles', :action => 'workflow' %>
<%= link_to l(:label_workflow), :controller => 'workflows', :action => 'edit' %>
</p>
<p class="icon22 icon22-workflow">
@@ -34,6 +34,16 @@
<%= link_to l(:label_settings), :controller => 'settings' %>
</p>
<% menu_items_for(:admin_menu) do |item, caption, url, selected| -%>
<%= content_tag 'p',
link_to(h(caption), item.url, item.html_options),
:class => ["icon22", "icon22-#{item.name}"].join(' ') %>
<% end -%>
<p class="icon22 icon22-plugin">
<%= link_to l(:label_plugins), :controller => 'admin', :action => 'plugins' %>
</p>
<p class="icon22 icon22-info">
<%= link_to l(:label_information_plural), :controller => 'admin', :action => 'info' %>
</p>

View File

@@ -8,20 +8,4 @@
<tr class="odd"><td><%= l(:text_rmagick_available) %></td><td><%= image_tag (@flags[:rmagick_available] ? 'true.png' : 'false.png'), :style => "vertical-align:bottom;" %></td></tr>
</table>
<% if @plugins.any? %>
&nbsp;
<h3 class="icon22 icon22-plugin"><%= l(:label_plugins) %></h3>
<table class="list">
<% @plugins.keys.sort {|x,y| x.to_s <=> y.to_s}.each do |plugin| %>
<tr class="<%= cycle('odd', 'even') %>">
<td><%=h @plugins[plugin].name %></td>
<td><%=h @plugins[plugin].description %></td>
<td><%=h @plugins[plugin].author %></td>
<td><%=h @plugins[plugin].version %></td>
<td><%= link_to(l(:button_configure), :controller => 'settings', :action => 'plugin', :id => plugin.to_s) if @plugins[plugin].configurable? %></td>
</tr>
<% end %>
</table>
<% end %>
<% html_title(l(:label_information_plural)) -%>

View File

@@ -0,0 +1,19 @@
<h2><%= l(:label_plugins) %></h2>
<% if @plugins.any? %>
<table class="list plugins">
<% @plugins.each do |plugin| %>
<tr class="<%= cycle('odd', 'even') %>">
<td><span class="name"><%=h plugin.name %></span>
<%= content_tag('span', h(plugin.description), :class => 'description') unless plugin.description.blank? %>
<%= content_tag('span', link_to(h(plugin.url), plugin.url), :class => 'url') unless plugin.url.blank? %>
</td>
<td class="author"><%= plugin.author_url.blank? ? h(plugin.author) : link_to(h(plugin.author), plugin.author_url) %></td>
<td class="version"><%=h plugin.version %></td>
<td class="configure"><%= link_to(l(:button_configure), :controller => 'settings', :action => 'plugin', :id => plugin.id) if plugin.configurable? %></td>
</tr>
<% end %>
</table>
<% else %>
<p class="nodata"><%= l(:label_no_data) %></p>
<% end %>

View File

@@ -4,11 +4,13 @@
<h2><%=l(:label_project_plural)%></h2>
<% form_tag() do %>
<% form_tag({}, :method => :get) do %>
<fieldset><legend><%= l(:label_filter_plural) %></legend>
<label><%= l(:field_status) %> :</label>
<%= select_tag 'status', project_status_options_for_select(@status), :class => "small", :onchange => "this.form.submit(); return false;" %>
<%= submit_tag l(:button_apply), :class => "small" %>
<label><%= l(:label_project) %>:</label>
<%= text_field_tag 'name', params[:name], :size => 30 %>
<%= submit_tag l(:button_apply), :class => "small", :name => nil %>
</fieldset>
<% end %>
&nbsp;

View File

@@ -9,6 +9,7 @@
<th><%=l(:field_name)%></th>
<th><%=l(:field_type)%></th>
<th><%=l(:field_host)%></th>
<th><%=l(:label_user_plural)%></th>
<th></th>
<th></th>
</tr></thead>
@@ -18,8 +19,12 @@
<td><%= link_to source.name, :action => 'edit', :id => source%></td>
<td align="center"><%= source.auth_method_name %></td>
<td align="center"><%= source.host %></td>
<td align="center"><%= source.users.count %></td>
<td align="center"><%= link_to l(:button_test), :action => 'test_connection', :id => source %></td>
<td align="center"><%= button_to l(:button_delete), { :action => 'destroy', :id => source }, :confirm => l(:text_are_you_sure), :class => "button-small" %></td>
<td align="center"><%= button_to l(:button_delete), { :action => 'destroy', :id => source },
:confirm => l(:text_are_you_sure),
:class => "button-small",
:disabled => source.users.any? %></td>
</tr>
<% end %>
</tbody>

View File

@@ -26,6 +26,7 @@
</div>
<h2><%=h @board.name %></h2>
<p class="subtitle"><%=h @board.description %></p>
<% if @topics.any? %>
<table class="list messages">
@@ -33,7 +34,7 @@
<th><%= l(:field_subject) %></th>
<th><%= l(:field_author) %></th>
<%= sort_header_tag("#{Message.table_name}.created_on", :caption => l(:field_created_on)) %>
<th><%= l(:label_reply_plural) %></th>
<%= sort_header_tag("#{Message.table_name}.replies_count", :caption => l(:label_reply_plural)) %>
<%= sort_header_tag("#{Message.table_name}.updated_on", :caption => l(:label_message_last)) %>
</tr></thead>
<tbody>

View File

@@ -6,7 +6,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom" do
xml.id url_for(:controller => 'welcome', :only_path => false)
xml.updated((@items.first ? @items.first.event_datetime : Time.now).xmlschema)
xml.author { xml.name "#{Setting.app_title}" }
xml.generator(:uri => Redmine::Info.url, :version => Redmine::VERSION) { xml.text! Redmine::Info.versioned_name; }
xml.generator(:uri => Redmine::Info.url) { xml.text! Redmine::Info.app_name; }
@items.each do |item|
xml.entry do
url = url_for(item.event_url(:only_path => false))

View File

@@ -2,7 +2,7 @@
<div class="changeset <%= cycle('odd', 'even') %>">
<p><%= link_to("#{l(:label_revision)} #{changeset.revision}",
:controller => 'repositories', :action => 'revision', :id => @project, :rev => changeset.revision) %><br />
<span class="author"><%= authoring(changeset.committed_on, changeset.committer) %></span></p>
<span class="author"><%= authoring(changeset.committed_on, changeset.author) %></span></p>
<%= textilizable(changeset, :comments) %>
</div>
<% end %>

View File

@@ -35,6 +35,7 @@
<fieldset><legend><%= l(:field_notes) %></legend>
<%= text_area_tag 'notes', @notes, :cols => 60, :rows => 10, :class => 'wiki-edit' %>
<%= wikitoolbar_for 'notes' %>
<%= call_hook(:view_issues_edit_notes_bottom, { :issue => @issue, :notes => @notes, :form => f }) %>
<p><%=l(:label_attachment_plural)%><br /><%= render :partial => 'attachments/form' %></p>
</fieldset>

View File

@@ -42,12 +42,12 @@
</div>
<div style="clear:both;"> </div>
<%= render :partial => 'form_custom_fields', :locals => {:values => @custom_values} %>
<%= render :partial => 'form_custom_fields' %>
<% if @issue.new_record? %>
<p><label><%=l(:label_attachment_plural)%></label><%= render :partial => 'attachments/form' %></p>
<% end %>
<%= Redmine::Plugin::Hook::Manager.call_hook(:issue_edit, {:project => @project, :issue => @issue, :form => f }) %>
<%= call_hook(:view_issues_form_details_bottom, { :issue => @issue, :form => f }) %>
<%= wikitoolbar_for 'issue_description' %>

View File

@@ -1,5 +1,5 @@
<div class="splitcontentleft">
<% i = 1 %>
<% i = 0 %>
<% split_on = @issue.custom_field_values.size / 2 %>
<% @issue.custom_field_values.each do |value| %>
<p><%= custom_field_tag_with_label :issue, value %></p>

View File

@@ -1,14 +1,16 @@
<% reply_links = authorize_for('issues', 'edit') -%>
<% for journal in journals %>
<div id="change-<%= journal.id %>" class="journal">
<h4><div style="float:right;"><%= link_to "##{journal.indice}", :anchor => "note-#{journal.indice}" %></div>
<%= content_tag('a', '', :name => "note-#{journal.indice}")%>
<%= format_time(journal.created_on) %> - <%= journal.user.name %></h4>
<ul>
<% for detail in journal.details %>
<li><%= show_detail(detail) %></li>
<% end %>
</ul>
<%= render_notes(journal, :reply_links => reply_links) unless journal.notes.blank? %>
</div>
<div id="change-<%= journal.id %>" class="journal">
<h4><div style="float:right;"><%= link_to "##{journal.indice}", :anchor => "note-#{journal.indice}" %></div>
<%= content_tag('a', '', :name => "note-#{journal.indice}")%>
<%= authoring journal.created_on, journal.user, :label => :label_updated_time_by %></h4>
<%= avatar(journal.user, :size => "32") %>
<ul>
<% for detail in journal.details %>
<li><%= show_detail(detail) %></li>
<% end %>
</ul>
<%= render_notes(journal, :reply_links => reply_links) unless journal.notes.blank? %>
</div>
<%= call_hook(:view_issues_history_journal_bottom, { :journal => journal }) %>
<% end %>

View File

@@ -10,7 +10,8 @@
<table style="width:100%">
<% @issue.relations.each do |relation| %>
<tr>
<td><%= l(relation.label_for(@issue)) %> <%= "(#{lwr(:actionview_datehelper_time_in_words_day, relation.delay)})" if relation.delay && relation.delay != 0 %> <%= link_to_issue relation.other_issue(@issue) %></td>
<td><%= l(relation.label_for(@issue)) %> <%= "(#{lwr(:actionview_datehelper_time_in_words_day, relation.delay)})" if relation.delay && relation.delay != 0 %>
<%= h(relation.other_issue(@issue).project) + ' - ' if Setting.cross_project_issue_relations? %> <%= link_to_issue relation.other_issue(@issue) %></td>
<td><%=h relation.other_issue(@issue).subject %></td>
<td><%= relation.other_issue(@issue).status.name %></td>
<td><%= format_date(relation.other_issue(@issue).start_date) %></td>

View File

@@ -2,7 +2,18 @@
<%= link_to l(:label_issue_view_all), { :controller => 'issues', :action => 'index', :project_id => @project, :set_filter => 1 } %><br />
<% if @project %>
<%= link_to l(:field_summary), :controller => 'reports', :action => 'issue_report', :id => @project %><br />
<%= link_to l(:label_change_log), :controller => 'projects', :action => 'changelog', :id => @project %>
<%= link_to l(:label_change_log), :controller => 'projects', :action => 'changelog', :id => @project %><br />
<% end %>
<%= call_hook(:view_issues_sidebar_issues_bottom) %>
<% planning_links = []
planning_links << link_to(l(:label_calendar), :action => 'calendar', :project_id => @project) if User.current.allowed_to?(:view_calendar, @project, :global => true)
planning_links << link_to(l(:label_gantt), :action => 'gantt', :project_id => @project) if User.current.allowed_to?(:view_gantt, @project, :global => true)
%>
<% unless planning_links.empty? %>
<h3><%= l(:label_planning) %></h3>
<p><%= planning_links.join(' | ') %></p>
<%= call_hook(:view_issues_sidebar_planning_bottom) %>
<% end %>
<% unless sidebar_queries.empty? -%>
@@ -11,4 +22,5 @@
<% sidebar_queries.each do |query| -%>
<%= link_to query.name, :controller => 'issues', :action => 'index', :project_id => @project, :query_id => query %><br />
<% end -%>
<%= call_hook(:view_issues_sidebar_queries_bottom) %>
<% end -%>

View File

@@ -38,7 +38,7 @@
<label><%= l(:field_done_ratio) %>:
<%= select_tag 'done_ratio', options_for_select([[l(:label_no_change_option), '']] + (0..10).to_a.collect {|r| ["#{r*10} %", r*10] }) %></label>
</p>
<%= Redmine::Plugin::Hook::Manager.call_hook(:issue_bulk_edit, {:project => @project, :issue => @issues }) %>
<%= call_hook(:view_issues_bulk_edit_details_bottom, { :issues => @issues }) %>
</fieldset>
<fieldset><legend><%= l(:field_notes) %></legend>

View File

@@ -0,0 +1,55 @@
<% form_tag({}, :id => 'query_form') do %>
<% if @query.new_record? %>
<h2><%= l(:label_calendar) %></h2>
<fieldset id="filters"><legend><%= l(:label_filter_plural) %></legend>
<%= render :partial => 'queries/filters', :locals => {:query => @query} %>
</fieldset>
<% else %>
<h2><%=h @query.name %></h2>
<% html_title @query.name %>
<% end %>
<fieldset id="date-range"><legend><%= l(:label_date_range) %></legend>
<%= select_month(@month, :prefix => "month", :discard_type => true) %>
<%= select_year(@year, :prefix => "year", :discard_type => true) %>
</fieldset>
<p style="float:right; margin:0px;">
<%= link_to_remote ('&#171; ' + (@month==1 ? "#{month_name(12)} #{@year-1}" : "#{month_name(@month-1)}")),
{:update => "content", :url => { :year => (@month==1 ? @year-1 : @year), :month =>(@month==1 ? 12 : @month-1) }},
{:href => url_for(:action => 'calendar', :year => (@month==1 ? @year-1 : @year), :month =>(@month==1 ? 12 : @month-1))}
%> |
<%= link_to_remote ((@month==12 ? "#{month_name(1)} #{@year+1}" : "#{month_name(@month+1)}") + ' &#187;'),
{:update => "content", :url => { :year => (@month==12 ? @year+1 : @year), :month =>(@month==12 ? 1 : @month+1) }},
{:href => url_for(:action => 'calendar', :year => (@month==12 ? @year+1 : @year), :month =>(@month==12 ? 1 : @month+1))}
%>
</p>
<p class="buttons">
<%= link_to_remote l(:button_apply),
{ :url => { :set_filter => (@query.new_record? ? 1 : nil) },
:update => "content",
:with => "Form.serialize('query_form')"
}, :class => 'icon icon-checked' %>
<%= link_to_remote l(:button_clear),
{ :url => { :set_filter => (@query.new_record? ? 1 : nil) },
:update => "content",
}, :class => 'icon icon-reload' if @query.new_record? %>
</p>
<% end %>
<%= error_messages_for 'query' %>
<% if @query.valid? %>
<%= render :partial => 'common/calendar', :locals => {:calendar => @calendar} %>
<%= image_tag 'arrow_from.png' %>&nbsp;&nbsp;<%= l(:text_tip_task_begin_day) %><br />
<%= image_tag 'arrow_to.png' %>&nbsp;&nbsp;<%= l(:text_tip_task_end_day) %><br />
<%= image_tag 'arrow_bw.png' %>&nbsp;&nbsp;<%= l(:text_tip_task_begin_end_day) %><br />
<% end %>
<% content_for :sidebar do %>
<%= render :partial => 'issues/sidebar' %>
<% end %>
<% html_title(l(:label_calendar)) -%>

View File

@@ -11,49 +11,56 @@
<% end -%>
</ul>
</li>
<% else %>
<li><%= context_menu_link l(:button_edit), {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id)},
:class => 'icon-edit', :disabled => !@can[:edit] %></li>
<% end %>
<li class="folder">
<a href="#" class="submenu"><%= l(:field_priority) %></a>
<ul>
<% @priorities.each do |p| -%>
<li><%= context_menu_link p.name, {:controller => 'issues', :action => 'edit', :id => @issue, 'issue[priority_id]' => p, :back_to => @back}, :method => :post,
:selected => (p == @issue.priority), :disabled => !@can[:edit] %></li>
<li><%= context_menu_link p.name, {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), 'priority_id' => p, :back_to => @back}, :method => :post,
:selected => (@issue && p == @issue.priority), :disabled => !@can[:edit] %></li>
<% end -%>
</ul>
</li>
<% unless @project.versions.empty? -%>
<% unless @project.nil? || @project.versions.empty? -%>
<li class="folder">
<a href="#" class="submenu"><%= l(:field_fixed_version) %></a>
<ul>
<% @project.versions.sort.each do |v| -%>
<li><%= context_menu_link v.name, {:controller => 'issues', :action => 'edit', :id => @issue, 'issue[fixed_version_id]' => v, :back_to => @back}, :method => :post,
:selected => (v == @issue.fixed_version), :disabled => !@can[:update] %></li>
<li><%= context_menu_link v.name, {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), 'fixed_version_id' => v, :back_to => @back}, :method => :post,
:selected => (@issue && v == @issue.fixed_version), :disabled => !@can[:update] %></li>
<% end -%>
<li><%= context_menu_link l(:label_none), {:controller => 'issues', :action => 'edit', :id => @issue, 'issue[fixed_version_id]' => '', :back_to => @back}, :method => :post,
:selected => @issue.fixed_version.nil?, :disabled => !@can[:update] %></li>
<li><%= context_menu_link l(:label_none), {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), 'fixed_version_id' => 'none', :back_to => @back}, :method => :post,
:selected => (@issue && @issue.fixed_version.nil?), :disabled => !@can[:update] %></li>
</ul>
</li>
<% end %>
<% unless @assignables.nil? || @assignables.empty? -%>
<li class="folder">
<a href="#" class="submenu"><%= l(:field_assigned_to) %></a>
<ul>
<% @assignables.each do |u| -%>
<li><%= context_menu_link u.name, {:controller => 'issues', :action => 'edit', :id => @issue, 'issue[assigned_to_id]' => u, :back_to => @back}, :method => :post,
:selected => (u == @issue.assigned_to), :disabled => !@can[:update] %></li>
<li><%= context_menu_link u.name, {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), 'assigned_to_id' => u, :back_to => @back}, :method => :post,
:selected => (@issue && u == @issue.assigned_to), :disabled => !@can[:update] %></li>
<% end -%>
<li><%= context_menu_link l(:label_nobody), {:controller => 'issues', :action => 'edit', :id => @issue, 'issue[assigned_to_id]' => '', :back_to => @back}, :method => :post,
:selected => @issue.assigned_to.nil?, :disabled => !@can[:update] %></li>
<li><%= context_menu_link l(:label_nobody), {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), 'assigned_to_id' => 'none', :back_to => @back}, :method => :post,
:selected => (@issue && @issue.assigned_to.nil?), :disabled => !@can[:update] %></li>
</ul>
</li>
<% unless @project.issue_categories.empty? -%>
<% end %>
<% unless @project.nil? || @project.issue_categories.empty? -%>
<li class="folder">
<a href="#" class="submenu"><%= l(:field_category) %></a>
<ul>
<% @project.issue_categories.each do |u| -%>
<li><%= context_menu_link u.name, {:controller => 'issues', :action => 'edit', :id => @issue, 'issue[category_id]' => u, :back_to => @back}, :method => :post,
:selected => (u == @issue.category), :disabled => !@can[:update] %></li>
<li><%= context_menu_link u.name, {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), 'category_id' => u, :back_to => @back}, :method => :post,
:selected => (@issue && u == @issue.category), :disabled => !@can[:update] %></li>
<% end -%>
<li><%= context_menu_link l(:label_none), {:controller => 'issues', :action => 'edit', :id => @issue, 'issue[category_id]' => '', :back_to => @back}, :method => :post,
:selected => @issue.category.nil?, :disabled => !@can[:update] %></li>
<li><%= context_menu_link l(:label_none), {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), 'category_id' => 'none', :back_to => @back}, :method => :post,
:selected => (@issue && @issue.category.nil?), :disabled => !@can[:update] %></li>
</ul>
</li>
<% end -%>
@@ -61,22 +68,21 @@
<a href="#" class="submenu"><%= l(:field_done_ratio) %></a>
<ul>
<% (0..10).map{|x|x*10}.each do |p| -%>
<li><%= context_menu_link "#{p}%", {:controller => 'issues', :action => 'edit', :id => @issue, 'issue[done_ratio]' => p, :back_to => @back}, :method => :post,
:selected => (p == @issue.done_ratio), :disabled => !@can[:edit] %></li>
<li><%= context_menu_link "#{p}%", {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), 'done_ratio' => p, :back_to => @back}, :method => :post,
:selected => (@issue && p == @issue.done_ratio), :disabled => !@can[:edit] %></li>
<% end -%>
</ul>
</li>
<% if !@issue.nil? %>
<li><%= context_menu_link l(:button_copy), {:controller => 'issues', :action => 'new', :project_id => @project, :copy_from => @issue},
:class => 'icon-copy', :disabled => !@can[:copy] %></li>
<% if @can[:log_time] -%>
<li><%= context_menu_link l(:button_log_time), {:controller => 'timelog', :action => 'edit', :issue_id => @issue},
:class => 'icon-time' %></li>
<% end %>
<li><%= context_menu_link l(:button_copy), {:controller => 'issues', :action => 'new', :project_id => @project, :copy_from => @issue},
:class => 'icon-copy', :disabled => !@can[:copy] %></li>
<% else -%>
<li><%= context_menu_link l(:button_edit), {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id)},
:class => 'icon-edit', :disabled => !@can[:edit] %></li>
<% end -%>
<% end %>
<li><%= context_menu_link l(:button_move), {:controller => 'issues', :action => 'move', :ids => @issues.collect(&:id)},
:class => 'icon-move', :disabled => !@can[:move] %></li>
<li><%= context_menu_link l(:button_delete), {:controller => 'issues', :action => 'destroy', :ids => @issues.collect(&:id)},

View File

@@ -1,12 +1,12 @@
<%
pdf=IfpdfHelper::IFPDF.new(current_language)
pdf.SetTitle("#{@project.name} - #{l(:label_gantt)}")
pdf.SetTitle("#{l(:label_gantt)} #{@project}")
pdf.AliasNbPages
pdf.footer_date = format_date(Date.today)
pdf.AddPage("L")
pdf.SetFontStyle('B',12)
pdf.SetX(15)
pdf.Cell(70, 20, @project.name)
pdf.Cell(70, 20, @project.to_s)
pdf.Ln
pdf.SetFontStyle('B',9)
@@ -17,17 +17,17 @@ headers_heigth = header_heigth
show_weeks = false
show_days = false
if @months < 7
if @gantt.months < 7
show_weeks = true
headers_heigth = 2*header_heigth
if @months < 3
if @gantt.months < 3
show_days = true
headers_heigth = 3*header_heigth
end
end
g_width = 210
zoom = (g_width) / (@date_to - @date_from + 1)
zoom = (g_width) / (@gantt.date_to - @gantt.date_from + 1)
g_height = 120
t_height = g_height + headers_heigth
@@ -37,10 +37,10 @@ y_start = pdf.GetY
#
# Months headers
#
month_f = @date_from
month_f = @gantt.date_from
left = subject_width
height = header_heigth
@months.times do
@gantt.months.times do
width = ((month_f >> 1) - month_f) * zoom
pdf.SetY(y_start)
pdf.SetX(left)
@@ -55,20 +55,20 @@ end
if show_weeks
left = subject_width
height = header_heigth
if @date_from.cwday == 1
# @date_from is monday
week_f = @date_from
if @gantt.date_from.cwday == 1
# @gantt.date_from is monday
week_f = @gantt.date_from
else
# find next monday after @date_from
week_f = @date_from + (7 - @date_from.cwday + 1)
width = (7 - @date_from.cwday + 1) * zoom-1
# find next monday after @gantt.date_from
week_f = @gantt.date_from + (7 - @gantt.date_from.cwday + 1)
width = (7 - @gantt.date_from.cwday + 1) * zoom-1
pdf.SetY(y_start + header_heigth)
pdf.SetX(left)
pdf.Cell(width + 1, height, "", "LTR")
left = left + width+1
end
while week_f <= @date_to
width = (week_f + 6 <= @date_to) ? 7 * zoom : (@date_to - week_f + 1) * zoom
while week_f <= @gantt.date_to
width = (week_f + 6 <= @gantt.date_to) ? 7 * zoom : (@gantt.date_to - week_f + 1) * zoom
pdf.SetY(y_start + header_heigth)
pdf.SetX(left)
pdf.Cell(width, height, (width >= 5 ? week_f.cweek.to_s : ""), "LTR", 0, "C")
@@ -83,9 +83,9 @@ end
if show_days
left = subject_width
height = header_heigth
wday = @date_from.cwday
wday = @gantt.date_from.cwday
pdf.SetFontStyle('B',7)
(@date_to - @date_from + 1).to_i.times do
(@gantt.date_to - @gantt.date_from + 1).to_i.times do
width = zoom
pdf.SetY(y_start + 2 * header_heigth)
pdf.SetX(left)
@@ -106,7 +106,7 @@ pdf.Cell(subject_width+g_width-15, headers_heigth, "", 1)
#
top = headers_heigth + y_start
pdf.SetFontStyle('B',7)
@events.each do |i|
@gantt.events.each do |i|
pdf.SetY(top)
pdf.SetX(15)
@@ -123,16 +123,16 @@ pdf.SetFontStyle('B',7)
pdf.SetY(top+1.5)
if i.is_a? Issue
i_start_date = (i.start_date >= @date_from ? i.start_date : @date_from )
i_end_date = (i.due_before <= @date_to ? i.due_before : @date_to )
i_start_date = (i.start_date >= @gantt.date_from ? i.start_date : @gantt.date_from )
i_end_date = (i.due_before <= @gantt.date_to ? i.due_before : @gantt.date_to )
i_done_date = i.start_date + ((i.due_before - i.start_date+1)*i.done_ratio/100).floor
i_done_date = (i_done_date <= @date_from ? @date_from : i_done_date )
i_done_date = (i_done_date >= @date_to ? @date_to : i_done_date )
i_done_date = (i_done_date <= @gantt.date_from ? @gantt.date_from : i_done_date )
i_done_date = (i_done_date >= @gantt.date_to ? @gantt.date_to : i_done_date )
i_late_date = [i_end_date, Date.today].min if i_start_date < Date.today
i_left = ((i_start_date - @date_from)*zoom)
i_left = ((i_start_date - @gantt.date_from)*zoom)
i_width = ((i_end_date - i_start_date + 1)*zoom)
d_width = ((i_done_date - i_start_date)*zoom)
l_width = ((i_late_date - i_start_date+1)*zoom) if i_late_date
@@ -159,7 +159,7 @@ pdf.SetFontStyle('B',7)
pdf.SetX(subject_width + i_left + i_width)
pdf.Cell(30, 2, "#{i.status.name} #{i.done_ratio}%")
else
i_left = ((i.start_date - @date_from)*zoom)
i_left = ((i.start_date - @gantt.date_from)*zoom)
pdf.SetX(subject_width + i_left)
pdf.SetFillColor(50,200,50)

View File

@@ -1,5 +1,53 @@
<% form_tag(params.merge(:month => nil, :year => nil, :months => nil), :id => 'query_form') do %>
<% if @query.new_record? %>
<h2><%=l(:label_gantt)%></h2>
<fieldset id="filters"><legend><%= l(:label_filter_plural) %></legend>
<%= render :partial => 'queries/filters', :locals => {:query => @query} %>
</fieldset>
<% else %>
<h2><%=h @query.name %></h2>
<% html_title @query.name %>
<% end %>
<fieldset id="date-range"><legend><%= l(:label_date_range) %></legend>
<%= text_field_tag 'months', @gantt.months, :size => 2 %>
<%= l(:label_months_from) %>
<%= select_month(@gantt.month_from, :prefix => "month", :discard_type => true) %>
<%= select_year(@gantt.year_from, :prefix => "year", :discard_type => true) %>
<%= hidden_field_tag 'zoom', @gantt.zoom %>
</fieldset>
<p style="float:right; margin:0px;">
<%= if @gantt.zoom < 4
link_to_remote image_tag('zoom_in.png'), {:url => @gantt.params.merge(:zoom => (@gantt.zoom+1)), :update => 'content'}, {:href => url_for(@gantt.params.merge(:zoom => (@gantt.zoom+1)))}
else
image_tag 'zoom_in_g.png'
end %>
<%= if @gantt.zoom > 1
link_to_remote image_tag('zoom_out.png'), {:url => @gantt.params.merge(:zoom => (@gantt.zoom-1)), :update => 'content'}, {:href => url_for(@gantt.params.merge(:zoom => (@gantt.zoom-1)))}
else
image_tag 'zoom_out_g.png'
end %>
</p>
<p class="buttons">
<%= link_to_remote l(:button_apply),
{ :url => { :set_filter => (@query.new_record? ? 1 : nil) },
:update => "content",
:with => "Form.serialize('query_form')"
}, :class => 'icon icon-checked' %>
<%= link_to_remote l(:button_clear),
{ :url => { :set_filter => (@query.new_record? ? 1 : nil) },
:update => "content",
}, :class => 'icon icon-reload' if @query.new_record? %>
</p>
<% end %>
<%= error_messages_for 'query' %>
<% if @query.valid? %>
<% zoom = 1
@zoom.times { zoom = zoom * 2 }
@gantt.zoom.times { zoom = zoom * 2 }
subject_width = 330
header_heigth = 18
@@ -8,56 +56,23 @@ headers_height = header_heigth
show_weeks = false
show_days = false
if @zoom >1
if @gantt.zoom >1
show_weeks = true
headers_height = 2*header_heigth
if @zoom > 2
if @gantt.zoom > 2
show_days = true
headers_height = 3*header_heigth
end
end
g_width = (@date_to - @date_from + 1)*zoom
g_height = [(20 * @events.length + 6)+150, 206].max
g_width = (@gantt.date_to - @gantt.date_from + 1)*zoom
g_height = [(20 * @gantt.events.length + 6)+150, 206].max
t_height = g_height + headers_height
%>
<div class="contextual">
</div>
<h2><%= l(:label_gantt) %></h2>
<% form_tag(params.merge(:month => nil, :year => nil, :months => nil)) do %>
<table width="100%">
<tr>
<td align="left">
<input type="text" name="months" size="2" value="<%= @months %>" />
<%= l(:label_months_from) %>
<%= select_month(@month_from, :prefix => "month", :discard_type => true) %>
<%= select_year(@year_from, :prefix => "year", :discard_type => true) %>
<%= hidden_field_tag 'zoom', @zoom %>
<%= submit_tag l(:button_submit), :class => "button-small" %>
</td>
<td align="right">
<%= if @zoom < 4
link_to image_tag('zoom_in.png'), {:zoom => (@zoom+1), :year => @year_from, :month => @month_from, :months => @months, :tracker_ids => @selected_tracker_ids, :with_subprojects => params[:with_subprojects]}
else
image_tag 'zoom_in_g.png'
end %>
<%= if @zoom > 1
link_to image_tag('zoom_out.png'),{:zoom => (@zoom-1), :year => @year_from, :month => @month_from, :months => @months, :tracker_ids => @selected_tracker_ids, :with_subprojects => params[:with_subprojects]}
else
image_tag 'zoom_out_g.png'
end %>
</td>
</tr>
</table>
<% end %>
<table width="100%" style="border:0; border-collapse: collapse;">
<tr>
<td style="width:<%= subject_width %>px;">
<td style="width:<%= subject_width %>px; padding:0px;">
<div style="position:relative;height:<%= t_height + 24 %>px;width:<%= subject_width + 1 %>px;">
<div style="right:-2px;width:<%= subject_width %>px;height:<%= headers_height %>px;background: #eee;" class="gantt_hdr"></div>
@@ -67,7 +82,7 @@ t_height = g_height + headers_height
# Tasks subjects
#
top = headers_height + 8
@events.each do |i| %>
@gantt.events.each do |i| %>
<div style="position: absolute;line-height:1.2em;height:16px;top:<%= top %>px;left:4px;overflow:hidden;"><small>
<% if i.is_a? Issue %>
<%= h("#{i.project} -") unless @project && @project == i.project %>
@@ -91,14 +106,14 @@ end %>
#
# Months headers
#
month_f = @date_from
month_f = @gantt.date_from
left = 0
height = (show_weeks ? header_heigth : header_heigth + g_height)
@months.times do
@gantt.months.times do
width = ((month_f >> 1) - month_f) * zoom - 1
%>
<div style="left:<%= left %>px;width:<%= width %>px;height:<%= height %>px;" class="gantt_hdr">
<%= link_to "#{month_f.year}-#{month_f.month}", { :year => month_f.year, :month => month_f.month, :zoom => @zoom, :months => @months, :tracker_ids => @selected_tracker_ids, :with_subprojects => params[:with_subprojects] }, :title => "#{month_name(month_f.month)} #{month_f.year}"%>
<%= link_to "#{month_f.year}-#{month_f.month}", @gantt.params.merge(:year => month_f.year, :month => month_f.month), :title => "#{month_name(month_f.month)} #{month_f.year}"%>
</div>
<%
left = left + width + 1
@@ -112,21 +127,21 @@ end %>
if show_weeks
left = 0
height = (show_days ? header_heigth-1 : header_heigth-1 + g_height)
if @date_from.cwday == 1
if @gantt.date_from.cwday == 1
# @date_from is monday
week_f = @date_from
week_f = @gantt.date_from
else
# find next monday after @date_from
week_f = @date_from + (7 - @date_from.cwday + 1)
width = (7 - @date_from.cwday + 1) * zoom-1
week_f = @gantt.date_from + (7 - @gantt.date_from.cwday + 1)
width = (7 - @gantt.date_from.cwday + 1) * zoom-1
%>
<div style="left:<%= left %>px;top:19px;width:<%= width %>px;height:<%= height %>px;" class="gantt_hdr">&nbsp;</div>
<%
left = left + width+1
end %>
<%
while week_f <= @date_to
width = (week_f + 6 <= @date_to) ? 7 * zoom -1 : (@date_to - week_f + 1) * zoom-1
while week_f <= @gantt.date_to
width = (week_f + 6 <= @gantt.date_to) ? 7 * zoom -1 : (@gantt.date_to - week_f + 1) * zoom-1
%>
<div style="left:<%= left %>px;top:19px;width:<%= width %>px;height:<%= height %>px;" class="gantt_hdr">
<small><%= week_f.cweek if width >= 16 %></small>
@@ -144,8 +159,8 @@ end %>
if show_days
left = 0
height = g_height + header_heigth - 1
wday = @date_from.cwday
(@date_to - @date_from + 1).to_i.times do
wday = @gantt.date_from.cwday
(@gantt.date_to - @gantt.date_from + 1).to_i.times do
width = zoom - 1
%>
<div style="left:<%= left %>px;top:37px;width:<%= width %>px;height:<%= height %>px;font-size:0.7em;<%= "background:#f1f1f1;" if wday > 5 %>" class="gantt_hdr">
@@ -163,18 +178,18 @@ end %>
# Tasks
#
top = headers_height + 10
@events.each do |i|
@gantt.events.each do |i|
if i.is_a? Issue
i_start_date = (i.start_date >= @date_from ? i.start_date : @date_from )
i_end_date = (i.due_before <= @date_to ? i.due_before : @date_to )
i_start_date = (i.start_date >= @gantt.date_from ? i.start_date : @gantt.date_from )
i_end_date = (i.due_before <= @gantt.date_to ? i.due_before : @gantt.date_to )
i_done_date = i.start_date + ((i.due_before - i.start_date+1)*i.done_ratio/100).floor
i_done_date = (i_done_date <= @date_from ? @date_from : i_done_date )
i_done_date = (i_done_date >= @date_to ? @date_to : i_done_date )
i_done_date = (i_done_date <= @gantt.date_from ? @gantt.date_from : i_done_date )
i_done_date = (i_done_date >= @gantt.date_to ? @gantt.date_to : i_done_date )
i_late_date = [i_end_date, Date.today].min if i_start_date < Date.today
i_left = ((i_start_date - @date_from)*zoom).floor
i_left = ((i_start_date - @gantt.date_from)*zoom).floor
i_width = ((i_end_date - i_start_date + 1)*zoom).floor - 2 # total width of the issue (- 2 for left and right borders)
d_width = ((i_done_date - i_start_date)*zoom).floor - 2 # done width
l_width = i_late_date ? ((i_late_date - i_start_date+1)*zoom).floor - 2 : 0 # delay width
@@ -195,7 +210,7 @@ top = headers_height + 10
<%= render_issue_tooltip i %>
</span></div>
<% else
i_left = ((i.start_date - @date_from)*zoom).floor
i_left = ((i.start_date - @gantt.date_from)*zoom).floor
%>
<div style="top:<%= top %>px;left:<%= i_left %>px;width:15px;" class="task milestone">&nbsp;</div>
<div style="top:<%= top %>px;left:<%= i_left + 12 %>px;background:#fff;" class="task">
@@ -210,8 +225,8 @@ end %>
#
# Today red line (excluded from cache)
#
if Date.today >= @date_from and Date.today <= @date_to %>
<div style="position: absolute;height:<%= g_height %>px;top:<%= headers_height + 1 %>px;left:<%= ((Date.today-@date_from+1)*zoom).floor()-1 %>px;width:10px;border-left: 1px dashed red;">&nbsp;</div>
if Date.today >= @gantt.date_from and Date.today <= @gantt.date_to %>
<div style="position: absolute;height:<%= g_height %>px;top:<%= headers_height + 1 %>px;left:<%= ((Date.today-@gantt.date_from+1)*zoom).floor()-1 %>px;width:10px;border-left: 1px dashed red;">&nbsp;</div>
<% end %>
</div>
@@ -221,29 +236,22 @@ if Date.today >= @date_from and Date.today <= @date_to %>
<table width="100%">
<tr>
<td align="left"><%= link_to ('&#171; ' + l(:label_previous)), :year => (@date_from << @months).year, :month => (@date_from << @months).month, :zoom => @zoom, :months => @months, :tracker_ids => @selected_tracker_ids, :with_subprojects => params[:with_subprojects] %></td>
<td align="right"><%= link_to (l(:label_next) + ' &#187;'), :year => (@date_from >> @months).year, :month => (@date_from >> @months).month, :zoom => @zoom, :months => @months, :tracker_ids => @selected_tracker_ids, :with_subprojects => params[:with_subprojects] %></td>
<td align="left"><%= link_to_remote ('&#171; ' + l(:label_previous)), {:url => @gantt.params_previous, :update => 'content', :complete => 'window.scrollTo(0,0)'}, {:href => url_for(@gantt.params_previous)} %></td>
<td align="right"><%= link_to_remote (l(:label_next) + ' &#187;'), {:url => @gantt.params_next, :update => 'content', :complete => 'window.scrollTo(0,0)'}, {:href => url_for(@gantt.params_next)} %></td>
</tr>
</table>
<p class="other-formats">
<%= l(:label_export_to) %>
<span><%= link_to 'PDF', {:zoom => @zoom, :year => @year_from, :month => @month_from, :months => @months, :tracker_ids => @selected_tracker_ids, :with_subprojects => params[:with_subprojects], :format => 'pdf'}, :class => 'pdf' %></span>
<%= content_tag('span', link_to('PNG', {:zoom => @zoom, :year => @year_from, :month => @month_from, :months => @months, :tracker_ids => @selected_tracker_ids, :with_subprojects => params[:with_subprojects], :format => 'png'}, :class => 'image')) if respond_to?('gantt_image') %>
<span><%= link_to 'PDF', @gantt.params.merge(:format => 'pdf'), :class => 'pdf' %></span>
<% if @gantt.respond_to?('to_image') %>
<span><%= link_to 'PNG', @gantt.params.merge(:format => 'png'), :class => 'image' %></span>
<% end %>
</p>
<% end # query.valid? %>
<% content_for :sidebar do %>
<h3><%= l(:label_gantt) %></h3>
<% form_tag(params.merge(:tracker_ids => nil, :with_subprojects => nil), :method => :get) do %>
<% @trackers.each do |tracker| %>
<label><%= check_box_tag "tracker_ids[]", tracker.id, (@selected_tracker_ids.include? tracker.id.to_s) %> <%= tracker.name %></label><br />
<% end %>
<% if @project.active_children.any? %>
<br /><label><%= check_box_tag 'with_subprojects', 1, @with_subprojects %> <%=l(:label_subproject_plural)%></label>
<%= hidden_field_tag 'with_subprojects', 0 %>
<% end %>
<p><%= submit_tag l(:button_apply), :class => 'button-small', :name => nil %></p>
<% end %>
<%= render :partial => 'issues/sidebar' %>
<% end %>
<% html_title(l(:label_gantt)) -%>

View File

@@ -79,7 +79,7 @@
pdf.Ln
for changeset in @issue.changesets
pdf.SetFontStyle('B',8)
pdf.Cell(190,5, format_time(changeset.committed_on) + " - " + changeset.committer)
pdf.Cell(190,5, format_time(changeset.committed_on) + " - " + changeset.author.to_s)
pdf.Ln
unless changeset.comments.blank?
pdf.SetFontStyle('',8)

View File

@@ -10,6 +10,7 @@
<h2><%= @issue.tracker.name %> #<%= @issue.id %></h2>
<div class="issue <%= "status-#{@issue.status.position} priority-#{@issue.priority.position}" %>">
<%= avatar(@issue.author, :size => "64") %>
<h3><%=h @issue.subject %></h3>
<p class="author">
<%= authoring @issue.created_on, @issue.author %>.
@@ -18,28 +19,28 @@
<table width="100%">
<tr>
<td style="width:15%"><b><%=l(:field_status)%>:</b></td><td style="width:35%"><%= @issue.status.name %></td>
<td style="width:15%"><b><%=l(:field_start_date)%>:</b></td><td style="width:35%"><%= format_date(@issue.start_date) %></td>
<td style="width:15%" class="status"><b><%=l(:field_status)%>:</b></td><td style="width:35%" class="status status-<%= @issue.status.name %>"><%= @issue.status.name %></td>
<td style="width:15%" class="start-date"><b><%=l(:field_start_date)%>:</b></td><td style="width:35%"><%= format_date(@issue.start_date) %></td>
</tr>
<tr>
<td><b><%=l(:field_priority)%>:</b></td><td><%= @issue.priority.name %></td>
<td><b><%=l(:field_due_date)%>:</b></td><td><%= format_date(@issue.due_date) %></td>
<td class="priority"><b><%=l(:field_priority)%>:</b></td><td class="priority priority-<%= @issue.priority.name %>"><%= @issue.priority.name %></td>
<td class="due-date"><b><%=l(:field_due_date)%>:</b></td><td class="due-date"><%= format_date(@issue.due_date) %></td>
</tr>
<tr>
<td><b><%=l(:field_assigned_to)%>:</b></td><td><%= @issue.assigned_to ? link_to_user(@issue.assigned_to) : "-" %></td>
<td><b><%=l(:field_done_ratio)%>:</b></td><td><%= progress_bar @issue.done_ratio, :width => '80px', :legend => "#{@issue.done_ratio}%" %></td>
<td class="assigned-to"><b><%=l(:field_assigned_to)%>:</b></td><td><%= avatar(@issue.assigned_to, :size => "14") %><%= @issue.assigned_to ? link_to_user(@issue.assigned_to) : "-" %></td>
<td class="progress"><b><%=l(:field_done_ratio)%>:</b></td><td class="progress"><%= progress_bar @issue.done_ratio, :width => '80px', :legend => "#{@issue.done_ratio}%" %></td>
</tr>
<tr>
<td><b><%=l(:field_category)%>:</b></td><td><%=h @issue.category ? @issue.category.name : "-" %></td>
<td class="category"><b><%=l(:field_category)%>:</b></td><td><%=h @issue.category ? @issue.category.name : "-" %></td>
<% if User.current.allowed_to?(:view_time_entries, @project) %>
<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', :project_id => @project, :issue_id => @issue}, :class => 'icon icon-time') : "-" %></td>
<td class="spent-time"><b><%=l(:label_spent_time)%>:</b></td>
<td class="spent-hours"><%= @issue.spent_hours > 0 ? (link_to lwr(:label_f_hour, @issue.spent_hours), {:controller => 'timelog', :action => 'details', :project_id => @project, :issue_id => @issue}, :class => 'icon icon-time') : "-" %></td>
<% end %>
</tr>
<tr>
<td><b><%=l(:field_fixed_version)%>:</b></td><td><%= @issue.fixed_version ? link_to_version(@issue.fixed_version) : "-" %></td>
<td class="fixed-version"><b><%=l(:field_fixed_version)%>:</b></td><td><%= @issue.fixed_version ? link_to_version(@issue.fixed_version) : "-" %></td>
<% if @issue.estimated_hours %>
<td><b><%=l(:field_estimated_hours)%>:</b></td><td><%= lwr(:label_f_hour, @issue.estimated_hours) %></td>
<td class="estimated-hours"><b><%=l(:field_estimated_hours)%>:</b></td><td><%= lwr(:label_f_hour, @issue.estimated_hours) %></td>
<% end %>
</tr>
<tr>
@@ -53,15 +54,12 @@
<%end
end %>
</tr>
<%= Redmine::Plugin::Hook::Manager.call_hook(:issue_show, {:project => @project, :issue => @issue}) %>
<%= call_hook(:view_issues_show_details_bottom, :issue => @issue) %>
</table>
<hr />
<div class="contextual">
<%= link_to_remote(image_tag('comment.png'),
{ :url => {:controller => 'issues', :action => 'reply', :id => @issue} },
:title => l(:button_reply)) if authorize_for('issues', 'edit') %>
<%= link_to_remote_if_authorized l(:button_quote), { :url => {:action => 'reply', :id => @issue} }, :class => 'icon icon-comment' %>
</div>
<p><strong><%=l(:field_description)%></strong></p>
@@ -80,6 +78,14 @@ end %>
</div>
<% end %>
<% if User.current.allowed_to?(:add_issue_watchers, @project) ||
(@issue.watchers.any? && User.current.allowed_to?(:view_issue_watchers, @project)) %>
<hr />
<div id="watchers">
<%= render :partial => 'watchers/watchers', :locals => {:watched => @issue} %>
</div>
<% end %>
</div>
<% if @issue.changesets.any? && User.current.allowed_to?(:view_changesets, @project) %>

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