Compare commits

..

261 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
392 changed files with 10482 additions and 14542 deletions

View File

@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2009 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -15,19 +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.
module Redmine
module Views
class OtherFormatsBuilder
def initialize(view)
@view = view
end
def link_to(name, options={})
url = { :format => name.to_s.downcase }.merge(options.delete(:url) || {})
caption = options.delete(:caption) || name
html_options = { :class => name.to_s.downcase, :rel => 'nofollow' }.merge(options)
@view.content_tag('span', @view.link_to(caption, url, html_options))
end
end
end
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_with_repository_enabled,
:expects => [],
:returns => [[AWSProjectWithRepository]]
api_method :repository_created,
:expects => [:string, :string, :string],
:returns => [:int]
end

View File

@@ -46,10 +46,24 @@ class AccountController < ApplicationController
self.logged_user = nil
else
# Authenticate user
if Setting.openid? && using_open_id?
open_id_authenticate(params[:openid_url])
user = User.try_to_login(params[:username], params[:password])
if user.nil?
# Invalid credentials
flash.now[:error] = l(:notice_account_invalid_creditentials)
elsif user.new_record?
# Onthefly creation failed, display the registration form to fill/fix attributes
@user = user
session[:auth_source_registration] = {:login => user.login, :auth_source_id => user.auth_source_id }
render :action => 'register'
else
password_authentication
# Valid user
self.logged_user = user
# generate a key and set cookie if autologin
if params[:autologin] && Setting.autologin?
token = Token.create(:user => user, :action => 'autologin')
cookies[:autologin] = { :value => token.value, :expires => 1.year.from_now }
end
redirect_back_or_default :controller => 'my', :action => 'page'
end
end
end
@@ -122,14 +136,31 @@ class AccountController < ApplicationController
else
@user.login = params[:user][:login]
@user.password, @user.password_confirmation = params[:password], params[:password_confirmation]
case Setting.self_registration
when '1'
register_by_email_activation(@user)
# Email activation
token = Token.new(:user => @user, :action => "register")
if @user.save and token.save
Mailer.deliver_register(token)
flash[:notice] = l(:notice_account_register_done)
redirect_to :action => 'login'
end
when '3'
register_automatically(@user)
# Automatic activation
@user.status = User::STATUS_ACTIVE
if @user.save
self.logged_user = @user
flash[:notice] = l(:notice_account_activated)
redirect_to :controller => 'my', :action => 'account'
end
else
register_manually_by_administrator(@user)
# Manual activation by the administrator
if @user.save
# Sends an email to the administrators
Mailer.deliver_account_activation_request(@user)
flash[:notice] = l(:notice_account_pending)
redirect_to :action => 'login'
end
end
end
end
@@ -160,119 +191,4 @@ private
session[:user_id] = nil
end
end
def password_authentication
user = User.try_to_login(params[:username], params[:password])
if user.nil?
# Invalid credentials
flash.now[:error] = l(:notice_account_invalid_creditentials)
elsif user.new_record?
# Onthefly creation failed, display the registration form to fill/fix attributes
@user = user
session[:auth_source_registration] = {:login => user.login, :auth_source_id => user.auth_source_id }
render :action => 'register'
else
# Valid user
successful_authentication(user)
end
end
def open_id_authenticate(openid_url)
authenticate_with_open_id(openid_url, :required => [:nickname, :fullname, :email], :return_to => signin_url) do |result, identity_url, registration|
if result.successful?
user = User.find_or_initialize_by_identity_url(identity_url)
if user.new_record?
# Self-registration off
redirect_to(home_url) && return unless Setting.self_registration?
# Create on the fly
user.login = registration['nickname'] unless registration['nickname'].nil?
user.mail = registration['email'] unless registration['email'].nil?
user.firstname, user.lastname = registration['fullname'].split(' ') unless registration['fullname'].nil?
user.random_password
user.status = User::STATUS_REGISTERED
case Setting.self_registration
when '1'
register_by_email_activation(user) do
onthefly_creation_failed(user, {:login => user.login, :identity_url => identity_url })
end
when '3'
register_automatically(user) do
onthefly_creation_failed(user, {:login => user.login, :identity_url => identity_url })
end
else
register_manually_by_administrator(user) do
onthefly_creation_failed(user, {:login => user.login, :identity_url => identity_url })
end
end
else
# Existing record
successful_authentication(user)
end
end
end
end
def successful_authentication(user)
# Valid user
self.logged_user = user
# generate a key and set cookie if autologin
if params[:autologin] && Setting.autologin?
token = Token.create(:user => user, :action => 'autologin')
cookies[:autologin] = { :value => token.value, :expires => 1.year.from_now }
end
redirect_back_or_default :controller => 'my', :action => 'page'
end
# Onthefly creation failed, display the registration form to fill/fix attributes
def onthefly_creation_failed(user, auth_source_options = { })
@user = user
session[:auth_source_registration] = auth_source_options unless auth_source_options.empty?
render :action => 'register'
end
# Register a user for email activation.
#
# Pass a block for behavior when a user fails to save
def register_by_email_activation(user, &block)
token = Token.new(:user => user, :action => "register")
if user.save and token.save
Mailer.deliver_register(token)
flash[:notice] = l(:notice_account_register_done)
redirect_to :action => 'login'
else
yield if block_given?
end
end
# Automatically register a user
#
# Pass a block for behavior when a user fails to save
def register_automatically(user, &block)
# Automatic activation
user.status = User::STATUS_ACTIVE
if user.save
self.logged_user = user
flash[:notice] = l(:notice_account_activated)
redirect_to :controller => 'my', :action => 'account'
else
yield if block_given?
end
end
# Manual activation by the administrator
#
# Pass a block for behavior when a user fails to save
def register_manually_by_administrator(user, &block)
if user.save
# Sends an email to the administrators
Mailer.deliver_account_activation_request(user)
flash[:notice] = l(:notice_account_pending)
redirect_to :action => 'login'
else
yield if block_given?
end
end
end

View File

@@ -26,6 +26,9 @@ class AdminController < ApplicationController
end
def projects
sort_init 'name', 'asc'
sort_update
@status = params[:status] ? params[:status].to_i : 1
c = ARCondition.new(@status == 0 ? "status <> 0" : ["status = ?", @status])
@@ -34,8 +37,14 @@ class AdminController < ApplicationController
c << ["LOWER(identifier) LIKE ? OR LOWER(name) LIKE ?", name, name]
end
@projects = Project.find :all, :order => 'lft',
:conditions => c.conditions
@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 => c.conditions,
:limit => @project_pages.items_per_page,
:offset => @project_pages.current.offset
render :action => "projects", :layout => false if request.xhr?
end
@@ -77,7 +86,6 @@ class AdminController < ApplicationController
@flags = {
:default_admin_changed => User.find(:first, :conditions => ["login=? and hashed_password=?", 'admin', User.hash_password('admin')]).nil?,
:file_repository_writable => File.writable?(Attachment.storage_path),
:plugin_assets_writable => File.writable?(Engines.public_directory),
:rmagick_available => Object.const_defined?(:Magick)
}
end

View File

@@ -126,14 +126,10 @@ class ApplicationController < ActionController::Base
def redirect_back_or_default(default)
back_url = CGI.unescape(params[:back_url].to_s)
if !back_url.blank?
begin
uri = URI.parse(back_url)
# 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
rescue URI::InvalidURIError
# redirect to default
uri = URI.parse(back_url)
# 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
redirect_to default
@@ -175,7 +171,6 @@ class ApplicationController < ActionController::Base
# TODO: move to model
def attach_files(obj, attachments)
attached = []
unsaved = []
if attachments && attachments.is_a?(Hash)
attachments.each_value do |attachment|
file = attachment['file']
@@ -184,10 +179,7 @@ class ApplicationController < ActionController::Base
:file => file,
:description => attachment['description'].to_s.strip,
:author => User.current)
a.new_record? ? (unsaved << a) : (attached << a)
end
if unsaved.any?
flash[:warning] = l(:warning_attachments_not_saved, unsaved.size)
attached << a unless a.new_record?
end
end
attached

View File

@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2008 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -17,11 +17,7 @@
class AttachmentsController < ApplicationController
before_filter :find_project
before_filter :read_authorize, :except => :destroy
before_filter :delete_authorize, :only => :destroy
verify :method => :post, :only => :destroy
def show
if @attachment.is_diff?
@diff = File.new(@attachment.diskfile, "rb").read
@@ -29,46 +25,31 @@ class AttachmentsController < ApplicationController
elsif @attachment.is_text?
@content = File.new(@attachment.diskfile, "rb").read
render :action => 'file'
else
elsif
download
end
end
def download
if @attachment.container.is_a?(Version) || @attachment.container.is_a?(Project)
@attachment.increment_download
end
@attachment.increment_download if @attachment.container.is_a?(Version)
# images are sent inline
send_file @attachment.diskfile, :filename => filename_for_content_disposition(@attachment.filename),
:type => @attachment.content_type,
:disposition => (@attachment.image? ? 'inline' : 'attachment')
end
def destroy
# Make sure association callbacks are called
@attachment.container.attachments.delete(@attachment)
redirect_to :back
rescue ::ActionController::RedirectBackError
redirect_to :controller => 'projects', :action => 'show', :id => @project
end
private
def find_project
@attachment = Attachment.find(params[:id])
# Show 404 if the filename in the url is wrong
raise ActiveRecord::RecordNotFound if params[:filename] && params[:filename] != @attachment.filename
@project = @attachment.project
permission = @attachment.container.is_a?(Version) ? :view_files : "view_#{@attachment.container.class.name.underscore.pluralize}".to_sym
allowed = User.current.allowed_to?(permission, @project)
allowed ? true : (User.current.logged? ? render_403 : require_login)
rescue ActiveRecord::RecordNotFound
render_404
end
def read_authorize
@attachment.visible? ? true : deny_access
end
def delete_authorize
@attachment.deletable? ? true : deny_access
end
end

View File

@@ -35,14 +35,12 @@ class BoardsController < ApplicationController
end
def show
sort_init 'updated_on', 'desc'
sort_update 'created_on' => "#{Message.table_name}.created_on",
'replies' => "#{Message.table_name}.replies_count",
'updated_on' => "#{Message.table_name}.updated_on"
sort_init "#{Message.table_name}.updated_on", "desc"
sort_update
@topic_count = @board.topics.count
@topic_pages = Paginator.new self, @topic_count, per_page_option, params['page']
@topics = @board.topics.find :all, :order => ["#{Message.table_name}.sticky DESC", sort_clause].compact.join(', '),
@topics = @board.topics.find :all, :order => "#{Message.table_name}.sticky DESC, #{sort_clause}",
:include => [:author, {:last_reply => :author}],
:limit => @topic_pages.items_per_page,
:offset => @topic_pages.current.offset

View File

@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2009 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -19,22 +19,34 @@ class CustomFieldsController < ApplicationController
before_filter :require_admin
def index
list
render :action => 'list' unless request.xhr?
end
def list
@custom_fields_by_type = CustomField.find(:all).group_by {|f| f.class.name }
@tab = params[:tab] || 'IssueCustomField'
render :action => "list", :layout => false if request.xhr?
end
def new
@custom_field = begin
if params[:type].to_s.match(/.+CustomField$/)
params[:type].to_s.constantize.new(params[:custom_field])
end
rescue
end
redirect_to(:action => 'index') and return unless @custom_field.is_a?(CustomField)
case params[:type]
when "IssueCustomField"
@custom_field = IssueCustomField.new(params[:custom_field])
@custom_field.trackers = Tracker.find(params[:tracker_ids]) if params[:tracker_ids]
when "UserCustomField"
@custom_field = UserCustomField.new(params[:custom_field])
when "ProjectCustomField"
@custom_field = ProjectCustomField.new(params[:custom_field])
when "TimeEntryCustomField"
@custom_field = TimeEntryCustomField.new(params[:custom_field])
else
redirect_to :action => 'list'
return
end
if request.post? and @custom_field.save
flash[:notice] = l(:notice_successful_create)
redirect_to :action => 'index', :tab => @custom_field.class.name
redirect_to :action => 'list', :tab => @custom_field.class.name
end
@trackers = Tracker.find(:all, :order => 'position')
end
@@ -42,8 +54,11 @@ class CustomFieldsController < ApplicationController
def edit
@custom_field = CustomField.find(params[:id])
if request.post? and @custom_field.update_attributes(params[:custom_field])
if @custom_field.is_a? IssueCustomField
@custom_field.trackers = params[:tracker_ids] ? Tracker.find(params[:tracker_ids]) : []
end
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'index', :tab => @custom_field.class.name
redirect_to :action => 'list', :tab => @custom_field.class.name
end
@trackers = Tracker.find(:all, :order => 'position')
end
@@ -60,14 +75,14 @@ class CustomFieldsController < ApplicationController
when 'lowest'
@custom_field.move_to_bottom
end if params[:position]
redirect_to :action => 'index', :tab => @custom_field.class.name
redirect_to :action => 'list', :tab => @custom_field.class.name
end
def destroy
@custom_field = CustomField.find(params[:id]).destroy
redirect_to :action => 'index', :tab => @custom_field.class.name
redirect_to :action => 'list', :tab => @custom_field.class.name
rescue
flash[:error] = "Unable to delete custom field"
redirect_to :action => 'index'
redirect_to :action => 'list'
end
end

View File

@@ -35,7 +35,6 @@ class DocumentsController < ApplicationController
else
@grouped = documents.group_by(&:category)
end
@document = @project.documents.build
render :layout => false if request.xhr?
end
@@ -71,6 +70,11 @@ class DocumentsController < ApplicationController
Mailer.deliver_attachments_added(attachments) if !attachments.empty? && Setting.notified_events.include?('document_added')
redirect_to :action => 'show', :id => @document
end
def destroy_attachment
@document.attachments.find(params[:attachment_id]).destroy
redirect_to :action => 'show', :id => @document
end
private
def find_project

View File

@@ -21,9 +21,6 @@ class IssueRelationsController < ApplicationController
def new
@relation = IssueRelation.new(params[:relation])
@relation.issue_from = @issue
if params[:relation] && !params[:relation][:issue_to_id].blank?
@relation.issue_to = Issue.visible.find_by_id(params[:relation][:issue_to_id])
end
@relation.save if request.post?
respond_to do |format|
format.html { redirect_to :controller => 'issues', :action => 'show', :id => @issue }

View File

@@ -18,7 +18,7 @@
class IssuesController < ApplicationController
menu_item :new_issue, :only => :new
before_filter :find_issue, :only => [:show, :edit, :reply]
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, :gantt, :calendar, :preview, :update_form, :context_menu]
@@ -30,6 +30,8 @@ class IssuesController < ApplicationController
include ProjectsHelper
helper :custom_fields
include CustomFieldsHelper
helper :ifpdf
include IfpdfHelper
helper :issue_relations
include IssueRelationsHelper
helper :watchers
@@ -41,13 +43,11 @@ class IssuesController < ApplicationController
include SortHelper
include IssuesHelper
helper :timelog
include Redmine::Export::PDF
def index
sort_init "#{Issue.table_name}.id", "desc"
sort_update
retrieve_query
sort_init 'id', 'desc'
sort_update({'id' => "#{Issue.table_name}.id"}.merge(@query.columns.inject({}) {|h, c| h[c.name.to_s] = c.sortable; h}))
if @query.valid?
limit = per_page_option
respond_to do |format|
@@ -67,7 +67,7 @@ class IssuesController < ApplicationController
format.html { render :template => 'issues/index.rhtml', :layout => !request.xhr? }
format.atom { render_feed(@issues, :title => "#{@project || Setting.app_title}: #{l(:label_issue_plural)}") }
format.csv { send_data(issues_to_csv(@issues, @project).read, :type => 'text/csv; header=present', :filename => 'export.csv') }
format.pdf { send_data(issues_to_pdf(@issues, @project), :type => 'application/pdf', :filename => 'export.pdf') }
format.pdf { send_data(render(:template => 'issues/index.rfpdf', :layout => false), :type => 'application/pdf', :filename => 'export.pdf') }
end
else
# Send html if the query is not valid
@@ -78,10 +78,9 @@ class IssuesController < ApplicationController
end
def changes
sort_init "#{Issue.table_name}.id", "desc"
sort_update
retrieve_query
sort_init 'id', 'desc'
sort_update({'id' => "#{Issue.table_name}.id"}.merge(@query.columns.inject({}) {|h, c| h[c.name.to_s] = c.sortable; h}))
if @query.valid?
@journals = Journal.find :all, :include => [ :details, :user, {:issue => [:project, :author, :tracker, :status]} ],
:conditions => @query.statement,
@@ -105,7 +104,7 @@ class IssuesController < ApplicationController
respond_to do |format|
format.html { render :template => 'issues/show.rhtml' }
format.atom { render :action => 'changes', :layout => false, :content_type => 'application/atom+xml' }
format.pdf { send_data(issue_to_pdf(@issue), :type => 'application/pdf', :filename => "#{@project.identifier}-#{@issue.id}.pdf") }
format.pdf { send_data(render(:template => 'issues/show.rfpdf', :layout => false), :type => 'application/pdf', :filename => "#{@project.identifier}-#{@issue.id}.pdf") }
end
end
@@ -122,10 +121,7 @@ class IssuesController < ApplicationController
render :nothing => true, :layout => true
return
end
if params[:issue].is_a?(Hash)
@issue.attributes = params[:issue]
@issue.watcher_user_ids = params[:issue]['watcher_user_ids'] if User.current.allowed_to?(:add_issue_watchers, @project)
end
@issue.attributes = params[:issue]
@issue.author = User.current
default_status = IssueStatus.default
@@ -147,9 +143,7 @@ class IssuesController < ApplicationController
attach_files(@issue, params[:attachments])
flash[:notice] = l(:notice_successful_create)
Mailer.deliver_issue_add(@issue) if Setting.notified_events.include?('issue_added')
call_hook(:controller_issues_new_after_save, { :params => params, :issue => @issue})
redirect_to(params[:continue] ? { :action => 'new', :tracker_id => @issue.tracker } :
{ :action => 'show', :id => @issue })
redirect_to :controller => 'issues', :action => 'show', :id => @issue
return
end
end
@@ -187,7 +181,7 @@ class IssuesController < ApplicationController
if (@time_entry.hours.nil? || @time_entry.valid?) && @issue.save
# Log spend time
if User.current.allowed_to?(:log_time, @project)
if current_role.allowed_to?(:log_time)
@time_entry.save
end
if !journal.new_record?
@@ -195,7 +189,6 @@ class IssuesController < ApplicationController
flash[:notice] = l(:notice_successful_update)
Mailer.deliver_issue_edit(journal) if Setting.notified_events.include?('issue_updated')
end
call_hook(:controller_issues_edit_after_save, { :params => params, :issue => @issue, :time_entry => @time_entry, :journal => journal})
redirect_to(params[:back_to] || {:action => 'show', :id => @issue})
end
end
@@ -232,7 +225,6 @@ 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])
custom_field_values = params[:custom_field_values] ? params[:custom_field_values].reject {|k,v| v.blank?} : nil
unsaved_issue_ids = []
@issues.each do |issue|
@@ -244,7 +236,6 @@ 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?
issue.custom_field_values = custom_field_values if custom_field_values && !custom_field_values.empty?
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
@@ -266,7 +257,6 @@ class IssuesController < ApplicationController
# 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.sort
@custom_fields = @project.issue_custom_fields.select {|f| f.field_format == 'list'}
end
def move
@@ -274,7 +264,7 @@ class IssuesController < ApplicationController
# find projects to which the user is allowed to move the issue
if User.current.admin?
# admin is allowed to move issues to any active (visible) project
@allowed_projects = Project.find(:all, :conditions => Project.visible_by(User.current))
@allowed_projects = Project.find(:all, :conditions => Project.visible_by(User.current), :order => 'name')
else
User.current.memberships.each {|m| @allowed_projects << m.project if m.role.allowed_to?(:move_issues)}
end
@@ -286,7 +276,7 @@ class IssuesController < ApplicationController
unsaved_issue_ids = []
@issues.each do |issue|
issue.init_journal(User.current)
unsaved_issue_ids << issue.id unless issue.move_to(@target_project, new_tracker, params[:copy_options])
unsaved_issue_ids << issue.id unless issue.move_to(@target_project, new_tracker)
end
if unsaved_issue_ids.empty?
flash[:notice] = l(:notice_successful_update) unless @issues.empty?
@@ -323,6 +313,17 @@ class IssuesController < ApplicationController
@issues.each(&:destroy)
redirect_to :action => 'index', :project_id => @project
end
def destroy_attachment
a = @issue.attachments.find(params[:attachment_id])
a.destroy
journal = @issue.init_journal(User.current)
journal.details << JournalDetail.new(:property => 'attachment',
:prop_key => a.id,
:old_value => a.filename)
journal.save
redirect_to :action => 'show', :id => @issue
end
def gantt
@gantt = Redmine::Helpers::Gantt.new(params)
@@ -350,8 +351,8 @@ class IssuesController < ApplicationController
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.nil? ? '' : "#{@project.identifier}-" }gantt.png") } if @gantt.respond_to?('to_image')
format.pdf { send_data(gantt_to_pdf(@gantt, @project), :type => 'application/pdf', :filename => "#{@project.nil? ? '' : "#{@project.identifier}-" }gantt.pdf") }
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

View File

@@ -22,7 +22,6 @@ class JournalsController < ApplicationController
if request.post?
@journal.update_attributes(:notes => params[:notes]) if params[:notes]
@journal.destroy if @journal.details.empty? && @journal.notes.blank?
call_hook(:controller_journals_edit_post, { :journal => @journal, :params => params})
respond_to do |format|
format.html { redirect_to :controller => 'issues', :action => 'show', :id => @journal.journalized_id }
format.js { render :action => 'update' }

View File

@@ -19,7 +19,6 @@ class MyController < ApplicationController
before_filter :require_login
helper :issues
helper :custom_fields
BLOCKS = { 'issuesassignedtome' => :label_assigned_to_me_issues,
'issuesreportedbyme' => :label_reported_issues,

View File

@@ -29,16 +29,12 @@ class ProjectsController < ApplicationController
before_filter :require_admin, :only => [ :add, :archive, :unarchive, :destroy ]
accept_key_auth :activity
after_filter :only => [:add, :edit, :archive, :unarchive, :destroy] do |controller|
if controller.request.post?
controller.send :expire_action, :controller => 'welcome', :action => 'robots.txt'
end
end
helper :sort
include SortHelper
helper :custom_fields
include CustomFieldsHelper
helper :ifpdf
include IfpdfHelper
helper :issues
helper IssuesHelper
helper :queries
@@ -49,14 +45,17 @@ class ProjectsController < ApplicationController
# Lists visible projects
def index
projects = Project.find :all,
:conditions => Project.visible_by(User.current),
:include => :parent
respond_to do |format|
format.html {
@projects = Project.visible.find(:all, :order => 'lft')
@project_tree = projects.group_by {|p| p.parent || p}
@project_tree.keys.each {|p| @project_tree[p] -= [p]}
}
format.atom {
projects = Project.visible.find(:all, :order => 'created_on DESC',
:limit => Setting.feeds_limit.to_i)
render_feed(projects, :title => "#{Setting.app_title}: #{l(:label_project_latest)}")
render_feed(projects.sort_by(&:created_on).reverse.slice(0, Setting.feeds_limit.to_i),
:title => "#{Setting.app_title}: #{l(:label_project_latest)}")
}
end
end
@@ -65,6 +64,9 @@ class ProjectsController < ApplicationController
def add
@issue_custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position")
@trackers = Tracker.all
@root_projects = Project.find(:all,
:conditions => "parent_id IS NULL AND status = #{Project::STATUS_ACTIVE}",
:order => 'name')
@project = Project.new(params[:project])
if request.get?
@project.identifier = Project.next_identifier if Setting.sequential_project_identifiers?
@@ -74,7 +76,6 @@ class ProjectsController < ApplicationController
else
@project.enabled_module_names = params[:enabled_modules]
if @project.save
@project.set_parent!(params[:project]['parent_id']) if User.current.admin? && params[:project].has_key?('parent_id')
flash[:notice] = l(:notice_successful_create)
redirect_to :controller => 'admin', :action => 'projects'
end
@@ -83,26 +84,20 @@ class ProjectsController < ApplicationController
# Show @project
def show
if params[:jump]
# try to redirect to the requested menu item
redirect_to_project_menu_item(@project, params[:jump]) && return
end
@members_by_role = @project.members.find(:all, :include => [:user, :role], :order => 'position').group_by {|m| m.role}
@subprojects = @project.children.visible
@ancestors = @project.ancestors.visible
@subprojects = @project.children.find(:all, :conditions => Project.visible_by(User.current))
@news = @project.news.find(:all, :limit => 5, :include => [ :author, :project ], :order => "#{News.table_name}.created_on DESC")
@trackers = @project.rolled_up_trackers
cond = @project.project_condition(Setting.display_subprojects_issues?)
@open_issues_by_tracker = Issue.visible.count(:group => :tracker,
Issue.visible_by(User.current) do
@open_issues_by_tracker = Issue.count(:group => :tracker,
:include => [:project, :status, :tracker],
:conditions => ["(#{cond}) AND #{IssueStatus.table_name}.is_closed=?", false])
@total_issues_by_tracker = Issue.visible.count(:group => :tracker,
@total_issues_by_tracker = Issue.count(:group => :tracker,
:include => [:project, :status, :tracker],
:conditions => cond)
end
TimeEntry.visible_by(User.current) do
@total_hours = TimeEntry.sum(:hours,
:include => :project,
@@ -112,6 +107,9 @@ class ProjectsController < ApplicationController
end
def settings
@root_projects = Project.find(:all,
:conditions => ["parent_id IS NULL AND status = #{Project::STATUS_ACTIVE} AND id <> ?", @project.id],
:order => 'name')
@issue_custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position")
@issue_category ||= IssueCategory.new
@member ||= @project.members.new
@@ -125,7 +123,6 @@ class ProjectsController < ApplicationController
if request.post?
@project.attributes = params[:project]
if @project.save
@project.set_parent!(params[:project]['parent_id']) if User.current.admin? && params[:project].has_key?('parent_id')
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'settings', :id => @project
else
@@ -191,26 +188,18 @@ class ProjectsController < ApplicationController
def add_file
if request.post?
container = (params[:version_id].blank? ? @project : @project.versions.find_by_id(params[:version_id]))
attachments = attach_files(container, params[:attachments])
if !attachments.empty? && Setting.notified_events.include?('file_added')
Mailer.deliver_attachments_added(attachments)
end
@version = @project.versions.find_by_id(params[:version_id])
attachments = attach_files(@version, params[:attachments])
Mailer.deliver_attachments_added(attachments) if !attachments.empty? && Setting.notified_events.include?('file_added')
redirect_to :controller => 'projects', :action => 'list_files', :id => @project
return
end
@versions = @project.versions.sort
end
def list_files
sort_init 'filename', 'asc'
sort_update 'filename' => "#{Attachment.table_name}.filename",
'created_on' => "#{Attachment.table_name}.created_on",
'size' => "#{Attachment.table_name}.filesize",
'downloads' => "#{Attachment.table_name}.downloads"
@containers = [ Project.find(@project.id, :include => :attachments, :order => sort_clause)]
@containers += @project.versions.find(:all, :include => :attachments, :order => sort_clause).sort.reverse
sort_init "#{Attachment.table_name}.filename", "asc"
sort_update
@versions = @project.versions.find(:all, :include => :attachments, :order => sort_clause).sort.reverse
render :layout => !request.xhr?
end

View File

@@ -61,7 +61,7 @@ class ReportsController < ApplicationController
render :template => "reports/issue_report_details"
when "subproject"
@field = "project_id"
@rows = @project.descendants.active
@rows = @project.active_children
@data = issues_by_subproject
@report_title = l(:field_subproject)
render :template => "reports/issue_report_details"
@@ -72,7 +72,7 @@ class ReportsController < ApplicationController
@categories = @project.issue_categories
@assignees = @project.members.collect { |m| m.user }
@authors = @project.members.collect { |m| m.user }
@subprojects = @project.descendants.active
@subprojects = @project.active_children
issues_by_tracker
issues_by_version
issues_by_priority
@@ -229,8 +229,8 @@ private
#{Issue.table_name} i, #{IssueStatus.table_name} s
where
i.status_id=s.id
and i.project_id IN (#{@project.descendants.active.collect{|p| p.id}.join(',')})
group by s.id, s.is_closed, i.project_id") if @project.descendants.active.any?
and i.project_id IN (#{@project.active_children.collect{|p| p.id}.join(',')})
group by s.id, s.is_closed, i.project_id") if @project.active_children.any?
@issues_by_subproject ||= []
end
end

View File

@@ -51,9 +51,8 @@ class RepositoriesController < ApplicationController
@users += User.find_all_by_id(additional_user_ids) unless additional_user_ids.empty?
@users.compact!
@users.sort!
if request.post? && params[:committers].is_a?(Hash)
# Build a hash with repository usernames as keys and corresponding user ids as values
@repository.committer_ids = params[:committers].values.inject({}) {|h, c| h[c.first] = c.last; h}
if request.post?
@repository.committer_ids = params[:committers]
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'committers', :id => @project
end
@@ -88,7 +87,7 @@ class RepositoriesController < ApplicationController
def changes
@entry = @repository.entry(@path, @rev)
show_error_not_found and return unless @entry
@changesets = @repository.changesets_for_path(@path, :limit => Setting.repository_log_display_limit.to_i)
@changesets = @repository.changesets_for_path(@path)
@properties = @repository.properties(@path, @rev)
end
@@ -127,9 +126,6 @@ class RepositoriesController < ApplicationController
end
def annotate
@entry = @repository.entry(@path, @rev)
show_error_not_found and return unless @entry
@annotate = @repository.scm.annotate(@path, @rev)
render_error l(:error_scm_annotate) and return if @annotate.nil? || @annotate.empty?
end

View File

@@ -34,7 +34,7 @@ class SearchController < ApplicationController
when 'my_projects'
User.current.memberships.collect(&:project)
when 'subprojects'
@project ? (@project.self_and_descendants.active) : nil
@project ? ([ @project ] + @project.active_children) : nil
else
@project
end

View File

@@ -40,8 +40,8 @@ class SettingsController < ApplicationController
@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.dup
@guessed_host_and_path << ('/'+ Redmine::Utils.relative_url_root.gsub(%r{^\/}, '')) unless Redmine::Utils.relative_url_root.blank?
@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

View File

@@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2009 Jean-Philippe Lang
# redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -16,35 +16,31 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class SysController < ActionController::Base
before_filter :check_enabled
wsdl_service_name 'Sys'
web_service_api SysApi
web_service_scaffold :invoke
def projects
p = Project.active.has_module(:repository).find(:all, :include => :repository, :order => 'identifier')
render :xml => p.to_xml(:include => :repository)
end
before_invocation :check_enabled
def create_project_repository
project = Project.find(params[:id])
if project.repository
render :nothing => true, :status => 409
else
logger.info "Repository for #{project.name} was reported to be created by #{request.remote_ip}."
project.repository = Repository.factory(params[:vendor], params[:repository])
if project.repository && project.repository.save
render :xml => project.repository, :status => 201
else
render :nothing => true, :status => 422
end
end
# Returns the projects list, with their repositories
def projects_with_repository_enabled
Project.has_module(:repository).find(:all, :include => :repository, :order => 'identifier')
end
protected
# Registers a repository for the given project identifier
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(vendor, :project => project, :url => url)
repository.save
repository.id || 0
end
def check_enabled
User.current = nil
unless Setting.sys_api_enabled?
render :nothing => 'Access denied. Repository management WS is disabled.', :status => 403
return false
end
protected
def check_enabled(name, args)
Setting.sys_api_enabled?
end
end

View File

@@ -138,12 +138,7 @@ class TimelogController < ApplicationController
def details
sort_init 'spent_on', 'desc'
sort_update 'spent_on' => 'spent_on',
'user' => 'user_id',
'activity' => 'activity_id',
'project' => "#{Project.table_name}.name",
'issue' => 'issue_id',
'hours' => 'hours'
sort_update
cond = ARCondition.new
if @project.nil?

View File

@@ -40,10 +40,8 @@ class TrackersController < ApplicationController
end
flash[:notice] = l(:notice_successful_create)
redirect_to :action => 'list'
return
end
@trackers = Tracker.find :all, :order => 'position'
@projects = Project.find(:all)
end
def edit
@@ -51,9 +49,7 @@ class TrackersController < ApplicationController
if request.post? and @tracker.update_attributes(params[:tracker])
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'list'
return
end
@projects = Project.find(:all)
end
def move

View File

@@ -30,7 +30,7 @@ class UsersController < ApplicationController
def list
sort_init 'login', 'asc'
sort_update %w(login firstname lastname mail admin created_on last_login_on)
sort_update
@status = params[:status] ? params[:status].to_i : 1
c = ARCondition.new(@status == 0 ? "status <> 0" : ["status = ?", @status])
@@ -83,7 +83,7 @@ class UsersController < ApplicationController
end
@auth_sources = AuthSource.find(:all)
@roles = Role.find_all_givable
@projects = Project.active.find(:all, :order => 'lft')
@projects = Project.find(:all, :order => 'name', :conditions => "status=#{Project::STATUS_ACTIVE}") - @user.projects
@membership ||= Member.new
@memberships = @user.memberships
end

View File

@@ -37,6 +37,12 @@ class VersionsController < ApplicationController
redirect_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
end
def destroy_file
@version.attachments.find(params[:attachment_id]).destroy
flash[:notice] = l(:notice_successful_delete)
redirect_to :controller => 'projects', :action => 'list_files', :id => @project
end
def status_by
respond_to do |format|
format.html { render :action => 'show' }

View File

@@ -16,15 +16,9 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class WelcomeController < ApplicationController
caches_action :robots
def index
@news = News.latest User.current
@projects = Project.latest User.current
end
def robots
@projects = Project.public.active
render :layout => false, :content_type => 'text/plain'
end
end

View File

@@ -19,9 +19,8 @@ require 'diff'
class WikiController < ApplicationController
before_filter :find_wiki, :authorize
before_filter :find_existing_page, :only => [:rename, :protect, :history, :diff, :annotate, :add_attachment, :destroy]
verify :method => :post, :only => [:destroy, :protect], :redirect_to => { :action => :index }
verify :method => :post, :only => [:destroy, :destroy_attachment, :protect], :redirect_to => { :action => :index }
helper :attachments
include AttachmentsHelper
@@ -45,11 +44,11 @@ class WikiController < ApplicationController
return
end
@content = @page.content_for_version(params[:version])
if params[:format] == 'html'
if params[:export] == 'html'
export = render_to_string :action => 'export', :layout => false
send_data(export, :type => 'text/html', :filename => "#{@page.title}.html")
return
elsif params[:format] == 'txt'
elsif params[:export] == 'txt'
send_data(@content.text, :type => 'text/plain', :filename => "#{@page.title}.txt")
return
end
@@ -92,7 +91,8 @@ class WikiController < ApplicationController
# rename a page
def rename
return render_403 unless editable?
@page = @wiki.find_page(params[:page])
return render_403 unless editable?
@page.redirect_existing_links = true
# used to display the *original* title if some AR validation errors occur
@original_title = @page.pretty_title
@@ -103,12 +103,15 @@ class WikiController < ApplicationController
end
def protect
@page.update_attribute :protected, params[:protected]
redirect_to :action => 'index', :id => @project, :page => @page.title
page = @wiki.find_page(params[:page])
page.update_attribute :protected, params[:protected]
redirect_to :action => 'index', :id => @project, :page => page.title
end
# show page history
def history
@page = @wiki.find_page(params[:page])
@version_count = @page.content.versions.count
@version_pages = Paginator.new self, @version_count, per_page_option, params['p']
# don't load text
@@ -122,19 +125,21 @@ class WikiController < ApplicationController
end
def diff
@page = @wiki.find_page(params[:page])
@diff = @page.diff(params[:version], params[:version_from])
render_404 unless @diff
end
def annotate
@page = @wiki.find_page(params[:page])
@annotate = @page.annotate(params[:version])
render_404 unless @annotate
end
# remove a wiki page and its history
def destroy
return render_403 unless editable?
@page.destroy
@page = @wiki.find_page(params[:page])
return render_403 unless editable?
@page.destroy if @page
redirect_to :action => 'special', :id => @project, :page => 'Page_index'
end
@@ -176,11 +181,19 @@ class WikiController < ApplicationController
end
def add_attachment
@page = @wiki.find_page(params[:page])
return render_403 unless editable?
attach_files(@page, params[:attachments])
redirect_to :action => 'index', :page => @page.title
end
def destroy_attachment
@page = @wiki.find_page(params[:page])
return render_403 unless editable?
@page.attachments.find(params[:attachment_id]).destroy
redirect_to :action => 'index', :page => @page.title
end
private
def find_wiki
@@ -191,12 +204,6 @@ private
render_404
end
# Finds the requested page and returns a 404 error if it doesn't exist
def find_existing_page
@page = @wiki.find_page(params[:page])
render_404 if @page.nil?
end
# Returns true if the current user is allowed to edit the page, otherwise false
def editable?(page = @page)
page.editable_by?(User.current)

View File

@@ -20,12 +20,4 @@ module AdminHelper
options_for_select([[l(:label_all), ''],
[l(:status_active), 1]], selected)
end
def css_project_classes(project)
s = 'project'
s << ' root' if project.root?
s << ' child' if project.child?
s << (project.leaf? ? ' leaf' : ' parent')
s
end
end

View File

@@ -18,7 +18,6 @@
require 'coderay'
require 'coderay/helpers/file_type'
require 'forwardable'
require 'cgi'
module ApplicationHelper
include Redmine::WikiFormatting::Macros::Definitions
@@ -48,8 +47,8 @@ module ApplicationHelper
end
# Display a link to user's account page
def link_to_user(user, options={})
(user && !user.anonymous?) ? link_to(user.name(options[:format]), :controller => 'account', :action => 'show', :id => user) : 'Anonymous'
def link_to_user(user)
(user && !user.anonymous?) ? link_to(user, :controller => 'account', :action => 'show', :id => user) : 'Anonymous'
end
def link_to_issue(issue, options={})
@@ -116,7 +115,7 @@ module ApplicationHelper
end
def format_activity_description(text)
h(truncate(text.to_s, 120).gsub(%r{[\r\n]*<(pre|code)>.*$}m, '...')).gsub(/[\r\n]+/, "<br />")
h(truncate(text.to_s, 250).gsub(%r{<(pre|code)>.*$}m, '...'))
end
def distance_of_date_in_words(from_date, to_date = 0)
@@ -147,78 +146,6 @@ module ApplicationHelper
end
content
end
# Renders flash messages
def render_flash_messages
s = ''
flash.each do |k,v|
s << content_tag('div', v, :class => "flash #{k}")
end
s
end
# Renders the project quick-jump box
def render_project_jump_box
# Retrieve them now to avoid a COUNT query
projects = User.current.projects.all
if projects.any?
s = '<select onchange="if (this.value != \'\') { window.location = this.value; }">' +
"<option selected='selected'>#{ l(:label_jump_to_a_project) }</option>" +
'<option disabled="disabled">---</option>'
s << project_tree_options_for_select(projects) do |p|
{ :value => url_for(:controller => 'projects', :action => 'show', :id => p, :jump => current_menu_item) }
end
s << '</select>'
s
end
end
def project_tree_options_for_select(projects, options = {})
s = ''
project_tree(projects) do |project, level|
name_prefix = (level > 0 ? ('&nbsp;' * 2 * level + '&#187; ') : '')
tag_options = {:value => project.id, :selected => ((project == options[:selected]) ? 'selected' : nil)}
tag_options.merge!(yield(project)) if block_given?
s << content_tag('option', name_prefix + h(project), tag_options)
end
s
end
# Yields the given block for each project with its level in the tree
def project_tree(projects, &block)
ancestors = []
projects.sort_by(&:lft).each do |project|
while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
ancestors.pop
end
yield project, ancestors.size
ancestors << project
end
end
def project_nested_ul(projects, &block)
s = ''
if projects.any?
ancestors = []
projects.sort_by(&:lft).each do |project|
if (ancestors.empty? || project.is_descendant_of?(ancestors.last))
s << "<ul>\n"
else
ancestors.pop
s << "</li>"
while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
ancestors.pop
s << "</ul></li>\n"
end
end
s << "<li>"
s << yield(project).to_s
ancestors << project
end
s << ("</li></ul>\n" * ancestors.size)
end
s
end
# Truncates and returns the string as a single line
def truncate_single_line(string, *args)
@@ -263,40 +190,43 @@ module ApplicationHelper
def pagination_links_full(paginator, count=nil, options={})
page_param = options.delete(:page_param) || :page
url_param = params.dup
# don't reuse query params if filters are present
url_param.merge!(:fields => nil, :values => nil, :operators => nil) if url_param.delete(:set_filter)
# don't reuse params if filters are present
url_param.clear if url_param.has_key?(:set_filter)
html = ''
if paginator.current.previous
html << link_to_remote_content_update('&#171; ' + l(:label_previous), url_param.merge(page_param => paginator.current.previous)) + ' '
end
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_content_update(n.to_s, url_param.merge(page_param => n))
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 || '')
if paginator.current.next
html << ' ' + link_to_remote_content_update((l(:label_next) + ' &#187;'), url_param.merge(page_param => paginator.current.next))
end
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(' | ')
html << [" (#{paginator.current.first_item}-#{paginator.current.last_item}/#{count})", per_page_links(paginator.items_per_page)].compact.join(' | ')
end
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),
:method => :get},
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
@@ -306,12 +236,6 @@ module ApplicationHelper
elements = args.flatten
elements.any? ? content_tag('p', args.join(' &#187; ') + ' &#187; ', :class => 'breadcrumb') : nil
end
def other_formats_links(&block)
concat('<p class="other-formats">' + l(:label_export_to), block.binding)
yield Redmine::Views::OtherFormatsBuilder.new(self)
concat('</p>', block.binding)
end
def html_title(*args)
if args.empty?
@@ -357,15 +281,16 @@ module ApplicationHelper
attachments = attachments.sort_by(&:created_on).reverse
text = text.gsub(/!((\<|\=|\>)?(\([^\)]+\))?(\[[^\]]+\])?(\{[^\}]+\})?)(\S+\.(bmp|gif|jpg|jpeg|png))!/i) do |m|
style = $1
filename = $6.downcase
filename = $6
rf = Regexp.new(Regexp.escape(filename), Regexp::IGNORECASE)
# search for the picture in attachments
if found = attachments.detect { |att| att.filename.downcase == filename }
if found = attachments.detect { |att| att.filename =~ rf }
image_url = url_for :only_path => only_path, :controller => 'attachments', :action => 'download', :id => found
desc = found.description.to_s.gsub(/^([^\(\)]*).*$/, "\\1")
alt = desc.blank? ? nil : "(#{desc})"
"!#{style}#{image_url}#{alt}!"
else
m
"!#{style}#{filename}!"
end
end
end
@@ -418,7 +343,7 @@ module ApplicationHelper
:class => ('wiki-page' + (wiki_page ? '' : ' new')))
else
# project or wiki doesn't exist
all
title || page
end
else
all
@@ -600,8 +525,7 @@ module ApplicationHelper
def back_url_hidden_field_tag
back_url = params[:back_url] || request.env['HTTP_REFERER']
back_url = CGI.unescape(back_url.to_s)
hidden_field_tag('back_url', CGI.escape(back_url)) unless back_url.blank?
hidden_field_tag('back_url', back_url) unless back_url.blank?
end
def check_all_links(form_name)
@@ -690,12 +614,4 @@ module ApplicationHelper
extend helper
return self
end
def link_to_remote_content_update(text, url_params)
link_to_remote(text,
{:url => url_params, :method => :get, :update => 'content', :complete => 'window.scrollTo(0,0)'},
{:href => url_for(:params => url_params)}
)
end
end

View File

@@ -16,15 +16,10 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
module AttachmentsHelper
# Displays view/delete links to the attachments of the given object
# Options:
# :author -- author names are not displayed if set to false
def link_to_attachments(container, options = {})
options.assert_valid_keys(:author)
if container.attachments.any?
options = {:deletable => container.attachments_deletable?, :author => true}.merge(options)
render :partial => 'attachments/links', :locals => {:attachments => container.attachments, :options => options}
# displays the links to a collection of attachments
def link_to_attachments(attachments, options = {})
if attachments.any?
render :partial => 'attachments/links', :locals => {:attachments => attachments, :options => options}
end
end

View File

@@ -0,0 +1,85 @@
# redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require 'iconv'
require 'rfpdf/chinese'
module IfpdfHelper
class IFPDF < FPDF
include GLoc
attr_accessor :footer_date
def initialize(lang)
super()
set_language_if_valid lang
case current_language.to_s
when 'ja'
extend(PDF_Japanese)
AddSJISFont()
@font_for_content = 'SJIS'
@font_for_footer = 'SJIS'
when 'zh'
extend(PDF_Chinese)
AddGBFont()
@font_for_content = 'GB'
@font_for_footer = 'GB'
when 'zh-tw'
extend(PDF_Chinese)
AddBig5Font()
@font_for_content = 'Big5'
@font_for_footer = 'Big5'
else
@font_for_content = 'Arial'
@font_for_footer = 'Helvetica'
end
SetCreator(Redmine::Info.app_name)
SetFont(@font_for_content)
end
def SetFontStyle(style, size)
SetFont(@font_for_content, style, size)
end
def Cell(w,h=0,txt='',border=0,ln=0,align='',fill=0,link='')
@ic ||= Iconv.new(l(:general_pdf_encoding), 'UTF-8')
# these quotation marks are not correctly rendered in the pdf
txt = txt.gsub(/[“”]/, '"') if txt
txt = begin
# 0x5c char handling
txtar = txt.split('\\')
txtar << '' if txt[-1] == ?\\
txtar.collect {|x| @ic.iconv(x)}.join('\\').gsub(/\\/, "\\\\\\\\")
rescue
txt
end || ''
super w,h,txt,border,ln,align,fill,link
end
def Footer
SetFont(@font_for_footer, 'I', 8)
SetY(-15)
SetX(15)
Cell(0, 5, @footer_date, 0, 0, 'L')
SetY(-15)
SetX(-30)
Cell(0, 5, PageNo().to_s + '/{nb}', 0, 0, 'C')
end
end
end

View File

@@ -33,14 +33,6 @@ module IssuesHelper
"<strong>#{@cached_label_priority}</strong>: #{issue.priority.name}"
end
# Returns a string of css classes that apply to the given issue
def css_issue_classes(issue)
s = "issue status-#{issue.status.position} priority-#{issue.priority.position}"
s << ' closed' if issue.closed?
s << ' overdue' if issue.overdue?
s
end
def sidebar_queries
unless @sidebar_queries
# User can see public queries and his own queries

View File

@@ -33,39 +33,4 @@ module ProjectsHelper
]
tabs.select {|tab| User.current.allowed_to?(tab[:action], @project)}
end
def parent_project_select_tag(project)
options = '<option></option>' + project_tree_options_for_select(project.possible_parents, :selected => project.parent)
content_tag('select', options, :name => 'project[parent_id]')
end
# Renders a tree of projects as a nested set of unordered lists
# The given collection may be a subset of the whole project tree
# (eg. some intermediate nodes are private and can not be seen)
def render_project_hierarchy(projects)
s = ''
if projects.any?
ancestors = []
projects.each do |project|
if (ancestors.empty? || project.is_descendant_of?(ancestors.last))
s << "<ul class='projects #{ ancestors.empty? ? 'root' : nil}'>\n"
else
ancestors.pop
s << "</li>"
while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
ancestors.pop
s << "</ul></li>\n"
end
end
classes = (ancestors.empty? ? 'root' : 'child')
s << "<li class='#{classes}'><div class='#{classes}'>" +
link_to(h(project), {:controller => 'projects', :action => 'show', :id => project}, :class => "project #{User.current.member_of?(project) ? 'my-project' : nil}")
s << "<div class='wiki description'>#{textilizable(project.short_description, :project => project)}</div>" unless project.description.blank?
s << "</div>\n"
ancestors << project
end
s << ("</li></ul>\n" * ancestors.size)
end
s
end
end

View File

@@ -22,8 +22,8 @@ module QueriesHelper
end
def column_header(column)
column.sortable ? sort_header_tag(column.name.to_s, :caption => column.caption,
:default_order => column.default_order) :
column.sortable ? sort_header_tag(column.sortable, :caption => column.caption,
:default_order => column.default_order) :
content_tag('th', column.caption)
end

View File

@@ -152,8 +152,7 @@ module RepositoriesHelper
content_tag('p', form.password_field(:password, :size => 30, :name => 'ignore',
:value => ((repository.new_record? || repository.password.blank?) ? '' : ('x'*15)),
:onfocus => "this.value=''; this.name='repository[password]';",
:onchange => "this.name='repository[password]';")) +
content_tag('p', form.check_box(:cache))
:onchange => "this.name='repository[password]';"))
end
def darcs_field_tags(form, repository)

View File

@@ -44,7 +44,7 @@ module SearchHelper
def project_select_tag
options = [[l(:label_project_all), 'all']]
options << [l(:label_my_projects), 'my_projects'] unless User.current.memberships.empty?
options << [l(:label_and_its_subprojects, @project.name), 'subprojects'] unless @project.nil? || @project.descendants.active.empty?
options << [l(:label_and_its_subprojects, @project.name), 'subprojects'] unless @project.nil? || @project.active_children.empty?
options << [@project.name, ''] unless @project.nil?
select_tag('scope', options_for_select(options, params[:scope].to_s)) if options.size > 1
end

View File

@@ -18,7 +18,6 @@
module SettingsHelper
def administration_settings_tabs
tabs = [{:name => 'general', :partial => 'settings/general', :label => :label_general},
{:name => 'display', :partial => 'settings/display', :label => :label_display},
{: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},

View File

@@ -67,31 +67,23 @@ module SortHelper
# Updates the sort state. Call this in the controller prior to calling
# sort_clause.
# sort_keys can be either an array or a hash of allowed keys
def sort_update(sort_keys)
sort_key = params[:sort_key]
sort_key = nil unless (sort_keys.is_a?(Array) ? sort_keys.include?(sort_key) : sort_keys[sort_key])
sort_order = (params[:sort_order] == 'desc' ? 'DESC' : 'ASC')
if sort_key
sort = {:key => sort_key, :order => sort_order}
#
def sort_update()
if params[:sort_key]
sort = {:key => params[:sort_key], :order => params[:sort_order]}
elsif session[@sort_name]
sort = session[@sort_name] # Previous sort.
else
sort = @sort_default
end
session[@sort_name] = sort
sort_column = (sort_keys.is_a?(Hash) ? sort_keys[sort[:key]] : sort[:key])
@sort_clause = (sort_column.blank? ? nil : [sort_column].flatten.collect {|s| "#{s} #{sort[:order]}"}.join(','))
end
# Returns an SQL sort clause corresponding to the current sort state.
# Use this to sort the controller's table items collection.
#
def sort_clause()
@sort_clause
session[@sort_name][:key] + ' ' + (session[@sort_name][:order] || 'ASC')
end
# Returns a link which sorts by the named column.
@@ -120,11 +112,8 @@ module SortHelper
# don't reuse params if filters are present
url_options = params.has_key?(:set_filter) ? sort_options : params.merge(sort_options)
# Add project_id to url_options
url_options = url_options.merge(:project_id => params[:project_id]) if params.has_key?(:project_id)
link_to_remote(caption,
{:update => "content", :url => url_options, :method => :get},
{:update => "content", :url => url_options},
{:href => url_for(url_options)}) +
(icon ? nbsp(2) + image_tag(icon) : '')
end

View File

@@ -16,8 +16,6 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
module TimelogHelper
include ApplicationHelper
def render_timelog_breadcrumb
links = []
links << link_to(l(:label_project_all), {:project_id => nil, :issue_id => nil})
@@ -83,7 +81,7 @@ module TimelogHelper
csv << headers.collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end }
# csv lines
entries.each do |entry|
fields = [format_date(entry.spent_on),
fields = [l_date(entry.spent_on),
entry.user,
entry.activity,
entry.project,

View File

@@ -25,10 +25,15 @@ module UsersHelper
end
# Options for the new membership projects combo-box
def options_for_membership_project_select(user, projects)
def projects_options_for_select(projects)
options = content_tag('option', "--- #{l(:actionview_instancetag_blank_option)} ---")
options << project_tree_options_for_select(projects) do |p|
{:disabled => (user.projects.include?(p))}
projects_by_root = projects.group_by(&:root)
projects_by_root.keys.sort.each do |root|
options << content_tag('option', h(root.name), :value => root.id, :disabled => (!projects.include?(root)))
projects_by_root[root].sort.each do |project|
next if project == root
options << content_tag('option', '&#187; ' + h(project.name), :value => project.id)
end
end
options
end

View File

@@ -98,14 +98,6 @@ class Attachment < ActiveRecord::Base
container.project
end
def visible?(user=User.current)
container.attachments_visible?(user)
end
def deletable?(user=User.current)
container.attachments_deletable?(user)
end
def image?
self.filename =~ /\.(jpe?g|gif|png)$/i
end

View File

@@ -23,8 +23,8 @@ class Changeset < ActiveRecord::Base
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.short_comments.blank? ? '' : (': ' + o.short_comments))},
:description => :long_comments,
acts_as_event :title => Proc.new {|o| "#{l(:label_revision)} #{o.revision}" + (o.comments.blank? ? '' : (': ' + o.comments))},
:description => :comments,
:datetime => :committed_on,
:url => Proc.new {|o| {:controller => 'repositories', :action => 'revision', :id => o.repository.project_id, :rev => o.revision}}
@@ -122,14 +122,6 @@ class Changeset < ActiveRecord::Base
self.issues = referenced_issues.uniq
end
def short_comments
@short_comments || split_comments.first
end
def long_comments
@long_comments || split_comments.last
end
# Returns the previous changeset
def previous
@previous ||= Changeset.find(:first, :conditions => ['id < ? AND repository_id = ?', self.id, self.repository_id], :order => 'id DESC')
@@ -147,12 +139,6 @@ class Changeset < ActiveRecord::Base
private
def split_comments
comments =~ /\A(.+?)\r?\n(.*)$/m
@short_comments = $1 || comments
@long_comments = $2.to_s.strip
return @short_comments, @long_comments
end
def self.to_utf8(str)
return str if /\A[\r\n\t\x20-\x7e]*\Z/n.match(str) # for us-ascii

View File

@@ -41,6 +41,8 @@ class CustomField < ActiveRecord::Base
end
def before_validation
# remove empty values
self.possible_values = self.possible_values.collect{|v| v unless v.empty?}.compact
# make sure these fields are not searchable
self.searchable = false if %w(int float date bool).include?(field_format)
true
@@ -57,49 +59,11 @@ class CustomField < ActiveRecord::Base
v.custom_field.is_required = false
errors.add(:default_value, :activerecord_error_invalid) unless v.valid?
end
# Makes possible_values accept a multiline string
def possible_values=(arg)
if arg.is_a?(Array)
write_attribute(:possible_values, arg.compact.collect(&:strip).select {|v| !v.blank?})
else
self.possible_values = arg.to_s.split(/[\n\r]+/)
end
end
# Returns a ORDER BY clause that can used to sort customized
# objects by their value of the custom field.
# Returns false, if the custom field can not be used for sorting.
def order_statement
case field_format
when 'string', 'text', 'list', 'date', 'bool'
# COALESCE is here to make sure that blank and NULL values are sorted equally
"COALESCE((SELECT cv_sort.value FROM #{CustomValue.table_name} cv_sort" +
" WHERE cv_sort.customized_type='#{self.class.customized_class.name}'" +
" AND cv_sort.customized_id=#{self.class.customized_class.table_name}.id" +
" AND cv_sort.custom_field_id=#{id} LIMIT 1), '')"
when 'int', 'float'
# Make the database cast values into numeric
# Postgresql will raise an error if a value can not be casted!
# CustomValue validations should ensure that it doesn't occur
"(SELECT CAST(cv_sort.value AS decimal(60,3)) FROM #{CustomValue.table_name} cv_sort" +
" WHERE cv_sort.customized_type='#{self.class.customized_class.name}'" +
" AND cv_sort.customized_id=#{self.class.customized_class.table_name}.id" +
" AND cv_sort.custom_field_id=#{id} AND cv_sort.value <> '' AND cv_sort.value IS NOT NULL LIMIT 1)"
else
nil
end
end
def <=>(field)
position <=> field.position
end
def self.customized_class
self.name =~ /^(.+)CustomField$/
begin; $1.constantize; rescue nil; end
end
# to move in project_custom_field
def self.for_all
find(:all, :conditions => ["is_for_all=?", true], :order => 'position')

View File

@@ -30,18 +30,6 @@ class CustomValue < ActiveRecord::Base
self.value == '1'
end
def editable?
custom_field.editable?
end
def required?
custom_field.is_required?
end
def to_s
value.to_s
end
protected
def validate
if value.blank?

View File

@@ -18,7 +18,7 @@
class Document < ActiveRecord::Base
belongs_to :project
belongs_to :category, :class_name => "Enumeration", :foreign_key => "category_id"
acts_as_attachable :delete_permission => :manage_documents
has_many :attachments, :as => :container, :dependent => :destroy
acts_as_searchable :columns => ['title', "#{table_name}.description"], :include => :project
acts_as_event :title => Proc.new {|o| "#{l(:label_document)}: #{o.title}"},
@@ -28,10 +28,4 @@ class Document < ActiveRecord::Base
validates_presence_of :project, :title, :category
validates_length_of :title, :maximum => 60
def after_initialize
if new_record?
self.category ||= Enumeration.default('DCAT')
end
end
end

View File

@@ -44,9 +44,7 @@ class Enumeration < ActiveRecord::Base
end
def before_save
if is_default? && is_default_changed?
Enumeration.update_all("is_default = #{connection.quoted_false}", {:opt => opt})
end
Enumeration.update_all("is_default = #{connection.quoted_false}", {:opt => opt}) if is_default?
end
def objects_count

View File

@@ -26,13 +26,13 @@ class Issue < ActiveRecord::Base
belongs_to :category, :class_name => 'IssueCategory', :foreign_key => 'category_id'
has_many :journals, :as => :journalized, :dependent => :destroy
has_many :attachments, :as => :container, :dependent => :destroy
has_many :time_entries, :dependent => :delete_all
has_and_belongs_to_many :changesets, :order => "#{Changeset.table_name}.committed_on ASC, #{Changeset.table_name}.id ASC"
has_many :relations_from, :class_name => 'IssueRelation', :foreign_key => 'issue_from_id', :dependent => :delete_all
has_many :relations_to, :class_name => 'IssueRelation', :foreign_key => 'issue_to_id', :dependent => :delete_all
acts_as_attachable :after_remove => :attachment_removed
acts_as_customizable
acts_as_watchable
acts_as_searchable :columns => ['subject', "#{table_name}.description", "#{Journal.table_name}.notes"],
@@ -40,25 +40,16 @@ class Issue < ActiveRecord::Base
# sort by id so that limited eager loading doesn't break with postgresql
:order_column => "#{table_name}.id"
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}},
:type => Proc.new {|o| 'issue' + (o.closed? ? ' closed' : '') }
:url => Proc.new {|o| {:controller => 'issues', :action => 'show', :id => o.id}}
acts_as_activity_provider :find_options => {:include => [:project, :author, :tracker]},
:author_key => :author_id
validates_presence_of :subject, :priority, :project, :tracker, :author, :status
validates_presence_of :subject, :description, :priority, :project, :tracker, :author, :status
validates_length_of :subject, :maximum => 255
validates_inclusion_of :done_ratio, :in => 0..100
validates_numericality_of :estimated_hours, :allow_nil => true
named_scope :visible, lambda {|*args| { :include => :project,
:conditions => Project.allowed_to_condition(args.first || User.current, :view_issues) } }
# Returns true if usr or current user is allowed to view the issue
def visible?(usr=nil)
(usr || User.current).allowed_to?(:view_issues, self.project)
end
def after_initialize
if new_record?
# set default values for new records only
@@ -79,43 +70,34 @@ class Issue < ActiveRecord::Base
self
end
# Moves/copies an issue to a new project and tracker
# Returns the moved/copied issue on success, false on failure
def move_to(new_project, new_tracker = nil, options = {})
options ||= {}
issue = options[:copy] ? self.clone : self
# Move an issue to a new project and tracker
def move_to(new_project, new_tracker = nil)
transaction do
if new_project && issue.project_id != new_project.id
if new_project && project_id != new_project.id
# delete issue relations
unless Setting.cross_project_issue_relations?
issue.relations_from.clear
issue.relations_to.clear
self.relations_from.clear
self.relations_to.clear
end
# issue is moved to another project
# reassign to the category with same name if any
new_category = issue.category.nil? ? nil : new_project.issue_categories.find_by_name(issue.category.name)
issue.category = new_category
issue.fixed_version = nil
issue.project = new_project
new_category = category.nil? ? nil : new_project.issue_categories.find_by_name(category.name)
self.category = new_category
self.fixed_version = nil
self.project = new_project
end
if new_tracker
issue.tracker = new_tracker
self.tracker = new_tracker
end
if options[:copy]
issue.custom_field_values = self.custom_field_values.inject({}) {|h,v| h[v.custom_field_id] = v.value; h}
issue.status = self.status
end
if issue.save
unless options[:copy]
# Manually update project_id on related time entries
TimeEntry.update_all("project_id = #{new_project.id}", {:issue_id => id})
end
if save
# Manually update project_id on related time entries
TimeEntry.update_all("project_id = #{new_project.id}", {:issue_id => id})
else
Issue.connection.rollback_db_transaction
rollback_db_transaction
return false
end
end
return issue
return true
end
def priority_id=(pid)
@@ -213,11 +195,6 @@ class Issue < ActiveRecord::Base
self.status.is_closed?
end
# Returns true if the issue is overdue
def overdue?
!due_date.nil? && (due_date < Date.today) && !status.is_closed?
end
# Users the issue can be assigned to
def assignable_users
project.assignable_users
@@ -275,18 +252,13 @@ class Issue < ActiveRecord::Base
@soonest_start ||= relations_to.collect{|relation| relation.successor_soonest_start}.compact.min
end
def self.visible_by(usr)
with_scope(:find => { :conditions => Project.visible_by(usr) }) do
yield
end
end
def to_s
"#{tracker} ##{id}: #{subject}"
end
private
# Callback on attachment deletion
def attachment_removed(obj)
journal = init_journal(User.current)
journal.details << JournalDetail.new(:property => 'attachment',
:prop_key => obj.id,
:old_value => obj.filename)
journal.save
end
end

View File

@@ -35,8 +35,6 @@ class IssueRelation < ActiveRecord::Base
validates_numericality_of :delay, :allow_nil => true
validates_uniqueness_of :issue_to_id, :scope => :issue_from_id
attr_protected :issue_from_id, :issue_to_id
def validate
if issue_from && issue_to
errors.add :issue_to_id, :activerecord_error_invalid if issue_from_id == issue_to_id

View File

@@ -16,7 +16,6 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class MailHandler < ActionMailer::Base
include ActionView::Helpers::SanitizeHelper
class UnauthorizedAction < StandardError; end
class MissingInformation < StandardError; end
@@ -40,7 +39,7 @@ class MailHandler < ActionMailer::Base
# Processes incoming emails
def receive(email)
@email = email
@user = User.active.find_by_mail(email.from.first.to_s.strip)
@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
@@ -53,24 +52,11 @@ class MailHandler < ActionMailer::Base
private
MESSAGE_ID_RE = %r{^<redmine\.([a-z0-9_]+)\-(\d+)\.\d+@}
ISSUE_REPLY_SUBJECT_RE = %r{\[[^\]]+#(\d+)\]}
MESSAGE_REPLY_SUBJECT_RE = %r{\[[^\]]+msg(\d+)\]}
def dispatch
headers = [email.in_reply_to, email.references].flatten.compact
if headers.detect {|h| h.to_s =~ MESSAGE_ID_RE}
klass, object_id = $1, $2.to_i
method_name = "receive_#{klass}_reply"
if self.class.private_instance_methods.include?(method_name)
send method_name, object_id
else
# ignoring it
end
elsif m = email.subject.match(ISSUE_REPLY_SUBJECT_RE)
receive_issue_reply(m[1].to_i)
elsif m = email.subject.match(MESSAGE_REPLY_SUBJECT_RE)
receive_message_reply(m[1].to_i)
if m = email.subject.match(ISSUE_REPLY_SUBJECT_RE)
receive_issue_update(m[1].to_i)
else
receive_issue
end
@@ -102,21 +88,15 @@ class MailHandler < ActionMailer::Base
issue.status = status
end
issue.subject = email.subject.chomp.toutf8
issue.description = plain_text_body
# custom fields
issue.custom_field_values = issue.available_custom_fields.inject({}) do |h, c|
if value = get_keyword(c.name, :override => true)
h[c.id] = value
end
h
end
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)
# send notification after adding watchers so that they can reply to Redmine
Mailer.deliver_issue_add(issue) if Setting.notified_events.include?('issue_added')
issue
end
@@ -130,7 +110,7 @@ class MailHandler < ActionMailer::Base
end
# Adds a note to an existing issue
def receive_issue_reply(issue_id)
def receive_issue_update(issue_id)
status = (get_keyword(:status) && IssueStatus.find_by_name(get_keyword(:status)))
issue = Issue.find_by_id(issue_id)
@@ -140,7 +120,7 @@ class MailHandler < ActionMailer::Base
raise UnauthorizedAction unless status.nil? || user.allowed_to?(:edit_issues, issue.project)
# add the note
journal = issue.init_journal(user, plain_text_body)
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)
@@ -152,33 +132,6 @@ class MailHandler < ActionMailer::Base
journal
end
# Reply will be added to the issue
def receive_journal_reply(journal_id)
journal = Journal.find_by_id(journal_id)
if journal && journal.journalized_type == 'Issue'
receive_issue_reply(journal.journalized_id)
end
end
# Receives a reply to a forum message
def receive_message_reply(message_id)
message = Message.find_by_id(message_id)
if message
message = message.root
if user.allowed_to?(:add_messages, message.project) && !message.locked?
reply = Message.new(:subject => email.subject.gsub(%r{^.*msg\d+\]}, '').strip,
:content => plain_text_body)
reply.author = user
reply.board = message.board
message.children << reply
add_attachments(reply)
reply
else
raise UnauthorizedAction
end
end
end
def add_attachments(obj)
if email.has_attachments?
email.attachments.each do |attachment|
@@ -202,38 +155,22 @@ class MailHandler < ActionMailer::Base
end
end
def get_keyword(attr, options={})
@keywords ||= {}
if @keywords.has_key?(attr)
@keywords[attr]
else
@keywords[attr] = begin
if (options[:override] || @@handler_options[:allow_override].include?(attr.to_s)) && plain_text_body.gsub!(/^#{attr}:[ \t]*(.+)\s*$/i, '')
$1.strip
elsif !@@handler_options[:issue][attr].blank?
@@handler_options[:issue][attr]
end
end
def get_keyword(attr)
if @@handler_options[:allow_override].include?(attr.to_s) && email.plain_text_body =~ /^#{attr}:[ \t]*(.+)$/i
$1.strip
elsif !@@handler_options[:issue][attr].blank?
@@handler_options[:issue][attr]
end
end
# Returns the text/plain part of the email
# If not found (eg. HTML-only email), returns the body with tags removed
def plain_text_body
return @plain_text_body unless @plain_text_body.nil?
parts = @email.parts.collect {|c| (c.respond_to?(:parts) && !c.parts.empty?) ? c.parts : c}.flatten
if parts.empty?
parts << @email
end
plain_text_part = parts.detect {|p| p.content_type == 'text/plain'}
if plain_text_part.nil?
# no text/plain part found, assuming html-only email
# strip html tags and remove doctype directive
@plain_text_body = strip_tags(@email.body.to_s)
@plain_text_body.gsub! %r{^<!DOCTYPE .*$}, ''
else
@plain_text_body = plain_text_part.body.to_s
end
@plain_text_body.strip!
end
end
class TMail::Mail
# Returns body of the first plain text part found if any
def plain_text_body
return @plain_text_body unless @plain_text_body.nil?
p = self.parts.collect {|c| (c.respond_to?(:parts) && !c.parts.empty?) ? c.parts : c}.flatten
plain = p.detect {|c| c.content_type == 'text/plain'}
@plain_text_body = plain.nil? ? self.body : plain.body
end
end

View File

@@ -27,9 +27,7 @@ class Mailer < ActionMailer::Base
'Issue-Id' => issue.id,
'Issue-Author' => issue.author.login
redmine_headers 'Issue-Assignee' => issue.assigned_to.login if issue.assigned_to
message_id issue
recipients issue.recipients
cc(issue.watcher_recipients - @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)
@@ -41,9 +39,6 @@ class Mailer < ActionMailer::Base
'Issue-Id' => issue.id,
'Issue-Author' => issue.author.login
redmine_headers 'Issue-Assignee' => issue.assigned_to.login if issue.assigned_to
message_id journal
references issue
@author = journal.user
recipients issue.recipients
# Watchers in cc
cc(issue.watcher_recipients - @recipients)
@@ -62,7 +57,7 @@ class Mailer < ActionMailer::Base
subject l(:mail_subject_reminder, issues.size)
body :issues => issues,
:days => days,
:issues_url => url_for(:controller => 'issues', :action => 'index', :set_filter => 1, :assigned_to_id => user.id, :sort_key => 'due_date', :sort_order => 'asc')
: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)
@@ -78,9 +73,6 @@ class Mailer < ActionMailer::Base
added_to = ''
added_to_url = ''
case container.class.name
when 'Project'
added_to_url = url_for(:controller => 'projects', :action => 'list_files', :id => container)
added_to = "#{l(:label_project)}: #{container}"
when 'Version'
added_to_url = url_for(:controller => 'projects', :action => 'list_files', :id => container.project_id)
added_to = "#{l(:label_version)}: #{container.name}"
@@ -98,7 +90,6 @@ class Mailer < ActionMailer::Base
def news_added(news)
redmine_headers 'Project' => news.project.identifier
message_id news
recipients news.project.recipients
subject "[#{news.project.name}] #{l(:label_news)}: #{news.title}"
body :news => news,
@@ -108,10 +99,8 @@ class Mailer < ActionMailer::Base
def message_posted(message, recipients)
redmine_headers 'Project' => message.project.identifier,
'Topic-Id' => (message.parent_id || message.id)
message_id message
references message.parent unless message.parent.nil?
recipients(recipients)
subject "[#{message.board.project.name} - #{message.board.name} - msg#{message.root.id}] #{message.subject}"
subject "[#{message.board.project.name} - #{message.board.name}] #{message.subject}"
body :message => message,
:message_url => url_for(:controller => 'messages', :action => 'show', :board_id => message.board_id, :id => message.root)
end
@@ -162,15 +151,7 @@ class Mailer < ActionMailer::Base
return false if (recipients.nil? || recipients.empty?) &&
(cc.nil? || cc.empty?) &&
(bcc.nil? || bcc.empty?)
# Set Message-Id and References
if @message_id_object
mail.message_id = self.class.message_id_for(@message_id_object)
end
if @references_objects
mail.references = @references_objects.collect {|o| self.class.message_id_for(o)}
end
super(mail)
super
end
# Sends reminders to issue assignees
@@ -205,7 +186,7 @@ class Mailer < ActionMailer::Base
# URL options
h = Setting.host_name
h = h.to_s.gsub(%r{\/.*$}, '') unless Redmine::Utils.relative_url_root.blank?
h = h.to_s.gsub(%r{\/.*$}, '') unless ActionController::AbstractRequest.relative_url_root.blank?
default_url_options[:host] = h
default_url_options[:protocol] = Setting.protocol
@@ -224,10 +205,9 @@ class Mailer < ActionMailer::Base
def create_mail
# Removes the current user from the recipients and cc
# if he doesn't want to receive notifications about what he does
@author ||= User.current
if @author.pref[:no_self_notified]
recipients.delete(@author.mail) if recipients
cc.delete(@author.mail) if cc
if User.current.pref[:no_self_notified]
recipients.delete(User.current.mail) if recipients
cc.delete(User.current.mail) if cc
end
# Blind carbon copy recipients
if Setting.bcc_recipients?
@@ -240,7 +220,7 @@ class Mailer < ActionMailer::Base
# Renders a message with the corresponding layout
def render_message(method_name, body)
layout = method_name.to_s.match(%r{text\.html\.(rhtml|rxml)}) ? 'layout.text.html.rhtml' : 'layout.text.plain.rhtml'
layout = method_name.match(%r{text\.html\.(rhtml|rxml)}) ? 'layout.text.html.rhtml' : 'layout.text.plain.rhtml'
body[:content_for_layout] = render(:file => method_name, :body => body)
ActionView::Base.new(template_root, body, self).render(:file => "mailer/#{layout}", :use_full_path => true)
end
@@ -264,34 +244,4 @@ class Mailer < ActionMailer::Base
def self.controller_path
''
end unless respond_to?('controller_path')
# Returns a predictable Message-Id for the given object
def self.message_id_for(object)
# id + timestamp should reduce the odds of a collision
# as far as we don't send multiple emails for the same object
hash = "redmine.#{object.class.name.demodulize.underscore}-#{object.id}.#{object.created_on.strftime("%Y%m%d%H%M%S")}"
host = Setting.mail_from.to_s.gsub(%r{^.*@}, '')
host = "#{::Socket.gethostname}.redmine" if host.empty?
"<#{hash}@#{host}>"
end
private
def message_id(object)
@message_id_object = object
end
def references(object)
@references_objects ||= []
@references_objects << object
end
end
# Patch TMail so that message_id is not overwritten
module TMail
class Mail
def add_message_id( fqdn = nil )
self.message_id ||= ::TMail::new_message_id(fqdn)
end
end
end

View File

@@ -19,11 +19,11 @@ class Message < ActiveRecord::Base
belongs_to :board
belongs_to :author, :class_name => 'User', :foreign_key => 'author_id'
acts_as_tree :counter_cache => :replies_count, :order => "#{Message.table_name}.created_on ASC"
acts_as_attachable
has_many :attachments, :as => :container, :dependent => :destroy
belongs_to :last_reply, :class_name => 'Message', :foreign_key => 'last_reply_id'
acts_as_searchable :columns => ['subject', 'content'],
:include => {:board => :project},
:include => {:board, :project},
:project_key => 'project_id',
:date_column => "#{table_name}.created_on"
acts_as_event :title => Proc.new {|o| "#{o.board.name}: #{o.subject}"},

View File

@@ -43,9 +43,7 @@ class Project < ActiveRecord::Base
:join_table => "#{table_name_prefix}custom_fields_projects#{table_name_suffix}",
:association_foreign_key => 'custom_field_id'
acts_as_nested_set :order => 'name', :dependent => :destroy
acts_as_attachable :view_permission => :view_files,
:delete_permission => :manage_files
acts_as_tree :order => "name", :counter_cache => true
acts_as_customizable
acts_as_searchable :columns => ['name', 'description'], :project_key => 'id', :permission => nil
@@ -60,15 +58,12 @@ class Project < ActiveRecord::Base
validates_associated :repository, :wiki
validates_length_of :name, :maximum => 30
validates_length_of :homepage, :maximum => 255
validates_length_of :identifier, :in => 2..20
validates_length_of :identifier, :in => 3..20
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] } }
named_scope :active, { :conditions => "#{Project.table_name}.status = #{STATUS_ACTIVE}"}
named_scope :public, { :conditions => { :is_public => true } }
named_scope :visible, lambda { { :conditions => Project.visible_by(User.current) } }
def identifier=(identifier)
super unless identifier_frozen?
@@ -81,7 +76,7 @@ class Project < ActiveRecord::Base
def issues_with_subprojects(include_subprojects=false)
conditions = nil
if include_subprojects
ids = [id] + descendants.collect(&:id)
ids = [id] + child_ids
conditions = ["#{Project.table_name}.id IN (#{ids.join(',')}) AND #{Project.visible_by}"]
end
conditions ||= ["#{Project.table_name}.id = ?", id]
@@ -121,7 +116,7 @@ class Project < ActiveRecord::Base
end
if options[:project]
project_statement = "#{Project.table_name}.id = #{options[:project].id}"
project_statement << " OR (#{Project.table_name}.lft > #{options[:project].lft} AND #{Project.table_name}.rgt < #{options[:project].rgt})" if options[:with_subprojects]
project_statement << " OR #{Project.table_name}.parent_id = #{options[:project].id}" if options[:with_subprojects]
base_statement = "(#{project_statement}) AND (#{base_statement})"
end
if user.admin?
@@ -144,7 +139,7 @@ class Project < ActiveRecord::Base
def project_condition(with_subprojects)
cond = "#{Project.table_name}.id = #{id}"
cond = "(#{cond} OR (#{Project.table_name}.lft > #{lft} AND #{Project.table_name}.rgt < #{rgt}))" if with_subprojects
cond = "(#{cond} OR #{Project.table_name}.parent_id = #{id})" if with_subprojects
cond
end
@@ -167,7 +162,6 @@ class Project < ActiveRecord::Base
self.status == STATUS_ACTIVE
end
# Archives the project and its descendants recursively
def archive
# Archive subprojects if any
children.each do |subproject|
@@ -176,62 +170,21 @@ class Project < ActiveRecord::Base
update_attribute :status, STATUS_ARCHIVED
end
# Unarchives the project
# All its ancestors must be active
def unarchive
return false if ancestors.detect {|a| !a.active?}
return false if parent && !parent.active?
update_attribute :status, STATUS_ACTIVE
end
# Returns an array of projects the project can be moved to
def possible_parents
@possible_parents ||= (Project.active.find(:all) - self_and_descendants)
def active_children
children.select {|child| child.active?}
end
# Sets the parent of the project
# Argument can be either a Project, a String, a Fixnum or nil
def set_parent!(p)
unless p.nil? || p.is_a?(Project)
if p.to_s.blank?
p = nil
else
p = Project.find_by_id(p)
return false unless p
end
end
if p == parent && !p.nil?
# Nothing to do
true
elsif p.nil? || (p.active? && move_possible?(p))
# Insert the project so that target's children or root projects stay alphabetically sorted
sibs = (p.nil? ? self.class.roots : p.children)
to_be_inserted_before = sibs.detect {|c| c.name.to_s.downcase > name.to_s.downcase }
if to_be_inserted_before
move_to_left_of(to_be_inserted_before)
elsif p.nil?
if sibs.empty?
# move_to_root adds the project in first (ie. left) position
move_to_root
else
move_to_right_of(sibs.last) unless self == sibs.last
end
else
# move_to_child_of adds the project in last (ie.right) position
move_to_child_of(p)
end
true
else
# Can not move to the given target
false
end
end
# Returns an array of the trackers used by the project and its active sub projects
# Returns an array of the trackers used by the project and its sub projects
def rolled_up_trackers
@rolled_up_trackers ||=
Tracker.find(:all, :include => :projects,
:select => "DISTINCT #{Tracker.table_name}.*",
:conditions => ["#{Project.table_name}.lft >= ? AND #{Project.table_name}.rgt <= ? AND #{Project.table_name}.status = #{STATUS_ACTIVE}", lft, rgt],
:conditions => ["#{Project.table_name}.id = ? OR #{Project.table_name}.parent_id = ?", id, id],
:order => "#{Tracker.table_name}.position")
end
@@ -270,7 +223,7 @@ class Project < ActiveRecord::Base
# Returns a short description of the projects (first lines)
def short_description(length = 255)
description.gsub(/^(.{#{length}}[^\n\r]*).*$/m, '\1...').strip if description
description.gsub(/^(.{#{length}}[^\n]*).*$/m, '\1').strip if description
end
def allows_to?(action)
@@ -302,6 +255,8 @@ class Project < ActiveRecord::Base
protected
def validate
errors.add(parent_id, " must be a root project") if parent and parent.parent
errors.add_to_base("A project with subprojects can't be a subproject") if parent and children.size > 0
errors.add(:identifier, :activerecord_error_invalid) if !identifier.blank? && identifier.match(/^\d*$/)
end

View File

@@ -35,7 +35,7 @@ class QueryCustomFieldColumn < QueryColumn
def initialize(custom_field)
self.name = "cf_#{custom_field.id}".to_sym
self.sortable = custom_field.order_statement || false
self.sortable = false
@cf = custom_field
end
@@ -98,10 +98,10 @@ class Query < ActiveRecord::Base
QueryColumn.new(:priority, :sortable => "#{Enumeration.table_name}.position", :default_order => 'desc'),
QueryColumn.new(:subject, :sortable => "#{Issue.table_name}.subject"),
QueryColumn.new(:author),
QueryColumn.new(:assigned_to, :sortable => ["#{User.table_name}.lastname", "#{User.table_name}.firstname"]),
QueryColumn.new(:assigned_to, :sortable => "#{User.table_name}.lastname"),
QueryColumn.new(:updated_on, :sortable => "#{Issue.table_name}.updated_on", :default_order => 'desc'),
QueryColumn.new(:category, :sortable => "#{IssueCategory.table_name}.name"),
QueryColumn.new(:fixed_version, :sortable => ["#{Version.table_name}.effective_date", "#{Version.table_name}.name"], :default_order => 'desc'),
QueryColumn.new(:fixed_version, :sortable => "#{Version.table_name}.effective_date", :default_order => 'desc'),
QueryColumn.new(:start_date, :sortable => "#{Issue.table_name}.start_date"),
QueryColumn.new(:due_date, :sortable => "#{Issue.table_name}.due_date"),
QueryColumn.new(:estimated_hours, :sortable => "#{Issue.table_name}.estimated_hours"),
@@ -165,10 +165,6 @@ class Query < ActiveRecord::Base
end
@available_filters["assigned_to_id"] = { :type => :list_optional, :order => 4, :values => user_values } unless user_values.empty?
@available_filters["author_id"] = { :type => :list, :order => 5, :values => user_values } unless user_values.empty?
if User.current.logged?
@available_filters["watcher_id"] = { :type => :list, :order => 15, :values => [["<< #{l(:label_me)} >>", "me"]] }
end
if project
# project specific filters
@@ -178,8 +174,8 @@ class Query < ActiveRecord::Base
unless @project.versions.empty?
@available_filters["fixed_version_id"] = { :type => :list_optional, :order => 7, :values => @project.versions.sort.collect{|s| [s.name, s.id.to_s] } }
end
unless @project.descendants.active.empty?
@available_filters["subproject_id"] = { :type => :list_subprojects, :order => 13, :values => @project.descendants.visible.collect{|s| [s.name, s.id.to_s] } }
unless @project.active_children.empty?
@available_filters["subproject_id"] = { :type => :list_subprojects, :order => 13, :values => @project.active_children.collect{|s| [s.name, s.id.to_s] } }
end
add_custom_fields_filters(@project.all_issue_custom_fields)
else
@@ -261,7 +257,7 @@ class Query < ActiveRecord::Base
def project_statement
project_clauses = []
if project && !@project.descendants.active.empty?
if project && !@project.active_children.empty?
ids = [project.id]
if has_filter?("subproject_id")
case operator_for("subproject_id")
@@ -272,10 +268,10 @@ class Query < ActiveRecord::Base
# main project only
else
# all subprojects
ids += project.descendants.collect(&:id)
ids += project.child_ids
end
elsif Setting.display_subprojects_issues?
ids += project.descendants.collect(&:id)
ids += project.child_ids
end
project_clauses << "#{Project.table_name}.id IN (%s)" % ids.join(',')
elsif project
@@ -292,34 +288,31 @@ class Query < ActiveRecord::Base
next if field == "subproject_id"
v = values_for(field).clone
next unless v and !v.empty?
operator = operator_for(field)
# "me" value subsitution
if %w(assigned_to_id author_id watcher_id).include?(field)
v.push(User.current.logged? ? User.current.id.to_s : "0") if v.delete("me")
end
sql = ''
is_custom_filter = false
if field =~ /^cf_(\d+)$/
# custom field
db_table = CustomValue.table_name
db_field = 'value'
is_custom_filter = true
sql << "#{Issue.table_name}.id IN (SELECT #{Issue.table_name}.id FROM #{Issue.table_name} LEFT OUTER JOIN #{db_table} ON #{db_table}.customized_type='Issue' AND #{db_table}.customized_id=#{Issue.table_name}.id AND #{db_table}.custom_field_id=#{$1} WHERE "
sql << sql_for_field(field, operator, v, db_table, db_field, true) + ')'
elsif field == 'watcher_id'
db_table = Watcher.table_name
db_field = 'user_id'
sql << "#{Issue.table_name}.id #{ operator == '=' ? 'IN' : 'NOT IN' } (SELECT #{db_table}.watchable_id FROM #{db_table} WHERE #{db_table}.watchable_type='Issue' AND "
sql << sql_for_field(field, '=', v, db_table, db_field) + ')'
else
# regular field
db_table = Issue.table_name
db_field = field
sql << '(' + sql_for_field(field, operator, v, db_table, db_field) + ')'
sql << '('
end
filters_clauses << sql
# "me" value subsitution
if %w(assigned_to_id author_id).include?(field)
v.push(User.current.logged? ? User.current.id.to_s : "0") if v.delete("me")
end
sql = sql + sql_for_field(field, v, db_table, db_field, is_custom_filter)
sql << ')'
filters_clauses << sql
end if filters and valid?
(filters_clauses << project_statement).join(' AND ')
@@ -327,10 +320,10 @@ class Query < ActiveRecord::Base
private
# Helper method to generate the WHERE sql for a +field+, +operator+ and a +value+
def sql_for_field(field, operator, value, db_table, db_field, is_custom_filter=false)
# 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
case operator_for field
when "="
sql = "#{db_table}.#{db_field} IN (" + value.collect{|val| "'#{connection.quote_string(val)}'"}.join(",") + ")"
when "!"

View File

@@ -1,4 +1,3 @@
# redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang
#
@@ -20,14 +19,14 @@ class Repository < ActiveRecord::Base
belongs_to :project
has_many :changesets, :order => "#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC"
has_many :changes, :through => :changesets
# Raw SQL to delete changesets and changes in the database
# has_many :changesets, :dependent => :destroy is too slow for big repositories
before_destroy :clear_changesets
# Checks if the SCM is enabled when creating a repository
validate_on_create { |r| r.errors.add(:type, :activerecord_error_invalid) unless Setting.enabled_scm.include?(r.class.name.demodulize) }
# Removes leading and trailing whitespace
def url=(arg)
write_attribute(:url, arg ? arg.to_s.strip : nil)
@@ -39,8 +38,7 @@ class Repository < ActiveRecord::Base
end
def scm
init_cache if cache_path.blank? and respond_to?(:init_cache)
@scm ||= self.scm_adapter.new(url, root_url, login, password, cache_path)
@scm ||= self.scm_adapter.new url, root_url, login, password
update_attribute(:root_url, @scm.root_url) if root_url.blank?
@scm
end
@@ -78,12 +76,11 @@ class Repository < ActiveRecord::Base
end
# Default behaviour: we search in cached changesets
def changesets_for_path(path, options={})
def changesets_for_path(path)
path = "/#{path}" unless path.starts_with?('/')
Change.find(:all, :include => {:changeset => :user},
:conditions => ["repository_id = ? AND path = ?", id, path],
:order => "committed_on DESC, #{Changeset.table_name}.id DESC",
:limit => options[:limit]).collect(&:changeset)
:conditions => ["repository_id = ? AND path = ?", id, path],
:order => "committed_on DESC, #{Changeset.table_name}.id DESC").collect(&:changeset)
end
# Returns a path relative to the url of the repository
@@ -164,31 +161,9 @@ class Repository < ActiveRecord::Base
rescue
nil
end
def remove_cache
scm.remove_cache if cache
end
def create_or_sync_cache
begin
scm.create_cache
rescue => e
# clean if problem in creation
scm.remove_cache
end
scm.synchronize
end
private
def repositories_cache_directory
unless @cache_directory
@cache_directory = Setting.repositories_cache_directory.gsub(/^([^#{File::SEPARATOR}].*)/, RAILS_ROOT + '/\1/')
Dir.mkdir(@cache_directory, File.umask(0077)) unless File.directory?(@cache_directory)
end
@cache_directory
end
def before_save
# Strips url and root_url
url.strip!

View File

@@ -21,17 +21,6 @@ class Repository::Git < Repository
attr_protected :root_url
validates_presence_of :url
before_destroy :remove_cache
def init_cache
return unless dir = repositories_cache_directory
# we need to use a cache only if repository isn't local and dir exists
if url[/^(rsync|https?|git|ssh):\/\//]
update_attribute(:cache_path, dir + project.identifier)
update_attribute(:cache, true)
end
end
def scm_adapter
Redmine::Scm::Adapters::GitAdapter
end
@@ -40,16 +29,13 @@ class Repository::Git < Repository
'Git'
end
def changesets_for_path(path, options={})
def changesets_for_path(path)
Change.find(:all, :include => {:changeset => :user},
:conditions => ["repository_id = ? AND path = ?", id, path],
:order => "committed_on DESC, #{Changeset.table_name}.revision DESC",
:limit => options[:limit]).collect(&:changeset)
:order => "committed_on DESC, #{Changeset.table_name}.revision DESC").collect(&:changeset)
end
def fetch_changesets
create_or_sync_cache if cache
scm_info = scm.info
if scm_info
# latest revision found in database

View File

@@ -21,15 +21,6 @@ class Repository::Mercurial < Repository
attr_protected :root_url
validates_presence_of :url
def init_cache
return unless dir = repositories_cache_directory
# we need to use a cache only if repository isn't local and dir exists
if url[/^(|https?|ssh):\/\//]
update_attribute(:cache_path, dir + project.identifier)
update_attribute(:cache, true)
end
end
def scm_adapter
Redmine::Scm::Adapters::MercurialAdapter
end
@@ -62,8 +53,6 @@ class Repository::Mercurial < Repository
end
def fetch_changesets
create_or_sync_cache if cache
scm_info = scm.info
if scm_info
# latest revision found in database

View File

@@ -22,16 +22,6 @@ class Repository::Subversion < Repository
validates_presence_of :url
validates_format_of :url, :with => /^(http|https|svn|svn\+ssh|file):\/\/.+/i
before_destroy :remove_cache
def init_cache
return unless dir = repositories_cache_directory
# we need to use a cache only if repository isn't local and dir exists
if cache and url[/^(svn|https?|svn\+ssh):\/\//]
update_attribute(:cache_path, dir + project.identifier)
end
end
def scm_adapter
Redmine::Scm::Adapters::SubversionAdapter
end
@@ -40,8 +30,8 @@ class Repository::Subversion < Repository
'Subversion'
end
def changesets_for_path(path, options={})
revisions = scm.revisions(path, nil, nil, :limit => options[:limit])
def changesets_for_path(path)
revisions = scm.revisions(path)
revisions ? changesets.find_all_by_revision(revisions.collect(&:identifier), :order => "committed_on DESC", :include => :user) : []
end
@@ -51,8 +41,6 @@ class Repository::Subversion < Repository
end
def fetch_changesets
create_or_sync_cache if cache
scm_info = scm.info
if scm_info
# latest revision found in database

View File

@@ -31,9 +31,9 @@ class Role < ActiveRecord::Base
raise "Can not copy workflow from a #{role.class}" unless role.is_a?(Role)
raise "Can not copy workflow from/to an unsaved role" if proxy_owner.new_record? || role.new_record?
clear
connection.insert "INSERT INTO #{Workflow.table_name} (tracker_id, old_status_id, new_status_id, role_id)" +
connection.insert "INSERT INTO workflows (tracker_id, old_status_id, new_status_id, role_id)" +
" SELECT tracker_id, old_status_id, new_status_id, #{proxy_owner.id}" +
" FROM #{Workflow.table_name}" +
" FROM workflows" +
" WHERE role_id = #{role.id}"
end
end

View File

@@ -140,10 +140,6 @@ class Setting < ActiveRecord::Base
per_page_options.split(%r{[\s,]}).collect(&:to_i).select {|n| n > 0}.sort
end
def self.openid?
Object.const_defined?(:OpenID) && self['openid'].to_s == '1'
end
# Checks if settings have changed since the values were read
# and clears the cache hash if it's the case
# Called once per request
@@ -165,14 +161,4 @@ private
setting = find_by_name(name)
setting ||= new(:name => name, :value => @@available_settings[name]['default']) if @@available_settings.has_key? name
end
protected
def validate
if self.name.to_s == "repositories_cache_directory" and not File.directory?(self.value.to_s)
logger.error("Le repertoire #{self.value.to_s} n'existe pas")
errors.add("Le repertoire #{self.value.to_s} n'existe pas")
end
end
end

View File

@@ -32,7 +32,7 @@ class TimeEntry < ActiveRecord::Base
:description => :comments
validates_presence_of :user_id, :activity_id, :project_id, :hours, :spent_on
validates_numericality_of :hours, :allow_nil => true, :message => :activerecord_error_invalid
validates_numericality_of :hours, :allow_nil => true
validates_length_of :comments, :maximum => 255, :allow_nil => true
def after_initialize
@@ -54,7 +54,7 @@ class TimeEntry < ActiveRecord::Base
end
def hours=(h)
write_attribute :hours, (h.is_a?(String) ? (h.to_hours || h) : h)
write_attribute :hours, (h.is_a?(String) ? h.to_hours : h)
end
# tyear, tmonth, tweek assigned where setting spent_on attributes

View File

@@ -23,9 +23,9 @@ class Tracker < ActiveRecord::Base
raise "Can not copy workflow from a #{tracker.class}" unless tracker.is_a?(Tracker)
raise "Can not copy workflow from/to an unsaved tracker" if proxy_owner.new_record? || tracker.new_record?
clear
connection.insert "INSERT INTO #{Workflow.table_name} (tracker_id, old_status_id, new_status_id, role_id)" +
connection.insert "INSERT INTO workflows (tracker_id, old_status_id, new_status_id, role_id)" +
" SELECT #{proxy_owner.id}, old_status_id, new_status_id, role_id" +
" FROM #{Workflow.table_name}" +
" FROM workflows" +
" WHERE tracker_id = #{tracker.id}"
end
end

View File

@@ -54,7 +54,7 @@ class User < ActiveRecord::Base
validates_presence_of :login, :firstname, :lastname, :mail, :if => Proc.new { |user| !user.is_a?(AnonymousUser) }
validates_uniqueness_of :login, :if => Proc.new { |user| !user.login.blank? }
validates_uniqueness_of :mail, :if => Proc.new { |user| !user.mail.blank? }, :case_sensitive => false
validates_uniqueness_of :mail, :if => Proc.new { |user| !user.mail.blank? }
# Login must contain lettres, numbers, underscores only
validates_format_of :login, :with => /^[a-z0-9_\-@\.]*$/i
validates_length_of :login, :maximum => 30
@@ -80,15 +80,6 @@ class User < ActiveRecord::Base
super
end
def identity_url=(url)
begin
self.write_attribute(:identity_url, OpenIdAuthentication.normalize_identifier(url))
rescue InvalidOpenId
# Invlaid url, don't save
end
self.read_attribute(:identity_url)
end
# Returns the user that matches provided login and password, or nil
def self.try_to_login(login, password)
# Make sure no one can sign in with an empty password
@@ -147,25 +138,13 @@ class User < ActiveRecord::Base
def check_password?(clear_password)
User.hash_password(clear_password) == self.hashed_password
end
# Generate and set a random password. Useful for automated user creation
# Based on Token#generate_token_value
#
def random_password
chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
password = ''
40.times { |i| password << chars[rand(chars.size-1)] }
self.password = password
self.password_confirmation = password
self
end
def pref
self.preference ||= UserPreference.new(:user => self)
end
def time_zone
@time_zone ||= (self.pref.time_zone.blank? ? nil : ActiveSupport::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?
@@ -199,11 +178,6 @@ class User < ActiveRecord::Base
token = Token.find_by_action_and_value('autologin', key)
token && (token.created_on > Setting.autologin.to_i.day.ago) && token.user.active? ? token.user : nil
end
# Makes find_by_mail case-insensitive
def self.find_by_mail(mail)
find(:first, :conditions => ["LOWER(mail) = ?", mail.to_s.downcase])
end
# Sort users by their display names
def <=>(user)

View File

@@ -19,8 +19,7 @@ class Version < ActiveRecord::Base
before_destroy :check_integrity
belongs_to :project
has_many :fixed_issues, :class_name => 'Issue', :foreign_key => 'fixed_version_id'
acts_as_attachable :view_permission => :view_files,
:delete_permission => :manage_files
has_many :attachments, :as => :container, :dependent => :destroy
validates_presence_of :name
validates_uniqueness_of :name, :scope => [:project_id]
@@ -51,20 +50,20 @@ class Version < ActiveRecord::Base
end
def completed_pourcent
if issues_count == 0
if fixed_issues.count == 0
0
elsif open_issues_count == 0
100
else
issues_progress(false) + issues_progress(true)
(closed_issues_count * 100 + Issue.sum('done_ratio', :include => 'status', :conditions => ["fixed_version_id = ? AND is_closed = ?", id, false]).to_f) / fixed_issues.count
end
end
def closed_pourcent
if issues_count == 0
if fixed_issues.count == 0
0
else
issues_progress(false)
closed_issues_count * 100.0 / fixed_issues.count
end
end
@@ -73,11 +72,6 @@ class Version < ActiveRecord::Base
effective_date && (effective_date < Date.today) && (open_issues_count > 0)
end
# Returns assigned issues count
def issues_count
@issue_count ||= fixed_issues.count
end
def open_issues_count
@open_issues_count ||= Issue.count(:all, :conditions => ["fixed_version_id = ? AND is_closed = ?", self.id, false], :include => :status)
end
@@ -109,35 +103,4 @@ private
def check_integrity
raise "Can't delete version" if self.fixed_issues.find(:first)
end
# Returns the average estimated time of assigned issues
# or 1 if no issue has an estimated time
# Used to weigth unestimated issues in progress calculation
def estimated_average
if @estimated_average.nil?
average = fixed_issues.average(:estimated_hours).to_f
if average == 0
average = 1
end
@estimated_average = average
end
@estimated_average
end
# Returns the total progress of open or closed issues
def issues_progress(open)
@issues_progress ||= {}
@issues_progress[open] ||= begin
progress = 0
if issues_count > 0
ratio = open ? 'done_ratio' : 100
done = fixed_issues.sum("COALESCE(estimated_hours, #{estimated_average}) * #{ratio}",
:include => :status,
:conditions => ["is_closed = ?", !open]).to_f
progress = done / (estimated_average * issues_count)
end
progress
end
end
end

View File

@@ -21,7 +21,7 @@ require 'enumerator'
class WikiPage < ActiveRecord::Base
belongs_to :wiki
has_one :content, :class_name => 'WikiContent', :foreign_key => 'page_id', :dependent => :destroy
acts_as_attachable :delete_permission => :delete_wiki_pages_attachments
has_many :attachments, :as => :container, :dependent => :destroy
acts_as_tree :order => 'title'
acts_as_event :title => Proc.new {|o| "#{l(:label_wiki)}: #{o.title}"},
@@ -111,10 +111,6 @@ class WikiPage < ActiveRecord::Base
def editable_by?(usr)
!protected? || usr.allowed_to?(:protect_wiki_pages, wiki.project)
end
def attachments_deletable?(usr=User.current)
editable_by?(usr) && super(usr)
end
def parent_title
@parent_title || (self.parent && self.parent.pretty_title)

View File

@@ -10,12 +10,6 @@
<td align="right"><label for="password"><%=l(:field_password)%>:</label></td>
<td align="left"><%= password_field_tag 'password', nil, :size => 40 %></td>
</tr>
<% if Setting.openid? %>
<tr>
<td align="right"><label for="openid_url"><%=l(:field_identity_url)%></label></td>
<td align="left"><%= text_field_tag "openid_url" %></td>
</tr>
<% end %>
<tr>
<td></td>
<td align="left">

View File

@@ -1,4 +1,4 @@
<h2><%=l(:label_register)%> <%=link_to l(:label_login_with_open_id_option), signin_url if Setting.openid? %></h2>
<h2><%=l(:label_register)%></h2>
<% form_tag({:action => 'register'}, :class => "tabular") do %>
<%= error_messages_for 'user' %>
@@ -29,12 +29,7 @@
<p><label for="user_language"><%=l(:field_language)%></label>
<%= select("user", "language", lang_options_for_select) %></p>
<% if Setting.openid? %>
<p><label for="user_identity_url"><%=l(:field_identity_url)%></label>
<%= text_field 'user', 'identity_url' %></p>
<% end %>
<% @user.custom_field_values.select {|v| v.editable? || v.required?}.each do |value| %>
<% @user.custom_field_values.each do |value| %>
<p><%= custom_field_tag_with_label :user, value %></p>
<% end %>
<!--[eoform:user]-->

View File

@@ -55,9 +55,10 @@
<% end -%>
</div>
<% other_formats_links do |f| %>
<%= f.link_to 'Atom', :url => {:controller => 'projects', :action => 'activity', :id => nil, :user_id => @user, :key => User.current.rss_key} %>
<% end %>
<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) %>

View File

@@ -4,8 +4,7 @@
<table class="list">
<tr class="odd"><td><%= l(:text_default_administrator_account_changed) %></td><td><%= image_tag (@flags[:default_admin_changed] ? 'true.png' : 'false.png'), :style => "vertical-align:bottom;" %></td></tr>
<tr class="even"><td><%= l(:text_file_repository_writable) %> (<%= Attachment.storage_path %>)</td><td><%= image_tag (@flags[:file_repository_writable] ? 'true.png' : 'false.png'), :style => "vertical-align:bottom;" %></td></tr>
<tr class="even"><td><%= l(:text_plugin_assets_writable) %> (<%= Engines.public_directory %>)</td><td><%= image_tag (@flags[:plugin_assets_writable] ? 'true.png' : 'false.png'), :style => "vertical-align:bottom;" %></td></tr>
<tr class="even"><td><%= l(:text_file_repository_writable) %></td><td><%= image_tag (@flags[:file_repository_writable] ? 'true.png' : 'false.png'), :style => "vertical-align:bottom;" %></td></tr>
<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>

View File

@@ -17,20 +17,22 @@
<table class="list">
<thead><tr>
<th><%=l(:label_project)%></th>
<%= sort_header_tag('name', :caption => l(:label_project)) %>
<th><%=l(:field_description)%></th>
<th><%=l(:field_is_public)%></th>
<th><%=l(:field_created_on)%></th>
<th><%=l(:label_subproject_plural)%></th>
<%= sort_header_tag('is_public', :caption => l(:field_is_public), :default_order => 'desc') %>
<%= sort_header_tag('created_on', :caption => l(:field_created_on), :default_order => 'desc') %>
<th></th>
<th></th>
</tr></thead>
<tbody>
<% for project in @projects %>
<tr class="<%= cycle("odd", "even") %> <%= css_project_classes(project) %>">
<td class="name" style="padding-left: <%= project.level %>em;"><%= project.active? ? link_to(h(project.name), :controller => 'projects', :action => 'settings', :id => project) : h(project.name) %></td>
<td><%= textilizable project.short_description, :project => project %></td>
<td align="center"><%= image_tag 'true.png' if project.is_public? %></td>
<td align="center"><%= format_date(project.created_on) %></td>
<tr class="<%= cycle("odd", "even") %>">
<td><%= project.active? ? link_to(h(project.name), :controller => 'projects', :action => 'settings', :id => project) : h(project.name) %>
<td><%= textilizable project.short_description, :project => project %>
<td align="center"><%= project.children.size %>
<td align="center"><%= image_tag 'true.png' if project.is_public? %>
<td align="center"><%= format_date(project.created_on) %>
<td align="center" style="width:10%">
<small>
<%= link_to(l(:button_archive), { :controller => 'projects', :action => 'archive', :id => project }, :confirm => l(:text_are_you_sure), :method => :post, :class => 'icon icon-lock') if project.active? %>
@@ -45,4 +47,6 @@
</tbody>
</table>
<p class="pagination"><%= pagination_links_full @project_pages, @project_count %></p>
<% html_title(l(:label_project_plural)) -%>

View File

@@ -3,14 +3,14 @@
<p><%= link_to_attachment attachment, :class => 'icon icon-attachment' -%>
<%= h(" - #{attachment.description}") unless attachment.description.blank? %>
<span class="size">(<%= number_to_human_size attachment.filesize %>)</span>
<% if options[:deletable] %>
<%= link_to image_tag('delete.png'), {:controller => 'attachments', :action => 'destroy', :id => attachment},
<% if options[:delete_url] %>
<%= link_to image_tag('delete.png'), options[:delete_url].update({:attachment_id => attachment}),
:confirm => l(:text_are_you_sure),
:method => :post,
:class => 'delete',
:title => l(:button_delete) %>
<% end %>
<% if options[:author] %>
<% unless options[:no_author] %>
<span class="author"><%= attachment.author %>, <%= format_time(attachment.created_on) %></span>
<% end %>
</p>

View File

@@ -29,9 +29,11 @@
</tbody>
</table>
<% other_formats_links do |f| %>
<%= f.link_to 'Atom', :url => {:controller => 'projects', :action => 'activity', :id => @project, :show_messages => 1, :key => User.current.rss_key} %>
<% end %>
<p class="other-formats">
<%= l(:label_export_to) %>
<span><%= link_to 'Atom', {:controller => 'projects', :action => 'activity', :id => @project, :format => 'atom', :show_messages => 1, :key => User.current.rss_key},
:class => 'feed' %></span>
</p>
<% content_for :header_tags do %>
<%= auto_discovery_link_tag(:atom, {:controller => 'projects', :action => 'activity', :id => @project, :format => 'atom', :show_messages => 1, :key => User.current.rss_key}) %>

View File

@@ -4,7 +4,7 @@
<%= link_to_if_authorized l(:label_message_new),
{:controller => 'messages', :action => 'new', :board_id => @board},
:class => 'icon icon-add',
:onclick => 'Element.show("add-message"); Form.Element.focus("message_subject"); return false;' %>
:onclick => 'Element.show("add-message"); return false;' %>
<%= watcher_tag(@board, User.current) %>
</div>
@@ -33,9 +33,9 @@
<thead><tr>
<th><%= l(:field_subject) %></th>
<th><%= l(:field_author) %></th>
<%= sort_header_tag('created_on', :caption => l(:field_created_on)) %>
<%= sort_header_tag('replies', :caption => l(:label_reply_plural)) %>
<%= sort_header_tag('updated_on', :caption => l(:label_message_last)) %>
<%= sort_header_tag("#{Message.table_name}.created_on", :caption => l(:field_created_on)) %>
<%= 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>
<% @topics.each do |topic| %>

View File

@@ -1,5 +1,4 @@
<% diff = Redmine::UnifiedDiff.new(diff, :type => diff_type, :max_lines => Setting.diff_max_lines_displayed.to_i) -%>
<% diff.each do |table_file| -%>
<% Redmine::UnifiedDiff.new(diff, diff_type).each do |table_file| -%>
<div class="autoscroll">
<% if diff_type == 'sbs' -%>
<table class="filecontent CodeRay">
@@ -63,5 +62,3 @@
</div>
<% end -%>
<%= l(:text_diff_truncated) if diff.truncated? %>

View File

@@ -49,6 +49,23 @@ function toggle_custom_field_format() {
}
}
function addValueField() {
var f = $$('p#custom_field_possible_values span');
p = document.getElementById("custom_field_possible_values");
var v = f[0].cloneNode(true);
v.childNodes[0].value = "";
p.appendChild(v);
}
function deleteValueField(e) {
var f = $$('p#custom_field_possible_values span');
if (f.length == 1) {
e.parentNode.childNodes[0].value = "";
} else {
Element.remove(e.parentNode);
}
}
//]]>
</script>
@@ -59,22 +76,22 @@ function toggle_custom_field_format() {
<%= f.text_field :min_length, :size => 5, :no_label => true %> -
<%= f.text_field :max_length, :size => 5, :no_label => true %><br>(<%=l(:text_min_max_length_info)%>)</p>
<p><%= f.text_field :regexp, :size => 50 %><br>(<%=l(:text_regexp_info)%>)</p>
<p id="custom_field_possible_values"><%= f.text_area :possible_values, :value => @custom_field.possible_values.to_a.join("\n"),
:cols => 20,
:rows => 15 %>
<br /><em><%= l(:text_custom_field_possible_values_info) %></em></p>
<p id="custom_field_possible_values"><label><%= l(:field_possible_values) %> <%= image_to_function "add.png", "addValueField();return false" %></label>
<% (@custom_field.possible_values.to_a + [""]).each do |value| %>
<span><%= text_field_tag 'custom_field[possible_values][]', value, :size => 30 %> <%= image_to_function "delete.png", "deleteValueField(this);return false" %><br /></span>
<% end %>
</p>
<p><%= @custom_field.field_format == 'bool' ? f.check_box(:default_value) : f.text_field(:default_value) %></p>
</div>
<div class="box">
<% case @custom_field.class.name
<% case @custom_field.type.to_s
when "IssueCustomField" %>
<fieldset><legend><%=l(:label_tracker_plural)%></legend>
<% for tracker in @trackers %>
<%= check_box_tag "custom_field[tracker_ids][]", tracker.id, (@custom_field.trackers.include? tracker) %> <%= tracker.name %>
<%= check_box_tag "tracker_ids[]", tracker.id, (@custom_field.trackers.include? tracker) %> <%= tracker.name %>
<% end %>
<%= hidden_field_tag "custom_field[tracker_ids][]", '' %>
</fieldset>
&nbsp;
<p><%= f.check_box :is_required %></p>
@@ -84,7 +101,6 @@ when "IssueCustomField" %>
<% when "UserCustomField" %>
<p><%= f.check_box :is_required %></p>
<p><%= f.check_box :editable %></p>
<% when "ProjectCustomField" %>
<p><%= f.check_box :is_required %></p>

View File

@@ -2,7 +2,7 @@
<%= link_to_if_authorized l(:label_document_new),
{:controller => 'documents', :action => 'new', :project_id => @project},
:class => 'icon icon-add',
:onclick => 'Element.show("add-document"); Form.Element.focus("document_title"); return false;' %>
:onclick => 'Element.show("add-document"); return false;' %>
</div>
<div id="add-document" style="display:none;">

View File

@@ -12,7 +12,7 @@
</div>
<h3><%= l(:label_attachment_plural) %></h3>
<%= link_to_attachments @document %>
<%= link_to_attachments @attachments, :delete_url => (authorize_for('documents', 'destroy_attachment') ? {:controller => 'documents', :action => 'destroy_attachment', :id => @document} : nil) %>
<% if authorize_for('documents', 'add_attachment') %>
<p><%= link_to l(:label_attachment_new), {}, :onclick => "Element.show('add_attachment_form'); Element.hide(this); Element.scrollTo('add_attachment_form'); return false;",

View File

@@ -8,14 +8,13 @@
<div id="issue_descr_fields" <%= 'style="display:none"' unless @issue.new_record? || @issue.errors.any? %>>
<p><%= f.text_field :subject, :size => 80, :required => true %></p>
<p><%= f.text_area :description,
<p><%= f.text_area :description, :required => true,
:cols => 60,
:rows => (@issue.description.blank? ? 10 : [[10, @issue.description.length / 50].max, 100].min),
:accesskey => accesskey(:edit),
:class => 'wiki-edit' %></p>
</div>
<div class="attributes">
<div class="splitcontentleft">
<% if @issue.new_record? || @allowed_statuses.any? %>
<p><%= f.select :status_id, (@allowed_statuses.collect {|p| [p.name, p.id]}), :required => true %></p>
@@ -25,13 +24,11 @@
<p><%= f.select :priority_id, (@priorities.collect {|p| [p.name, p.id]}), :required => true %></p>
<p><%= f.select :assigned_to_id, (@issue.assignable_users.collect {|m| [m.name, m.id]}), :include_blank => true %></p>
<% unless @project.issue_categories.empty? %>
<p><%= f.select :category_id, (@project.issue_categories.collect {|c| [c.name, c.id]}), :include_blank => true %>
<%= prompt_to_remote(l(:label_issue_category_new),
l(:label_issue_category_new), 'category[name]',
{:controller => 'projects', :action => 'add_issue_category', :id => @project},
:class => 'small', :tabindex => 199) if authorize_for('projects', 'add_issue_category') %></p>
<% end %>
<%= content_tag('p', f.select(:fixed_version_id,
(@project.versions.sort.collect {|v| [v.name, v.id]}),
{ :include_blank => true })) unless @project.versions.empty? %>
@@ -46,20 +43,11 @@
<div style="clear:both;"> </div>
<%= render :partial => 'form_custom_fields' %>
</div>
<% if @issue.new_record? %>
<p><label><%=l(:label_attachment_plural)%></label><%= render :partial => 'attachments/form' %></p>
<% end %>
<% if @issue.new_record? && User.current.allowed_to?(:add_issue_watchers, @project) -%>
<p><label><%= l(:label_issue_watchers) %></label>
<% @issue.project.users.sort.each do |user| -%>
<label class="floating"><%= check_box_tag 'issue[watcher_user_ids][]', user.id, @issue.watcher_user_ids.include?(user.id) %> <%=h user %></label>
<% end -%>
</p>
<% end %>
<%= call_hook(:view_issues_form_details_bottom, { :issue => @issue, :form => f }) %>
<%= wikitoolbar_for 'issue_description' %>

View File

@@ -1,4 +1,3 @@
<div class="attributes">
<div class="splitcontentleft">
<p><%= f.select :status_id, (@allowed_statuses.collect {|p| [p.name, p.id]}), :required => true %></p>
<p><%= f.select :assigned_to_id, (@issue.assignable_users.collect {|m| [m.name, m.id]}), :include_blank => true %></p>
@@ -9,4 +8,3 @@
(@project.versions.sort.collect {|v| [v.name, v.id]}),
{ :include_blank => true })) unless @project.versions.empty? %>
</div>
</div>

View File

@@ -4,14 +4,14 @@
<th><%= link_to image_tag('toggle_check.png'), {}, :onclick => 'toggleIssuesSelection(Element.up(this, "form")); return false;',
:title => "#{l(:button_check_all)}/#{l(:button_uncheck_all)}" %>
</th>
<%= sort_header_tag('id', :caption => '#', :default_order => 'desc') %>
<%= sort_header_tag("#{Issue.table_name}.id", :caption => '#', :default_order => 'desc') %>
<% query.columns.each do |column| %>
<%= column_header(column) %>
<% end %>
</tr></thead>
<tbody>
<% issues.each do |issue| -%>
<tr id="issue-<%= issue.id %>" class="hascontextmenu <%= cycle('odd', 'even') %> <%= css_issue_classes(issue) %>">
<tr id="issue-<%= issue.id %>" class="issue hascontextmenu <%= cycle('odd', 'even') %> <%= "status-#{issue.status.position} priority-#{issue.priority.position}" %>">
<td class="checkbox"><%= check_box_tag("ids[]", issue.id, false, :id => nil) %></td>
<td><%= link_to issue.id, :controller => 'issues', :action => 'show', :id => issue %></td>
<% query.columns.each do |column| %><%= content_tag 'td', column_content(column, issue), :class => column.name %><% end %>

View File

@@ -3,22 +3,21 @@
<table class="list issues">
<thead><tr>
<th>#</th>
<th><%=l(:field_project)%></th>
<th><%=l(:field_tracker)%></th>
<th><%=l(:field_subject)%></th>
</tr></thead>
<tbody>
<% for issue in issues %>
<tr id="issue-<%= issue.id %>" class="hascontextmenu <%= cycle('odd', 'even') %> <%= css_issue_classes(issue) %>">
<tr id="issue-<%= issue.id %>" class="issue hascontextmenu <%= cycle('odd', 'even') %> <%= "status-#{issue.status.position} priority-#{issue.priority.position}" %>">
<td class="id">
<%= check_box_tag("ids[]", issue.id, false, :style => 'display:none;') %>
<%= check_box_tag("ids[]", issue.id, false, :style => 'display:none;') %>
<%= link_to issue.id, :controller => 'issues', :action => 'show', :id => issue %>
</td>
<td class="project"><%=h issue.project %></td>
<td class="tracker"><%=h issue.tracker %></td>
<td><%=h issue.project.name %> - <%= issue.tracker.name %><br />
<%= issue.status.name %> - <%= format_time(issue.updated_on) %></td>
<td class="subject">
<%= link_to h(truncate(issue.subject, 60)), :controller => 'issues', :action => 'show', :id => issue %> (<%=h issue.status %>)
</td>
<%= link_to h(issue.subject), :controller => 'issues', :action => 'show', :id => issue %>
</td>
</tr>
<% end %>
</tbody>

View File

@@ -8,7 +8,7 @@
<% if @issue.relations.any? %>
<table style="width:100%">
<% @issue.relations.select {|r| r.other_issue(@issue).visible? }.each do |relation| %>
<% @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 %>
<%= h(relation.other_issue(@issue).project) + ' - ' if Setting.cross_project_issue_relations? %> <%= link_to_issue relation.other_issue(@issue) %></td>

View File

@@ -20,7 +20,7 @@
<h3><%= l(:label_query_plural) %></h3>
<% sidebar_queries.each do |query| -%>
<%= link_to(h(query.name), :controller => 'issues', :action => 'index', :project_id => @project, :query_id => query) %><br />
<%= 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

@@ -27,7 +27,7 @@
<label><%= l(:field_fixed_version) %>:
<%= select_tag('fixed_version_id', content_tag('option', l(:label_no_change_option), :value => '') +
content_tag('option', l(:label_none), :value => 'none') +
options_from_collection_for_select(@project.versions.sort, :id, :name)) %></label>
options_from_collection_for_select(@project.versions, :id, :name)) %></label>
</p>
<p>
@@ -38,20 +38,12 @@
<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>
<% @custom_fields.each do |custom_field| %>
<p><label><%= h(custom_field.name) %></label>
<%= select_tag "custom_field_values[#{custom_field.id}]", options_for_select([[l(:label_no_change_option), '']] + custom_field.possible_values) %></label>
</p>
<% end %>
<%= call_hook(:view_issues_bulk_edit_details_bottom, { :issues => @issues }) %>
</fieldset>
<fieldset><legend><%= l(:field_notes) %></legend>
<%= text_area_tag 'notes', @notes, :cols => 60, :rows => 10, :class => 'wiki-edit' %>
<%= wikitoolbar_for 'notes' %>
</fieldset>
</div>
<p><%= submit_tag l(:button_submit) %>

View File

@@ -79,10 +79,7 @@
: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-add' %></li>
<% end %>
<% if User.current.logged? %>
<li><%= watcher_link(@issue, User.current) %></li>
:class => 'icon-time' %></li>
<% end %>
<% end %>

View File

@@ -0,0 +1,188 @@
<%
pdf=IfpdfHelper::IFPDF.new(current_language)
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.to_s)
pdf.Ln
pdf.SetFontStyle('B',9)
subject_width = 70
header_heigth = 5
headers_heigth = header_heigth
show_weeks = false
show_days = false
if @gantt.months < 7
show_weeks = true
headers_heigth = 2*header_heigth
if @gantt.months < 3
show_days = true
headers_heigth = 3*header_heigth
end
end
g_width = 210
zoom = (g_width) / (@gantt.date_to - @gantt.date_from + 1)
g_height = 120
t_height = g_height + headers_heigth
y_start = pdf.GetY
#
# Months headers
#
month_f = @gantt.date_from
left = subject_width
height = header_heigth
@gantt.months.times do
width = ((month_f >> 1) - month_f) * zoom
pdf.SetY(y_start)
pdf.SetX(left)
pdf.Cell(width, height, "#{month_f.year}-#{month_f.month}", "LTR", 0, "C")
left = left + width
month_f = month_f >> 1
end
#
# Weeks headers
#
if show_weeks
left = subject_width
height = header_heigth
if @gantt.date_from.cwday == 1
# @gantt.date_from is monday
week_f = @gantt.date_from
else
# 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 <= @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")
left = left + width
week_f = week_f+7
end
end
#
# Days headers
#
if show_days
left = subject_width
height = header_heigth
wday = @gantt.date_from.cwday
pdf.SetFontStyle('B',7)
(@gantt.date_to - @gantt.date_from + 1).to_i.times do
width = zoom
pdf.SetY(y_start + 2 * header_heigth)
pdf.SetX(left)
pdf.Cell(width, height, day_name(wday).first, "LTR", 0, "C")
left = left + width
wday = wday + 1
wday = 1 if wday > 7
end
end
pdf.SetY(y_start)
pdf.SetX(15)
pdf.Cell(subject_width+g_width-15, headers_heigth, "", 1)
#
# Tasks
#
top = headers_heigth + y_start
pdf.SetFontStyle('B',7)
@gantt.events.each do |i|
pdf.SetY(top)
pdf.SetX(15)
if i.is_a? Issue
pdf.Cell(subject_width-15, 5, "#{i.tracker.name} #{i.id}: #{i.subject}".sub(/^(.{30}[^\s]*\s).*$/, '\1 (...)'), "LR")
else
pdf.Cell(subject_width-15, 5, "#{l(:label_version)}: #{i.name}", "LR")
end
pdf.SetY(top)
pdf.SetX(subject_width)
pdf.Cell(g_width, 5, "", "LR")
pdf.SetY(top+1.5)
if i.is_a? Issue
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 <= @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 - @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
l_width ||= 0
pdf.SetX(subject_width + i_left)
pdf.SetFillColor(200,200,200)
pdf.Cell(i_width, 2, "", 0, 0, "", 1)
if l_width > 0
pdf.SetY(top+1.5)
pdf.SetX(subject_width + i_left)
pdf.SetFillColor(255,100,100)
pdf.Cell(l_width, 2, "", 0, 0, "", 1)
end
if d_width > 0
pdf.SetY(top+1.5)
pdf.SetX(subject_width + i_left)
pdf.SetFillColor(100,100,255)
pdf.Cell(d_width, 2, "", 0, 0, "", 1)
end
pdf.SetY(top+1.5)
pdf.SetX(subject_width + i_left + i_width)
pdf.Cell(30, 2, "#{i.status.name} #{i.done_ratio}%")
else
i_left = ((i.start_date - @gantt.date_from)*zoom)
pdf.SetX(subject_width + i_left)
pdf.SetFillColor(50,200,50)
pdf.Cell(2, 2, "", 0, 0, "", 1)
pdf.SetY(top+1.5)
pdf.SetX(subject_width + i_left + 3)
pdf.Cell(30, 2, "#{i.name}")
end
top = top + 5
pdf.SetDrawColor(200, 200, 200)
pdf.Line(15, top, subject_width+g_width, top)
if pdf.GetY() > 180
pdf.AddPage("L")
top = 20
pdf.Line(15, top, subject_width+g_width, top)
end
pdf.SetDrawColor(0, 0, 0)
end
pdf.Line(15, top, subject_width+g_width, top)
%>
<%= pdf.Output %>

View File

@@ -241,10 +241,13 @@ if Date.today >= @gantt.date_from and Date.today <= @gantt.date_to %>
</tr>
</table>
<% other_formats_links do |f| %>
<%= f.link_to 'PDF', :url => @gantt.params %>
<%= f.link_to('PNG', :url => @gantt.params) if @gantt.respond_to?('to_image') %>
<p class="other-formats">
<%= l(:label_export_to) %>
<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 %>

View File

@@ -0,0 +1,50 @@
<% pdf=IfpdfHelper::IFPDF.new(current_language)
title = @project ? "#{@project.name} - #{l(:label_issue_plural)}" : "#{l(:label_issue_plural)}"
pdf.SetTitle(title)
pdf.AliasNbPages
pdf.footer_date = format_date(Date.today)
pdf.AddPage("L")
row_height = 7
#
# title
#
pdf.SetFontStyle('B',11)
pdf.Cell(190,10, title)
pdf.Ln
#
# headers
#
pdf.SetFontStyle('B',10)
pdf.SetFillColor(230, 230, 230)
pdf.Cell(15, row_height, "#", 0, 0, 'L', 1)
pdf.Cell(30, row_height, l(:field_tracker), 0, 0, 'L', 1)
pdf.Cell(30, row_height, l(:field_status), 0, 0, 'L', 1)
pdf.Cell(30, row_height, l(:field_priority), 0, 0, 'L', 1)
pdf.Cell(40, row_height, l(:field_assigned_to), 0, 0, 'L', 1)
pdf.Cell(25, row_height, l(:field_updated_on), 0, 0, 'L', 1)
pdf.Cell(0, row_height, l(:field_subject), 0, 0, 'L', 1)
pdf.Line(10, pdf.GetY, 287, pdf.GetY)
pdf.Ln
pdf.Line(10, pdf.GetY, 287, pdf.GetY)
pdf.SetY(pdf.GetY() + 1)
#
# rows
#
pdf.SetFontStyle('',9)
pdf.SetFillColor(255, 255, 255)
@issues.each do |issue|
pdf.Cell(15, row_height, issue.id.to_s, 0, 0, 'L', 1)
pdf.Cell(30, row_height, issue.tracker.name, 0, 0, 'L', 1)
pdf.Cell(30, row_height, issue.status.name, 0, 0, 'L', 1)
pdf.Cell(30, row_height, issue.priority.name, 0, 0, 'L', 1)
pdf.Cell(40, row_height, issue.assigned_to ? issue.assigned_to.name : '', 0, 0, 'L', 1)
pdf.Cell(25, row_height, format_date(issue.updated_on), 0, 0, 'L', 1)
pdf.MultiCell(0, row_height, (@project == issue.project ? issue.subject : "#{issue.project.name} - #{issue.subject}"))
pdf.Line(10, pdf.GetY, 287, pdf.GetY)
pdf.SetY(pdf.GetY() + 1)
end
%>
<%= pdf.Output %>

View File

@@ -3,7 +3,7 @@
<% html_title(l(:label_issue_plural)) %>
<% form_tag({ :controller => 'queries', :action => 'new' }, :id => 'query_form') do %>
<%= hidden_field_tag('project_id', @project.to_param) if @project %>
<%= hidden_field_tag('project_id', @project.id) if @project %>
<fieldset id="filters"><legend><%= l(:label_filter_plural) %></legend>
<%= render :partial => 'queries/filters', :locals => {:query => @query} %>
<p class="buttons">
@@ -14,8 +14,7 @@
}, :class => 'icon icon-checked' %>
<%= link_to_remote l(:button_clear),
{ :url => { :set_filter => 1, :project_id => @project },
:method => :get,
{ :url => { :set_filter => 1 },
:update => "content",
}, :class => 'icon icon-reload' %>
@@ -44,12 +43,12 @@
<%= render :partial => 'issues/list', :locals => {:issues => @issues, :query => @query} %>
<p class="pagination"><%= pagination_links_full @issue_pages, @issue_count %></p>
<% other_formats_links do |f| %>
<%= f.link_to 'Atom', :url => {:query_id => (@query.new_record? ? nil : @query), :key => User.current.rss_key} %>
<%= f.link_to 'CSV' %>
<%= f.link_to 'PDF' %>
<% end %>
<p class="other-formats">
<%= l(:label_export_to) %>
<span><%= link_to 'Atom', {:query_id => @query, :format => 'atom', :key => User.current.rss_key}, :class => 'feed' %></span>
<span><%= link_to 'CSV', {:format => 'csv'}, :class => 'csv' %></span>
<span><%= link_to 'PDF', {:format => 'pdf'}, :class => 'pdf' %></span>
</p>
<% end %>
<% end %>

View File

@@ -6,19 +6,16 @@
<%= @issues.collect {|i| hidden_field_tag('ids[]', i.id)}.join %>
<div class="box tabular">
<p><label for="new_project_id"><%=l(:field_project)%>:</label>
<p><label for="new_project_id"><%=l(:field_project)%> :</label>
<%= select_tag "new_project_id",
project_tree_options_for_select(@allowed_projects, :selected => @target_project),
options_from_collection_for_select(@allowed_projects, 'id', 'name', @target_project.id),
:onchange => remote_function(:url => { :action => 'move' },
:method => :get,
:update => 'content',
:with => "Form.serialize('move_form')") %></p>
<p><label for="new_tracker_id"><%=l(:field_tracker)%>:</label>
<p><label for="new_tracker_id"><%=l(:field_tracker)%> :</label>
<%= select_tag "new_tracker_id", "<option value=\"\">#{l(:label_no_change_option)}</option>" + options_from_collection_for_select(@trackers, "id", "name") %></p>
<p><label for="copy_options_copy"><%= l(:button_copy)%></label>
<%= check_box_tag "copy_options[copy]", "1" %></p>
</div>
<%= submit_tag l(:button_move) %>

View File

@@ -7,7 +7,6 @@
<%= render :partial => 'issues/form', :locals => {:f => f} %>
</div>
<%= submit_tag l(:button_create) %>
<%= submit_tag l(:button_create_and_continue), :name => 'continue' %>
<%= link_to_remote l(:label_preview),
{ :url => { :controller => 'issues', :action => 'preview', :project_id => @project },
:method => 'post',
@@ -15,12 +14,6 @@
:with => "Form.serialize('issue-form')",
:complete => "Element.scrollTo('preview')"
}, :accesskey => accesskey(:preview) %>
<%= javascript_tag "Form.Element.focus('issue_subject');" %>
<% end %>
<div id="preview" class="wiki"></div>
<% content_for :header_tags do %>
<%= stylesheet_link_tag 'scm' %>
<% end %>

126
app/views/issues/show.rfpdf Normal file
View File

@@ -0,0 +1,126 @@
<% pdf=IfpdfHelper::IFPDF.new(current_language)
pdf.SetTitle("#{@project.name} - ##{@issue.tracker.name} #{@issue.id}")
pdf.AliasNbPages
pdf.footer_date = format_date(Date.today)
pdf.AddPage
pdf.SetFontStyle('B',11)
pdf.Cell(190,10, "#{@issue.project} - #{@issue.tracker} # #{@issue.id}: #{@issue.subject}")
pdf.Ln
y0 = pdf.GetY
pdf.SetFontStyle('B',9)
pdf.Cell(35,5, l(:field_status) + ":","LT")
pdf.SetFontStyle('',9)
pdf.Cell(60,5, @issue.status.name,"RT")
pdf.SetFontStyle('B',9)
pdf.Cell(35,5, l(:field_priority) + ":","LT")
pdf.SetFontStyle('',9)
pdf.Cell(60,5, @issue.priority.name,"RT")
pdf.Ln
pdf.SetFontStyle('B',9)
pdf.Cell(35,5, l(:field_author) + ":","L")
pdf.SetFontStyle('',9)
pdf.Cell(60,5, @issue.author.name,"R")
pdf.SetFontStyle('B',9)
pdf.Cell(35,5, l(:field_category) + ":","L")
pdf.SetFontStyle('',9)
pdf.Cell(60,5, (@issue.category ? @issue.category.name : "-"),"R")
pdf.Ln
pdf.SetFontStyle('B',9)
pdf.Cell(35,5, l(:field_created_on) + ":","L")
pdf.SetFontStyle('',9)
pdf.Cell(60,5, format_date(@issue.created_on),"R")
pdf.SetFontStyle('B',9)
pdf.Cell(35,5, l(:field_assigned_to) + ":","L")
pdf.SetFontStyle('',9)
pdf.Cell(60,5, (@issue.assigned_to ? @issue.assigned_to.name : "-"),"R")
pdf.Ln
pdf.SetFontStyle('B',9)
pdf.Cell(35,5, l(:field_updated_on) + ":","LB")
pdf.SetFontStyle('',9)
pdf.Cell(60,5, format_date(@issue.updated_on),"RB")
pdf.SetFontStyle('B',9)
pdf.Cell(35,5, l(:field_due_date) + ":","LB")
pdf.SetFontStyle('',9)
pdf.Cell(60,5, format_date(@issue.due_date),"RB")
pdf.Ln
for custom_value in @issue.custom_values
pdf.SetFontStyle('B',9)
pdf.Cell(35,5, custom_value.custom_field.name + ":","L")
pdf.SetFontStyle('',9)
pdf.MultiCell(155,5, (show_value custom_value),"R")
end
pdf.SetFontStyle('B',9)
pdf.Cell(35,5, l(:field_subject) + ":","LTB")
pdf.SetFontStyle('',9)
pdf.Cell(155,5, @issue.subject,"RTB")
pdf.Ln
pdf.SetFontStyle('B',9)
pdf.Cell(35,5, l(:field_description) + ":")
pdf.SetFontStyle('',9)
pdf.MultiCell(155,5, @issue.description,"BR")
pdf.Line(pdf.GetX, y0, pdf.GetX, pdf.GetY)
pdf.Line(pdf.GetX, pdf.GetY, 170, pdf.GetY)
pdf.Ln
if @issue.changesets.any? && User.current.allowed_to?(:view_changesets, @issue.project)
pdf.SetFontStyle('B',9)
pdf.Cell(190,5, l(:label_associated_revisions), "B")
pdf.Ln
for changeset in @issue.changesets
pdf.SetFontStyle('B',8)
pdf.Cell(190,5, format_time(changeset.committed_on) + " - " + changeset.author.to_s)
pdf.Ln
unless changeset.comments.blank?
pdf.SetFontStyle('',8)
pdf.MultiCell(190,5, changeset.comments)
end
pdf.Ln
end
end
pdf.SetFontStyle('B',9)
pdf.Cell(190,5, l(:label_history), "B")
pdf.Ln
for journal in @issue.journals.find(:all, :include => [:user, :details], :order => "#{Journal.table_name}.created_on ASC")
pdf.SetFontStyle('B',8)
pdf.Cell(190,5, format_time(journal.created_on) + " - " + journal.user.name)
pdf.Ln
pdf.SetFontStyle('I',8)
for detail in journal.details
pdf.Cell(190,5, "- " + show_detail(detail, true))
pdf.Ln
end
if journal.notes?
pdf.SetFontStyle('',8)
pdf.MultiCell(190,5, journal.notes)
end
pdf.Ln
end
if @issue.attachments.any?
pdf.SetFontStyle('B',9)
pdf.Cell(190,5, l(:label_attachment_plural), "B")
pdf.Ln
for attachment in @issue.attachments
pdf.SetFontStyle('',8)
pdf.Cell(80,5, attachment.filename)
pdf.Cell(20,5, number_to_human_size(attachment.filesize),0,0,"R")
pdf.Cell(25,5, format_date(attachment.created_on),0,0,"R")
pdf.Cell(65,5, attachment.author.name,0,0,"R")
pdf.Ln
end
end
%>
<%= pdf.Output %>

View File

@@ -1,6 +1,6 @@
<div class="contextual">
<%= link_to_if_authorized(l(:button_update), {:controller => 'issues', :action => 'edit', :id => @issue }, :onclick => 'showAndScrollTo("update", "notes"); return false;', :class => 'icon icon-edit', :accesskey => accesskey(:edit)) %>
<%= link_to_if_authorized l(:button_log_time), {:controller => 'timelog', :action => 'edit', :issue_id => @issue}, :class => 'icon icon-time-add' %>
<%= link_to_if_authorized l(:button_log_time), {:controller => 'timelog', :action => 'edit', :issue_id => @issue}, :class => 'icon icon-time' %>
<%= watcher_tag(@issue, User.current) %>
<%= link_to_if_authorized l(:button_copy), {:controller => 'issues', :action => 'new', :project_id => @project, :copy_from => @issue }, :class => 'icon icon-copy' %>
<%= link_to_if_authorized l(:button_move), {:controller => 'issues', :action => 'move', :id => @issue }, :class => 'icon icon-move' %>
@@ -9,7 +9,7 @@
<h2><%= @issue.tracker.name %> #<%= @issue.id %></h2>
<div class="<%= css_issue_classes(@issue) %>">
<div class="issue <%= "status-#{@issue.status.position} priority-#{@issue.priority.position}" %>">
<%= avatar(@issue.author, :size => "64") %>
<h3><%=h @issue.subject %></h3>
<p class="author">
@@ -23,7 +23,7 @@
<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 class="priority"><b><%=l(:field_priority)%>:</b></td><td class="priority"><%= @issue.priority.name %></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>
@@ -34,7 +34,7 @@
<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 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}) : "-" %></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>
@@ -59,7 +59,7 @@ end %>
<hr />
<div class="contextual">
<%= link_to_remote_if_authorized(l(:button_quote), { :url => {:action => 'reply', :id => @issue} }, :class => 'icon icon-comment') unless @issue.description.blank? %>
<%= 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>
@@ -67,7 +67,9 @@ end %>
<%= textilizable @issue, :description, :attachments => @issue.attachments %>
</div>
<%= link_to_attachments @issue %>
<% if @issue.attachments.any? %>
<%= link_to_attachments @issue.attachments, :delete_url => (authorize_for('issues', 'destroy_attachment') ? {:controller => 'issues', :action => 'destroy_attachment', :id => @issue} : nil) %>
<% end %>
<% if authorize_for('issue_relations', 'new') || @issue.relations.any? %>
<hr />
@@ -108,10 +110,11 @@ end %>
</div>
<% end %>
<% other_formats_links do |f| %>
<%= f.link_to 'Atom', :url => {:key => User.current.rss_key} %>
<%= f.link_to 'PDF' %>
<% end %>
<p class="other-formats">
<%= l(:label_export_to) %>
<span><%= link_to 'Atom', {:format => 'atom', :key => User.current.rss_key}, :class => 'feed' %></span>
<span><%= link_to 'PDF', {:format => 'pdf'}, :class => 'pdf' %></span>
</p>
<% html_title "#{@issue.tracker.name} ##{@issue.id}: #{@issue.subject}" %>

View File

@@ -1,7 +1,6 @@
<% form_remote_tag(:url => {}, :html => { :id => "journal-#{@journal.id}-form" }) do %>
<%= text_area_tag :notes, h(@journal.notes), :class => 'wiki-edit',
:rows => (@journal.notes.blank? ? 10 : [[10, @journal.notes.length / 50].max, 100].min) %>
<%= call_hook(:view_journals_notes_form_after_notes, { :journal => @journal}) %>
<%= text_area_tag :notes, @journal.notes, :class => 'wiki-edit',
:rows => (@journal.notes.blank? ? 10 : [[10, @journal.notes.length / 50].max, 100].min) %>
<p><%= submit_tag l(:button_save) %>
<%= link_to l(:button_cancel), '#', :onclick => "Element.remove('journal-#{@journal.id}-form'); " +
"Element.show('journal-#{@journal.id}-notes'); return false;" %></p>

View File

@@ -6,5 +6,3 @@ else
page.show "journal-#{@journal.id}-notes"
page.remove "journal-#{@journal.id}-form"
end
call_hook(:view_journals_update_rjs_bottom, { :page => page, :journal => @journal })

View File

@@ -0,0 +1,12 @@
<% user_projects_by_root = User.current.projects.find(:all, :include => :parent).group_by(&:root) %>
<select onchange="if (this.value != '') { window.location = this.value; }">
<option selected="selected"><%= l(:label_jump_to_a_project) %></option>
<option disabled="disabled">---</option>
<% user_projects_by_root.keys.sort.each do |root| %>
<%= content_tag('option', h(root.name), :value => url_for(:controller => 'projects', :action => 'show', :id => root)) %>
<% user_projects_by_root[root].sort.each do |project| %>
<% next if project == root %>
<%= content_tag('option', ('&#187; ' + h(project.name)), :value => url_for(:controller => 'projects', :action => 'show', :id => project)) %>
<% end %>
<% end %>
</select>

View File

@@ -24,7 +24,7 @@
<div id="account">
<%= render_menu :account_menu -%>
</div>
<%= content_tag('div', "#{l(:label_logged_as)} #{link_to_user(User.current, :format => :username)}", :id => 'loggedas') if User.current.logged? %>
<%= content_tag('div', "#{l(:label_logged_as)} #{User.current.login}", :id => 'loggedas') if User.current.logged? %>
<%= render_menu :top_menu -%>
</div>
@@ -34,7 +34,7 @@
<%= link_to l(:label_search), {:controller => 'search', :action => 'index', :id => @project}, :accesskey => accesskey(:search) %>:
<%= text_field_tag 'q', @question, :size => 20, :class => 'small', :accesskey => accesskey(:quick_search) %>
<% end %>
<%= render_project_jump_box %>
<%= render :partial => 'layouts/project_selector' if User.current.memberships.any? %>
</div>
<h1><%= h(@project && !@project.new_record? ? @project.name : Setting.app_title) %></h1>
@@ -47,20 +47,19 @@
<%= tag('div', {:id => 'main', :class => (has_content?(:sidebar) ? '' : 'nosidebar')}, true) %>
<div id="sidebar">
<%= yield :sidebar %>
<%= call_hook :view_layouts_base_sidebar %>
</div>
<div id="content">
<%= render_flash_messages %>
<%= content_tag('div', flash[:error], :class => 'flash error') if flash[:error] %>
<%= content_tag('div', flash[:notice], :class => 'flash notice') if flash[:notice] %>
<%= yield %>
<%= call_hook :view_layouts_base_content %>
</div>
</div>
<div id="ajax-indicator" style="display:none;"><span><%= l(:label_loading) %></span></div>
<div id="footer">
Powered by <%= link_to Redmine::Info.app_name, Redmine::Info.url %> &copy; 2006-2009 Jean-Philippe Lang
Powered by <%= link_to Redmine::Info.app_name, Redmine::Info.url %> &copy; 2006-2008 Jean-Philippe Lang
</div>
</div>
<%= call_hook :view_layouts_base_body_bottom %>

View File

@@ -15,7 +15,7 @@
<div class="wiki">
<%= textilizable(@topic.content, :attachments => @topic.attachments) %>
</div>
<%= link_to_attachments @topic, :author => false %>
<%= link_to_attachments @topic.attachments, :no_author => true %>
</div>
<br />
@@ -23,15 +23,15 @@
<h3 class="icon22 icon22-comment"><%= l(:label_reply_plural) %></h3>
<% @replies.each do |message| %>
<a name="<%= "message-#{message.id}" %>"></a>
<div class="contextual">
<%= link_to_remote_if_authorized image_tag('comment.png'), { :url => {:action => 'quote', :id => message} }, :title => l(:button_quote) %>
<%= link_to(image_tag('edit.png'), {:action => 'edit', :id => message}, :title => l(:button_edit)) if message.editable_by?(User.current) %>
<%= link_to(image_tag('delete.png'), {:action => 'destroy', :id => message}, :method => :post, :confirm => l(:text_are_you_sure), :title => l(:button_delete)) if message.destroyable_by?(User.current) %>
</div>
<div class="message reply">
<div class="contextual">
<%= link_to_remote_if_authorized image_tag('comment.png'), { :url => {:action => 'quote', :id => message} }, :title => l(:button_quote) %>
<%= link_to(image_tag('edit.png'), {:action => 'edit', :id => message}, :title => l(:button_edit)) if message.editable_by?(User.current) %>
<%= link_to(image_tag('delete.png'), {:action => 'destroy', :id => message}, :method => :post, :confirm => l(:text_are_you_sure), :title => l(:button_delete)) if message.destroyable_by?(User.current) %>
</div>
<h4><%=h message.subject %> - <%= authoring message.created_on, message.author %></h4>
<div class="wiki"><%= textilizable message, :content, :attachments => message.attachments %></div>
<%= link_to_attachments message, :author => false %>
<%= link_to_attachments message.attachments, :no_author => true %>
</div>
<% end %>
<% end %>

View File

@@ -15,13 +15,6 @@
<p><%= f.text_field :lastname, :required => true %></p>
<p><%= f.text_field :mail, :required => true %></p>
<p><%= f.select :language, lang_options_for_select %></p>
<% if Setting.openid? %>
<p><%= f.text_field :identity_url %></p>
<% end %>
<% @user.custom_field_values.select(&:editable?).each do |value| %>
<p><%= custom_field_tag_with_label :user, value %></p>
<% end %>
</div>
<%= submit_tag l(:button_save) %>
@@ -45,7 +38,7 @@
<div class="box tabular">
<% fields_for :pref, @user.pref, :builder => TabularFormBuilder, :lang => current_language do |pref_fields| %>
<p><%= pref_fields.check_box :hide_mail %></p>
<p><%= pref_fields.select :time_zone, ActiveSupport::TimeZone.all.collect {|z| [ z.to_s, z.name ]}, :include_blank => true %></p>
<p><%= pref_fields.select :time_zone, TimeZone.all.collect {|z| [ z.to_s, z.name ]}, :include_blank => true %></p>
<p><%= pref_fields.select :comments_sorting, [[l(:label_chronological_order), 'asc'], [l(:label_reverse_chronological_order), 'desc']] %></p>
<% end %>
</div>

View File

@@ -1,6 +1,6 @@
<h3><%=l(:label_assigned_to_me_issues)%></h3>
<% assigned_issues = Issue.visible.find(:all,
:conditions => ["assigned_to_id=? AND #{IssueStatus.table_name}.is_closed=?", user.id, false],
<% assigned_issues = Issue.find(:all,
:conditions => ["assigned_to_id=? AND #{IssueStatus.table_name}.is_closed=? AND #{Project.table_name}.status=#{Project::STATUS_ACTIVE}", user.id, false],
:limit => 10,
:include => [ :status, :project, :tracker, :priority ],
:order => "#{Enumeration.table_name}.position DESC, #{Issue.table_name}.updated_on DESC") %>

View File

@@ -1,6 +1,6 @@
<h3><%=l(:label_reported_issues)%></h3>
<% reported_issues = Issue.visible.find(:all,
:conditions => { :author_id => user.id },
<% reported_issues = Issue.find(:all,
:conditions => ["author_id=? AND #{Project.table_name}.status=#{Project::STATUS_ACTIVE}", user.id],
:limit => 10,
:include => [ :status, :project, :tracker ],
:order => "#{Issue.table_name}.updated_on DESC") %>

View File

@@ -1,10 +1,10 @@
<h3><%=l(:label_watched_issues)%></h3>
<% watched_issues = Issue.visible.find(:all,
<% watched_issues = Issue.find(:all,
:include => [:status, :project, :tracker, :watchers],
:limit => 10,
:conditions => ["#{Watcher.table_name}.user_id = ?", user.id],
:conditions => ["#{Watcher.table_name}.user_id = ? AND #{Project.table_name}.status=#{Project::STATUS_ACTIVE}", user.id],
:order => "#{Issue.table_name}.updated_on DESC") %>
<%= render :partial => 'issues/list_simple', :locals => { :issues => watched_issues } %>
<% if watched_issues.length > 0 %>
<p class="small"><%= link_to l(:label_issue_view_all), :controller => 'issues', :action => 'index', :set_filter => 1, :watcher_id => 'me' %></p>
<p><%=lwr(:label_last_updates, watched_issues.length)%></p>
<% end %>

View File

@@ -47,6 +47,6 @@ entries_by_day = entries.group_by(&:spent_on)
</tr>
<% end -%>
<% end -%>
</tbody>
</tbdoy>
</table>
<% end %>

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