# HG changeset patch # User Chris Cannam # Date 1282750224 -3600 # Node ID 1d32c0a0efbf9f4d397646106e08b484dbb462de # Parent 9cc62779c13ae34e02de1475308ad31f9733976b * Update to SVN trunk (revisions 3892-4040) diff -r 9cc62779c13a -r 1d32c0a0efbf .svn/all-wcprops --- a/.svn/all-wcprops Wed Jul 28 12:47:17 2010 +0100 +++ b/.svn/all-wcprops Wed Aug 25 16:30:24 2010 +0100 @@ -1,7 +1,7 @@ K 25 svn:wc:ra_dav:version-url V 24 -/svn/!svn/ver/3891/trunk +/svn/!svn/ver/4040/trunk END Rakefile K 25 diff -r 9cc62779c13a -r 1d32c0a0efbf .svn/entries --- a/.svn/entries Wed Jul 28 12:47:17 2010 +0100 +++ b/.svn/entries Wed Aug 25 16:30:24 2010 +0100 @@ -1,15 +1,15 @@ 10 dir -3892 +4040 http://redmine.rubyforge.org/svn/trunk http://redmine.rubyforge.org/svn -2010-07-25T12:56:57.556116Z -3891 -jplang +2010-08-25T14:59:16.537407Z +4040 +edavis10 has-props @@ -69,9 +69,6 @@ 307 -extra -dir - README.rdoc file @@ -106,6 +103,9 @@ 208 +extra +dir + db dir @@ -121,6 +121,12 @@ script dir +doc +dir + +config +dir + .gitignore file @@ -155,12 +161,6 @@ 322 -config -dir - -doc -dir - lib dir diff -r 9cc62779c13a -r 1d32c0a0efbf app/.svn/all-wcprops --- a/app/.svn/all-wcprops Wed Jul 28 12:47:17 2010 +0100 +++ b/app/.svn/all-wcprops Wed Aug 25 16:30:24 2010 +0100 @@ -1,5 +1,5 @@ K 25 svn:wc:ra_dav:version-url V 28 -/svn/!svn/ver/3889/trunk/app +/svn/!svn/ver/4040/trunk/app END diff -r 9cc62779c13a -r 1d32c0a0efbf app/.svn/entries --- a/app/.svn/entries Wed Jul 28 12:47:17 2010 +0100 +++ b/app/.svn/entries Wed Aug 25 16:30:24 2010 +0100 @@ -1,15 +1,15 @@ 10 dir -3892 +4040 http://redmine.rubyforge.org/svn/trunk/app http://redmine.rubyforge.org/svn -2010-07-25T12:48:28.979207Z -3889 -jplang +2010-08-25T14:59:16.537407Z +4040 +edavis10 diff -r 9cc62779c13a -r 1d32c0a0efbf app/controllers/.svn/all-wcprops --- a/app/controllers/.svn/all-wcprops Wed Jul 28 12:47:17 2010 +0100 +++ b/app/controllers/.svn/all-wcprops Wed Aug 25 16:30:24 2010 +0100 @@ -1,13 +1,13 @@ K 25 svn:wc:ra_dav:version-url V 40 -/svn/!svn/ver/3885/trunk/app/controllers +/svn/!svn/ver/4040/trunk/app/controllers END issues_controller.rb K 25 svn:wc:ra_dav:version-url V 61 -/svn/!svn/ver/3766/trunk/app/controllers/issues_controller.rb +/svn/!svn/ver/4040/trunk/app/controllers/issues_controller.rb END queries_controller.rb K 25 @@ -33,6 +33,12 @@ V 63 /svn/!svn/ver/3798/trunk/app/controllers/watchers_controller.rb END +auto_completes_controller.rb +K 25 +svn:wc:ra_dav:version-url +V 69 +/svn/!svn/ver/3945/trunk/app/controllers/auto_completes_controller.rb +END my_controller.rb K 25 svn:wc:ra_dav:version-url @@ -51,11 +57,17 @@ V 62 /svn/!svn/ver/2941/trunk/app/controllers/welcome_controller.rb END +context_menus_controller.rb +K 25 +svn:wc:ra_dav:version-url +V 68 +/svn/!svn/ver/4006/trunk/app/controllers/context_menus_controller.rb +END journals_controller.rb K 25 svn:wc:ra_dav:version-url V 63 -/svn/!svn/ver/3185/trunk/app/controllers/journals_controller.rb +/svn/!svn/ver/4034/trunk/app/controllers/journals_controller.rb END workflows_controller.rb K 25 @@ -73,7 +85,7 @@ K 25 svn:wc:ra_dav:version-url V 62 -/svn/!svn/ver/3826/trunk/app/controllers/timelog_controller.rb +/svn/!svn/ver/3931/trunk/app/controllers/timelog_controller.rb END settings_controller.rb K 25 @@ -91,13 +103,25 @@ K 25 svn:wc:ra_dav:version-url V 60 -/svn/!svn/ver/3601/trunk/app/controllers/users_controller.rb +/svn/!svn/ver/3935/trunk/app/controllers/users_controller.rb +END +issue_moves_controller.rb +K 25 +svn:wc:ra_dav:version-url +V 66 +/svn/!svn/ver/3940/trunk/app/controllers/issue_moves_controller.rb END application_controller.rb K 25 svn:wc:ra_dav:version-url V 66 -/svn/!svn/ver/3827/trunk/app/controllers/application_controller.rb +/svn/!svn/ver/3949/trunk/app/controllers/application_controller.rb +END +ldap_auth_sources_controller.rb +K 25 +svn:wc:ra_dav:version-url +V 72 +/svn/!svn/ver/3744/trunk/app/controllers/ldap_auth_sources_controller.rb END auth_sources_controller.rb K 25 @@ -105,11 +129,11 @@ V 67 /svn/!svn/ver/3744/trunk/app/controllers/auth_sources_controller.rb END -ldap_auth_sources_controller.rb +previews_controller.rb K 25 svn:wc:ra_dav:version-url -V 72 -/svn/!svn/ver/3744/trunk/app/controllers/ldap_auth_sources_controller.rb +V 63 +/svn/!svn/ver/3946/trunk/app/controllers/previews_controller.rb END search_controller.rb K 25 @@ -123,23 +147,23 @@ V 63 /svn/!svn/ver/3528/trunk/app/controllers/messages_controller.rb END +issue_relations_controller.rb +K 25 +svn:wc:ra_dav:version-url +V 70 +/svn/!svn/ver/3591/trunk/app/controllers/issue_relations_controller.rb +END versions_controller.rb K 25 svn:wc:ra_dav:version-url V 63 /svn/!svn/ver/3866/trunk/app/controllers/versions_controller.rb END -issue_relations_controller.rb -K 25 -svn:wc:ra_dav:version-url -V 70 -/svn/!svn/ver/3591/trunk/app/controllers/issue_relations_controller.rb -END boards_controller.rb K 25 svn:wc:ra_dav:version-url V 61 -/svn/!svn/ver/3535/trunk/app/controllers/boards_controller.rb +/svn/!svn/ver/3942/trunk/app/controllers/boards_controller.rb END attachments_controller.rb K 25 @@ -163,7 +187,7 @@ K 25 svn:wc:ra_dav:version-url V 61 -/svn/!svn/ver/3735/trunk/app/controllers/groups_controller.rb +/svn/!svn/ver/3934/trunk/app/controllers/groups_controller.rb END issue_statuses_controller.rb K 25 @@ -205,13 +229,13 @@ K 25 svn:wc:ra_dav:version-url V 62 -/svn/!svn/ver/3572/trunk/app/controllers/account_controller.rb +/svn/!svn/ver/3906/trunk/app/controllers/account_controller.rb END calendars_controller.rb K 25 svn:wc:ra_dav:version-url V 64 -/svn/!svn/ver/3732/trunk/app/controllers/calendars_controller.rb +/svn/!svn/ver/3944/trunk/app/controllers/calendars_controller.rb END issue_categories_controller.rb K 25 @@ -223,7 +247,7 @@ K 25 svn:wc:ra_dav:version-url V 61 -/svn/!svn/ver/3885/trunk/app/controllers/gantts_controller.rb +/svn/!svn/ver/3943/trunk/app/controllers/gantts_controller.rb END documents_controller.rb K 25 diff -r 9cc62779c13a -r 1d32c0a0efbf app/controllers/.svn/entries --- a/app/controllers/.svn/entries Wed Jul 28 12:47:17 2010 +0100 +++ b/app/controllers/.svn/entries Wed Aug 25 16:30:24 2010 +0100 @@ -1,15 +1,15 @@ 10 dir -3892 +4040 http://redmine.rubyforge.org/svn/trunk/app/controllers http://redmine.rubyforge.org/svn -2010-07-25T11:43:19.719822Z -3885 -jplang +2010-08-25T14:59:16.537407Z +4040 +edavis10 @@ -32,10 +32,10 @@ -2010-07-23T14:49:44.744097Z -5133e0b07e1f41f9250490b55707fbe0 -2010-06-05T03:52:59.030840Z -3766 +2010-08-25T15:27:53.046491Z +b9533adcb710aba95a687413ccc4cb59 +2010-08-25T14:59:16.537407Z +4040 edavis10 has-props @@ -58,7 +58,7 @@ -19569 +12932 queries_controller.rb file @@ -196,6 +196,40 @@ 3234 +auto_completes_controller.rb +file + + + + +2010-08-25T15:27:53.046491Z +8cd8e21323a4500a05c26e4ebdde1812 +2010-08-17T15:03:58.074505Z +3945 +edavis10 + + + + + + + + + + + + + + + + + + + + + +665 + my_controller.rb file @@ -298,17 +332,51 @@ 1091 +context_menus_controller.rb +file + + + + +2010-08-25T15:27:53.046491Z +e3ff02e7eaab56330be93b78dd81da7e +2010-08-19T18:16:54.064576Z +4006 +edavis10 + + + + + + + + + + + + + + + + + + + + + +1486 + journals_controller.rb file -2010-07-23T14:49:44.748131Z -85307fc40948cd0550fe2b79c519b10c -2009-12-18T14:22:18.271694Z -3185 -jplang +2010-08-25T15:27:53.046491Z +deb481ad0f862b9305b79f292d59fb51 +2010-08-23T15:04:36.844654Z +4034 +edavis10 has-props @@ -330,7 +398,7 @@ -1620 +3439 workflows_controller.rb file @@ -406,10 +474,10 @@ -2010-07-23T14:49:44.748131Z -3d719c6d956fedad17246b77f49542a2 -2010-07-01T18:49:25.466495Z -3826 +2010-08-25T15:27:53.046491Z +72160c2e91f17c8fef5ca833a8556463 +2010-08-09T23:15:32.315886Z +3931 edavis10 has-props @@ -432,7 +500,7 @@ -14346 +14494 settings_controller.rb file @@ -508,33 +576,67 @@ -2010-07-23T14:49:44.748131Z -6ba548afb15199d8d3d9e9b1c3ee70a1 -2010-03-18T15:49:11.191178Z -3601 +2010-08-25T15:27:53.046491Z +c77cc4f901292e6f8c921656d0503467 +2010-08-10T23:07:44.448600Z +3935 +jbbarth +has-props + + + + + + + + + + + + + + + + + + + + +5939 + +issue_moves_controller.rb +file + + + + +2010-08-25T15:27:53.046491Z +41a4ff4e0c4fab3e134a9bfa8136323a +2010-08-13T14:59:04.653474Z +3940 edavis10 -has-props - - - - - - - - - - - - - - - - - - - - -5803 + + + + + + + + + + + + + + + + + + + + + +2290 application_controller.rb file @@ -542,10 +644,10 @@ -2010-07-23T14:49:44.748131Z -e6b3d697b6459396ccb84693d3bb8115 -2010-07-05T18:00:50.379659Z -3827 +2010-08-25T15:27:53.046491Z +90bc300210100504fc73d1a96ff6f002 +2010-08-19T01:01:35.693458Z +3949 edavis10 has-props @@ -568,7 +670,41 @@ -11860 +13301 + +ldap_auth_sources_controller.rb +file + + + + +2010-07-23T14:49:44.748131Z +5f7b9cb2e9c8a60db58ea0833cf481c5 +2010-05-23T03:16:31.304135Z +3744 +edavis10 + + + + + + + + + + + + + + + + + + + + + +917 auth_sources_controller.rb file @@ -604,16 +740,16 @@ 2542 -ldap_auth_sources_controller.rb +previews_controller.rb file -2010-07-23T14:49:44.748131Z -5f7b9cb2e9c8a60db58ea0833cf481c5 -2010-05-23T03:16:31.304135Z -3744 +2010-08-25T15:27:53.046491Z +1407f3993f625a9fa14fa0e208c3df4f +2010-08-18T15:01:35.032314Z +3946 edavis10 @@ -636,7 +772,7 @@ -917 +835 search_controller.rb file @@ -706,6 +842,40 @@ 5824 +issue_relations_controller.rb +file + + + + +2010-07-23T14:49:44.752129Z +c57448661f2eaab81c76f23d7f51e0ab +2010-03-16T15:17:47.586688Z +3591 +edavis10 +has-props + + + + + + + + + + + + + + + + + + + + +2220 + versions_controller.rb file @@ -740,16 +910,16 @@ 3951 -issue_relations_controller.rb +boards_controller.rb file -2010-07-23T14:49:44.752129Z -c57448661f2eaab81c76f23d7f51e0ab -2010-03-16T15:17:47.586688Z -3591 +2010-08-25T15:27:53.046491Z +52638c82da929081099076d5aa0f9212 +2010-08-16T23:39:27.396462Z +3942 edavis10 has-props @@ -772,41 +942,7 @@ -2220 - -boards_controller.rb -file - - - - -2010-07-23T14:49:44.752129Z -751dad23faa6654e0e414aaf528c87ea -2010-03-04T05:33:40.554127Z -3535 -edavis10 -has-props - - - - - - - - - - - - - - - - - - - - -3462 +3494 attachments_controller.rb file @@ -916,11 +1052,11 @@ -2010-07-23T14:49:44.752129Z -cf223aa9e6dd1a1cd56cf1458976fdd2 -2010-05-09T11:31:39.850182Z -3735 -jplang +2010-08-25T15:27:53.046491Z +77209ec52ddcefafb0bfbb76195167ec +2010-08-10T22:37:00.826946Z +3934 +jbbarth has-props @@ -942,7 +1078,7 @@ -4971 +5214 issue_statuses_controller.rb file @@ -1154,11 +1290,11 @@ -2010-07-23T14:49:44.752129Z -0c7bb434edcc940fe9c77a382e42e63a -2010-03-12T19:22:11.176513Z -3572 -jplang +2010-08-25T15:27:53.046491Z +d0ff6be1c749b765d9d64bef64c27537 +2010-08-03T15:26:50.842290Z +3906 +edavis10 has-props @@ -1180,7 +1316,7 @@ -9092 +8962 calendars_controller.rb file @@ -1188,10 +1324,10 @@ -2010-07-23T14:49:44.752129Z -880833e2ed52f5069a8080d38ca1fcea -2010-05-03T16:02:37.599964Z -3732 +2010-08-25T15:27:53.050542Z +2395bf1ef8a03eae77dbc9c9956de2e4 +2010-08-16T23:56:42.601754Z +3944 edavis10 @@ -1214,7 +1350,7 @@ -1285 +1307 issue_categories_controller.rb file @@ -1256,33 +1392,33 @@ -2010-07-28T11:07:47.104150Z -43f8fc731af8112723123c97ea49987e -2010-07-25T11:43:19.719822Z -3885 -jplang - - - - - - - - - - - - - - - - - - - - - -2320 +2010-08-25T15:27:53.050542Z +8214d623a887f0d2dd8801af05426a35 +2010-08-16T23:56:37.048249Z +3943 +edavis10 + + + + + + + + + + + + + + + + + + + + + +2339 documents_controller.rb file diff -r 9cc62779c13a -r 1d32c0a0efbf app/controllers/.svn/text-base/account_controller.rb.svn-base --- a/app/controllers/.svn/text-base/account_controller.rb.svn-base Wed Jul 28 12:47:17 2010 +0100 +++ b/app/controllers/.svn/text-base/account_controller.rb.svn-base Wed Aug 25 16:30:24 2010 +0100 @@ -83,9 +83,9 @@ else @user = User.new(params[:user]) @user.admin = false - @user.status = User::STATUS_REGISTERED + @user.register if session[:auth_source_registration] - @user.status = User::STATUS_ACTIVE + @user.activate @user.login = session[:auth_source_registration][:login] @user.auth_source_id = session[:auth_source_registration][:auth_source_id] if @user.save @@ -116,8 +116,8 @@ token = Token.find_by_action_and_value('register', params[:token]) redirect_to(home_url) && return unless token and !token.expired? user = token.user - redirect_to(home_url) && return unless user.status == User::STATUS_REGISTERED - user.status = User::STATUS_ACTIVE + redirect_to(home_url) && return unless user.registered? + user.activate if user.save token.destroy flash[:notice] = l(:notice_account_activated) @@ -170,7 +170,7 @@ 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 + user.register case Setting.self_registration when '1' @@ -241,7 +241,7 @@ # Pass a block for behavior when a user fails to save def register_automatically(user, &block) # Automatic activation - user.status = User::STATUS_ACTIVE + user.activate user.last_login_on = Time.now if user.save self.logged_user = user diff -r 9cc62779c13a -r 1d32c0a0efbf app/controllers/.svn/text-base/application_controller.rb.svn-base --- a/app/controllers/.svn/text-base/application_controller.rb.svn-base Wed Jul 28 12:47:17 2010 +0100 +++ b/app/controllers/.svn/text-base/application_controller.rb.svn-base Wed Aug 25 16:30:24 2010 +0100 @@ -201,7 +201,23 @@ def self.model_object(model) write_inheritable_attribute('model_object', model) end - + + # Filter for bulk issue operations + def find_issues + @issues = Issue.find_all_by_id(params[:id] || params[:ids]) + raise ActiveRecord::RecordNotFound if @issues.empty? + projects = @issues.collect(&:project).compact.uniq + if projects.size == 1 + @project = projects.first + else + # TODO: let users bulk edit/move/destroy issues from different projects + render_error 'Can not bulk edit/move/destroy issues from different projects' + return false + end + rescue ActiveRecord::RecordNotFound + render_404 + end + # make sure that the user is a member of the project (or admin) if project is private # used as a before_filter for actions that do not require any particular permission on the project def check_project_privacy @@ -218,6 +234,10 @@ end end + def back_url + params[:back_url] || request.env['HTTP_REFERER'] + end + def redirect_back_or_default(default) back_url = CGI.unescape(params[:back_url].to_s) if !back_url.blank? @@ -238,7 +258,7 @@ def render_403 @project = nil respond_to do |format| - format.html { render :template => "common/403", :layout => (request.xhr? ? false : 'base'), :status => 403 } + format.html { render :template => "common/403", :layout => use_layout, :status => 403 } format.atom { head 403 } format.xml { head 403 } format.js { head 403 } @@ -249,7 +269,7 @@ def render_404 respond_to do |format| - format.html { render :template => "common/404", :layout => !request.xhr?, :status => 404 } + format.html { render :template => "common/404", :layout => use_layout, :status => 404 } format.atom { head 404 } format.xml { head 404 } format.js { head 404 } @@ -262,7 +282,7 @@ respond_to do |format| format.html { flash.now[:error] = msg - render :text => '', :layout => !request.xhr?, :status => 500 + render :text => '', :layout => use_layout, :status => 500 } format.atom { head 500 } format.xml { head 500 } @@ -270,6 +290,13 @@ format.json { head 500 } end end + + # Picks which layout to use based on the request + # + # @return [boolean, string] name of the layout to use or false for no layout + def use_layout + request.xhr? ? false : 'base' + end def invalid_authenticity_token if api_request? @@ -345,6 +372,21 @@ flash[:warning] = l(:warning_attachments_not_saved, obj.unsaved_attachments.size) if obj.unsaved_attachments.present? end + # Sets the `flash` notice or error based the number of issues that did not save + # + # @param [Array, Issue] issues all of the saved and unsaved Issues + # @param [Array, Integer] unsaved_issue_ids the issue ids that were not saved + def set_flash_from_bulk_issue_save(issues, unsaved_issue_ids) + if unsaved_issue_ids.empty? + flash[:notice] = l(:notice_successful_update) unless issues.empty? + else + flash[:error] = l(:notice_failed_to_save_issues, + :count => unsaved_issue_ids.size, + :total => issues.size, + :ids => '#' + unsaved_issue_ids.join(', #')) + end + end + # Rescues an invalid query statement. Just in case... def query_statement_invalid(exception) logger.error "Query::StatementInvalid: #{exception.message}" if logger diff -r 9cc62779c13a -r 1d32c0a0efbf app/controllers/.svn/text-base/auto_completes_controller.rb.svn-base --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/app/controllers/.svn/text-base/auto_completes_controller.rb.svn-base Wed Aug 25 16:30:24 2010 +0100 @@ -0,0 +1,25 @@ +class AutoCompletesController < ApplicationController + before_filter :find_project + + def issues + @issues = [] + q = params[:q].to_s + if q.match(/^\d+$/) + @issues << @project.issues.visible.find_by_id(q.to_i) + end + unless q.blank? + @issues += @project.issues.visible.find(:all, :conditions => ["LOWER(#{Issue.table_name}.subject) LIKE ?", "%#{q.downcase}%"], :limit => 10) + end + render :layout => false + end + + private + + def find_project + project_id = (params[:issue] && params[:issue][:project_id]) || params[:project_id] + @project = Project.find(project_id) + rescue ActiveRecord::RecordNotFound + render_404 + end + +end diff -r 9cc62779c13a -r 1d32c0a0efbf app/controllers/.svn/text-base/boards_controller.rb.svn-base --- a/app/controllers/.svn/text-base/boards_controller.rb.svn-base Wed Jul 28 12:47:17 2010 +0100 +++ b/app/controllers/.svn/text-base/boards_controller.rb.svn-base Wed Aug 25 16:30:24 2010 +0100 @@ -18,6 +18,7 @@ class BoardsController < ApplicationController default_search_scope :messages before_filter :find_project, :find_board_if_available, :authorize + accept_key_auth :index, :show helper :messages include MessagesHelper diff -r 9cc62779c13a -r 1d32c0a0efbf app/controllers/.svn/text-base/calendars_controller.rb.svn-base --- a/app/controllers/.svn/text-base/calendars_controller.rb.svn-base Wed Jul 28 12:47:17 2010 +0100 +++ b/app/controllers/.svn/text-base/calendars_controller.rb.svn-base Wed Aug 25 16:30:24 2010 +0100 @@ -1,4 +1,5 @@ class CalendarsController < ApplicationController + menu_item :calendar before_filter :find_optional_project rescue_from Query::StatementInvalid, :with => :query_statement_invalid diff -r 9cc62779c13a -r 1d32c0a0efbf app/controllers/.svn/text-base/context_menus_controller.rb.svn-base --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/app/controllers/.svn/text-base/context_menus_controller.rb.svn-base Wed Aug 25 16:30:24 2010 +0100 @@ -0,0 +1,33 @@ +class ContextMenusController < ApplicationController + helper :watchers + + def issues + @issues = Issue.find_all_by_id(params[:ids], :include => :project) + if (@issues.size == 1) + @issue = @issues.first + @allowed_statuses = @issue.new_statuses_allowed_to(User.current) + end + projects = @issues.collect(&:project).compact.uniq + @project = projects.first if projects.size == 1 + + @can = {:edit => (@project && User.current.allowed_to?(:edit_issues, @project)), + :log_time => (@project && User.current.allowed_to?(:log_time, @project)), + :update => (@project && (User.current.allowed_to?(:edit_issues, @project) || (User.current.allowed_to?(:change_status, @project) && @allowed_statuses && !@allowed_statuses.empty?))), + :move => (@project && User.current.allowed_to?(:move_issues, @project)), + :copy => (@issue && @project.trackers.include?(@issue.tracker) && User.current.allowed_to?(:add_issues, @project)), + :delete => (@project && User.current.allowed_to?(:delete_issues, @project)) + } + if @project + @assignables = @project.assignable_users + @assignables << @issue.assigned_to if @issue && @issue.assigned_to && !@assignables.include?(@issue.assigned_to) + @trackers = @project.trackers + end + + @priorities = IssuePriority.all.reverse + @statuses = IssueStatus.find(:all, :order => 'position') + @back = back_url + + render :layout => false + end + +end diff -r 9cc62779c13a -r 1d32c0a0efbf app/controllers/.svn/text-base/gantts_controller.rb.svn-base --- a/app/controllers/.svn/text-base/gantts_controller.rb.svn-base Wed Jul 28 12:47:17 2010 +0100 +++ b/app/controllers/.svn/text-base/gantts_controller.rb.svn-base Wed Aug 25 16:30:24 2010 +0100 @@ -1,4 +1,5 @@ class GanttsController < ApplicationController + menu_item :gantt before_filter :find_optional_project rescue_from Query::StatementInvalid, :with => :query_statement_invalid diff -r 9cc62779c13a -r 1d32c0a0efbf app/controllers/.svn/text-base/groups_controller.rb.svn-base --- a/app/controllers/.svn/text-base/groups_controller.rb.svn-base Wed Jul 28 12:47:17 2010 +0100 +++ b/app/controllers/.svn/text-base/groups_controller.rb.svn-base Wed Aug 25 16:30:24 2010 +0100 @@ -141,14 +141,22 @@ @membership = Member.edit_membership(params[:membership_id], params[:membership], @group) @membership.save if request.post? respond_to do |format| - format.html { redirect_to :controller => 'groups', :action => 'edit', :id => @group, :tab => 'memberships' } - format.js { - render(:update) {|page| - page.replace_html "tab-content-memberships", :partial => 'groups/memberships' - page.visual_effect(:highlight, "member-#{@membership.id}") - } - } - end + if @membership.valid? + format.html { redirect_to :controller => 'groups', :action => 'edit', :id => @group, :tab => 'memberships' } + format.js { + render(:update) {|page| + page.replace_html "tab-content-memberships", :partial => 'groups/memberships' + page.visual_effect(:highlight, "member-#{@membership.id}") + } + } + else + format.js { + render(:update) {|page| + page.alert(l(:notice_failed_to_save_members, :errors => @membership.errors.full_messages.join(', '))) + } + } + end + end end def destroy_membership diff -r 9cc62779c13a -r 1d32c0a0efbf app/controllers/.svn/text-base/issue_moves_controller.rb.svn-base --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/app/controllers/.svn/text-base/issue_moves_controller.rb.svn-base Wed Aug 25 16:30:24 2010 +0100 @@ -0,0 +1,65 @@ +class IssueMovesController < ApplicationController + default_search_scope :issues + before_filter :find_issues + before_filter :authorize + + def new + prepare_for_issue_move + render :layout => false if request.xhr? + end + + def create + prepare_for_issue_move + + if request.post? + new_tracker = params[:new_tracker_id].blank? ? nil : @target_project.trackers.find_by_id(params[:new_tracker_id]) + unsaved_issue_ids = [] + moved_issues = [] + @issues.each do |issue| + issue.reload + issue.init_journal(User.current) + call_hook(:controller_issues_move_before_save, { :params => params, :issue => issue, :target_project => @target_project, :copy => !!@copy }) + if r = issue.move_to_project(@target_project, new_tracker, {:copy => @copy, :attributes => extract_changed_attributes_for_move(params)}) + moved_issues << r + else + unsaved_issue_ids << issue.id + end + end + set_flash_from_bulk_issue_save(@issues, unsaved_issue_ids) + + if params[:follow] + if @issues.size == 1 && moved_issues.size == 1 + redirect_to :controller => 'issues', :action => 'show', :id => moved_issues.first + else + redirect_to :controller => 'issues', :action => 'index', :project_id => (@target_project || @project) + end + else + redirect_to :controller => 'issues', :action => 'index', :project_id => @project + end + return + end + end + + private + + def prepare_for_issue_move + @issues.sort! + @copy = params[:copy_options] && params[:copy_options][:copy] + @allowed_projects = Issue.allowed_target_projects_on_move + @target_project = @allowed_projects.detect {|p| p.id.to_s == params[:new_project_id]} if params[:new_project_id] + @target_project ||= @project + @trackers = @target_project.trackers + @available_statuses = Workflow.available_statuses(@project) + end + + def extract_changed_attributes_for_move(params) + changed_attributes = {} + [:assigned_to_id, :status_id, :start_date, :due_date].each do |valid_attribute| + unless params[valid_attribute].blank? + changed_attributes[valid_attribute] = (params[valid_attribute] == 'none' ? nil : params[valid_attribute]) + end + end + changed_attributes + end + +end diff -r 9cc62779c13a -r 1d32c0a0efbf app/controllers/.svn/text-base/issues_controller.rb.svn-base --- a/app/controllers/.svn/text-base/issues_controller.rb.svn-base Wed Jul 28 12:47:17 2010 +0100 +++ b/app/controllers/.svn/text-base/issues_controller.rb.svn-base Wed Aug 25 16:30:24 2010 +0100 @@ -19,14 +19,14 @@ menu_item :new_issue, :only => [:new, :create] default_search_scope :issues - before_filter :find_issue, :only => [:show, :edit, :update, :reply] - before_filter :find_issues, :only => [:bulk_edit, :move, :destroy] - before_filter :find_project, :only => [:new, :create, :update_form, :preview, :auto_complete] - before_filter :authorize, :except => [:index, :changes, :preview, :context_menu] - before_filter :find_optional_project, :only => [:index, :changes] + before_filter :find_issue, :only => [:show, :edit, :update] + before_filter :find_issues, :only => [:bulk_edit, :bulk_update, :move, :perform_move, :destroy] + before_filter :find_project, :only => [:new, :create] + before_filter :authorize, :except => [:index] + before_filter :find_optional_project, :only => [:index] before_filter :check_for_default_issue_status, :only => [:new, :create] before_filter :build_new_issue_from_params, :only => [:new, :create] - accept_key_auth :index, :show, :changes + accept_key_auth :index, :show rescue_from Query::StatementInvalid, :with => :query_statement_invalid @@ -54,6 +54,7 @@ :render => { :nothing => true, :status => :method_not_allowed } verify :method => :post, :only => :create, :render => {:nothing => true, :status => :method_not_allowed } + verify :method => :post, :only => :bulk_update, :render => {:nothing => true, :status => :method_not_allowed } verify :method => :put, :only => :update, :render => {:nothing => true, :status => :method_not_allowed } def index @@ -95,21 +96,6 @@ render_404 end - def changes - retrieve_query - sort_init 'id', 'desc' - sort_update(@query.sortable_columns) - - if @query.valid? - @journals = @query.journals(:order => "#{Journal.table_name}.created_on DESC", - :limit => 25) - end - @title = (@project ? @project.name : Setting.app_title) + ": " + (@query.new_record? ? l(:label_changes_details) : @query.name) - render :layout => false, :content_type => 'application/atom+xml' - rescue ActiveRecord::RecordNotFound - render_404 - end - def show @journals = @issue.journals.find(:all, :include => [:user, :details], :order => "#{Journal.table_name}.created_on ASC") @journals.each_with_index {|j,i| j.indice = i+1} @@ -124,7 +110,7 @@ format.html { render :template => 'issues/show.rhtml' } format.xml { render :layout => false } format.json { render :text => @issue.to_json, :layout => false } - format.atom { render :action => 'changes', :layout => false, :content_type => 'application/atom+xml' } + format.atom { render :template => 'journals/index', :layout => false, :content_type => 'application/atom+xml' } format.pdf { send_data(issue_to_pdf(@issue), :type => 'application/pdf', :filename => "#{@project.identifier}-#{@issue.id}.pdf") } end end @@ -132,7 +118,10 @@ # Add a new issue # The new issue will be created from an existing one if copy_from parameter is given def new - render :action => 'new', :layout => !request.xhr? + respond_to do |format| + format.html { render :action => 'new', :layout => !request.xhr? } + format.js { render :partial => 'attributes' } + end end def create @@ -200,98 +189,30 @@ end end - def reply - journal = Journal.find(params[:journal_id]) if params[:journal_id] - if journal - user = journal.user - text = journal.notes - else - user = @issue.author - text = @issue.description - end - # Replaces pre blocks with [...] - text = text.to_s.strip.gsub(%r{
((.|\s)*?)}m, '[...]') - content = "#{ll(Setting.default_language, :text_user_wrote, user)}\n> " - content << text.gsub(/(\r?\n|\r\n?)/, "\n> ") + "\n\n" - - render(:update) { |page| - page.<< "$('notes').value = \"#{escape_javascript content}\";" - page.show 'update' - page << "Form.Element.focus('notes');" - page << "Element.scrollTo('update');" - page << "$('notes').scrollTop = $('notes').scrollHeight - $('notes').clientHeight;" - } - end - # Bulk edit a set of issues def bulk_edit @issues.sort! - if request.post? - attributes = (params[:issue] || {}).reject {|k,v| v.blank?} - attributes.keys.each {|k| attributes[k] = '' if attributes[k] == 'none'} - attributes[:custom_field_values].reject! {|k,v| v.blank?} if attributes[:custom_field_values] - - unsaved_issue_ids = [] - @issues.each do |issue| - issue.reload - journal = issue.init_journal(User.current, params[:notes]) - issue.safe_attributes = attributes - call_hook(:controller_issues_bulk_edit_before_save, { :params => params, :issue => issue }) - unless issue.save - # Keep unsaved issue ids to display them in flash error - unsaved_issue_ids << issue.id - end - end - set_flash_from_bulk_issue_save(@issues, unsaved_issue_ids) - redirect_back_or_default({:controller => 'issues', :action => 'index', :project_id => @project}) - return - end @available_statuses = Workflow.available_statuses(@project) @custom_fields = @project.all_issue_custom_fields end - def move + def bulk_update @issues.sort! - @copy = params[:copy_options] && params[:copy_options][:copy] - @allowed_projects = Issue.allowed_target_projects_on_move - @target_project = @allowed_projects.detect {|p| p.id.to_s == params[:new_project_id]} if params[:new_project_id] - @target_project ||= @project - @trackers = @target_project.trackers - @available_statuses = Workflow.available_statuses(@project) - if request.post? - new_tracker = params[:new_tracker_id].blank? ? nil : @target_project.trackers.find_by_id(params[:new_tracker_id]) - unsaved_issue_ids = [] - moved_issues = [] - @issues.each do |issue| - issue.reload - changed_attributes = {} - [:assigned_to_id, :status_id, :start_date, :due_date].each do |valid_attribute| - unless params[valid_attribute].blank? - changed_attributes[valid_attribute] = (params[valid_attribute] == 'none' ? nil : params[valid_attribute]) - end - end - issue.init_journal(User.current) - call_hook(:controller_issues_move_before_save, { :params => params, :issue => issue, :target_project => @target_project, :copy => !!@copy }) - if r = issue.move_to_project(@target_project, new_tracker, {:copy => @copy, :attributes => changed_attributes}) - moved_issues << r - else - unsaved_issue_ids << issue.id - end + attributes = parse_params_for_bulk_issue_attributes(params) + + unsaved_issue_ids = [] + @issues.each do |issue| + issue.reload + journal = issue.init_journal(User.current, params[:notes]) + issue.safe_attributes = attributes + call_hook(:controller_issues_bulk_edit_before_save, { :params => params, :issue => issue }) + unless issue.save + # Keep unsaved issue ids to display them in flash error + unsaved_issue_ids << issue.id end - set_flash_from_bulk_issue_save(@issues, unsaved_issue_ids) - - if params[:follow] - if @issues.size == 1 && moved_issues.size == 1 - redirect_to :controller => 'issues', :action => 'show', :id => moved_issues.first - else - redirect_to :controller => 'issues', :action => 'index', :project_id => (@target_project || @project) - end - else - redirect_to :controller => 'issues', :action => 'index', :project_id => @project - end - return end - render :layout => false if request.xhr? + set_flash_from_bulk_issue_save(@issues, unsaved_issue_ids) + redirect_back_or_default({:controller => 'issues', :action => 'index', :project_id => @project}) end def destroy @@ -324,77 +245,7 @@ format.json { head :ok } end end - - def context_menu - @issues = Issue.find_all_by_id(params[:ids], :include => :project) - if (@issues.size == 1) - @issue = @issues.first - @allowed_statuses = @issue.new_statuses_allowed_to(User.current) - end - projects = @issues.collect(&:project).compact.uniq - @project = projects.first if projects.size == 1 - @can = {:edit => (@project && User.current.allowed_to?(:edit_issues, @project)), - :log_time => (@project && User.current.allowed_to?(:log_time, @project)), - :update => (@project && (User.current.allowed_to?(:edit_issues, @project) || (User.current.allowed_to?(:change_status, @project) && @allowed_statuses && !@allowed_statuses.empty?))), - :move => (@project && User.current.allowed_to?(:move_issues, @project)), - :copy => (@issue && @project.trackers.include?(@issue.tracker) && User.current.allowed_to?(:add_issues, @project)), - :delete => (@project && User.current.allowed_to?(:delete_issues, @project)) - } - if @project - @assignables = @project.assignable_users - @assignables << @issue.assigned_to if @issue && @issue.assigned_to && !@assignables.include?(@issue.assigned_to) - @trackers = @project.trackers - end - - @priorities = IssuePriority.all.reverse - @statuses = IssueStatus.find(:all, :order => 'position') - @back = params[:back_url] || request.env['HTTP_REFERER'] - - render :layout => false - end - - def update_form - if params[:id].blank? - @issue = Issue.new - @issue.project = @project - else - @issue = @project.issues.visible.find(params[:id]) - end - @issue.attributes = params[:issue] - @allowed_statuses = ([@issue.status] + @issue.status.find_new_statuses_allowed_to(User.current.roles_for_project(@project), @issue.tracker)).uniq - @priorities = IssuePriority.all - - render :partial => 'attributes' - end - - def preview - @issue = @project.issues.find_by_id(params[:id]) unless params[:id].blank? - if @issue - @attachements = @issue.attachments - @description = params[:issue] && params[:issue][:description] - if @description && @description.gsub(/(\r?\n|\n\r?)/, "\n") == @issue.description.to_s.gsub(/(\r?\n|\n\r?)/, "\n") - @description = nil - end - @notes = params[:notes] - else - @description = (params[:issue] ? params[:issue][:description] : nil) - end - render :layout => false - end - - def auto_complete - @issues = [] - q = params[:q].to_s - if q.match(/^\d+$/) - @issues << @project.issues.visible.find_by_id(q.to_i) - end - unless q.blank? - @issues += @project.issues.visible.find(:all, :conditions => ["LOWER(#{Issue.table_name}.subject) LIKE ?", "%#{q.downcase}%"], :limit => 10) - end - render :layout => false - end - private def find_issue @issue = Issue.find(params[:id], :include => [:project, :tracker, :status, :author, :priority, :category]) @@ -403,22 +254,6 @@ render_404 end - # Filter for bulk operations - def find_issues - @issues = Issue.find_all_by_id(params[:id] || params[:ids]) - raise ActiveRecord::RecordNotFound if @issues.empty? - projects = @issues.collect(&:project).compact.uniq - if projects.size == 1 - @project = projects.first - else - # TODO: let users bulk edit/move/destroy issues from different projects - render_error 'Can not bulk edit/move/destroy issues from different projects' - return false - end - rescue ActiveRecord::RecordNotFound - render_404 - end - def find_project project_id = (params[:issue] && params[:issue][:project_id]) || params[:project_id] @project = Project.find(project_id) @@ -449,8 +284,14 @@ # TODO: Refactor, lots of extra code in here def build_new_issue_from_params - @issue = Issue.new - @issue.copy_from(params[:copy_from]) if params[:copy_from] + if params[:id].blank? + @issue = Issue.new + @issue.copy_from(params[:copy_from]) if params[:copy_from] + @issue.project = @project + else + @issue = @project.issues.visible.find(params[:id]) + end + @issue.project = @project # Tracker must be set before custom field values @issue.tracker ||= @project.trackers.find((params[:issue] && params[:issue][:tracker_id]) || params[:tracker_id] || :first) @@ -468,21 +309,17 @@ @allowed_statuses = @issue.new_statuses_allowed_to(User.current, true) end - def set_flash_from_bulk_issue_save(issues, unsaved_issue_ids) - if unsaved_issue_ids.empty? - flash[:notice] = l(:notice_successful_update) unless issues.empty? - else - flash[:error] = l(:notice_failed_to_save_issues, - :count => unsaved_issue_ids.size, - :total => issues.size, - :ids => '#' + unsaved_issue_ids.join(', #')) - end - end - def check_for_default_issue_status if IssueStatus.default.nil? render_error l(:error_no_default_issue_status) return false end end + + def parse_params_for_bulk_issue_attributes(params) + attributes = (params[:issue] || {}).reject {|k,v| v.blank?} + attributes.keys.each {|k| attributes[k] = '' if attributes[k] == 'none'} + attributes[:custom_field_values].reject! {|k,v| v.blank?} if attributes[:custom_field_values] + attributes + end end diff -r 9cc62779c13a -r 1d32c0a0efbf app/controllers/.svn/text-base/journals_controller.rb.svn-base --- a/app/controllers/.svn/text-base/journals_controller.rb.svn-base Wed Jul 28 12:47:17 2010 +0100 +++ b/app/controllers/.svn/text-base/journals_controller.rb.svn-base Wed Aug 25 16:30:24 2010 +0100 @@ -16,7 +16,54 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. class JournalsController < ApplicationController - before_filter :find_journal + before_filter :find_journal, :only => [:edit] + before_filter :find_issue, :only => [:new] + before_filter :find_optional_project, :only => [:index] + accept_key_auth :index + + helper :issues + helper :queries + include QueriesHelper + helper :sort + include SortHelper + + def index + retrieve_query + sort_init 'id', 'desc' + sort_update(@query.sortable_columns) + + if @query.valid? + @journals = @query.journals(:order => "#{Journal.table_name}.created_on DESC", + :limit => 25) + end + @title = (@project ? @project.name : Setting.app_title) + ": " + (@query.new_record? ? l(:label_changes_details) : @query.name) + render :layout => false, :content_type => 'application/atom+xml' + rescue ActiveRecord::RecordNotFound + render_404 + end + + def new + journal = Journal.find(params[:journal_id]) if params[:journal_id] + if journal + user = journal.user + text = journal.notes + else + user = @issue.author + text = @issue.description + end + # Replaces pre blocks with [...] + text = text.to_s.strip.gsub(%r{
((.|\s)*?)}m, '[...]') + content = "#{ll(Setting.default_language, :text_user_wrote, user)}\n> " + content << text.gsub(/(\r?\n|\r\n?)/, "\n> ") + "\n\n" + + render(:update) { |page| + page.<< "$('notes').value = \"#{escape_javascript content}\";" + page.show 'update' + page << "Form.Element.focus('notes');" + page << "Element.scrollTo('update');" + page << "$('notes').scrollTop = $('notes').scrollHeight - $('notes').clientHeight;" + } + end def edit if request.post? @@ -38,4 +85,12 @@ rescue ActiveRecord::RecordNotFound render_404 end + + # TODO: duplicated in IssuesController + def find_issue + @issue = Issue.find(params[:id], :include => [:project, :tracker, :status, :author, :priority, :category]) + @project = @issue.project + rescue ActiveRecord::RecordNotFound + render_404 + end end diff -r 9cc62779c13a -r 1d32c0a0efbf app/controllers/.svn/text-base/previews_controller.rb.svn-base --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/app/controllers/.svn/text-base/previews_controller.rb.svn-base Wed Aug 25 16:30:24 2010 +0100 @@ -0,0 +1,28 @@ +class PreviewsController < ApplicationController + before_filter :find_project + + def issue + @issue = @project.issues.find_by_id(params[:id]) unless params[:id].blank? + if @issue + @attachements = @issue.attachments + @description = params[:issue] && params[:issue][:description] + if @description && @description.gsub(/(\r?\n|\n\r?)/, "\n") == @issue.description.to_s.gsub(/(\r?\n|\n\r?)/, "\n") + @description = nil + end + @notes = params[:notes] + else + @description = (params[:issue] ? params[:issue][:description] : nil) + end + render :layout => false + end + + private + + def find_project + project_id = (params[:issue] && params[:issue][:project_id]) || params[:project_id] + @project = Project.find(project_id) + rescue ActiveRecord::RecordNotFound + render_404 + end + +end diff -r 9cc62779c13a -r 1d32c0a0efbf app/controllers/.svn/text-base/timelog_controller.rb.svn-base --- a/app/controllers/.svn/text-base/timelog_controller.rb.svn-base Wed Jul 28 12:47:17 2010 +0100 +++ b/app/controllers/.svn/text-base/timelog_controller.rb.svn-base Wed Aug 25 16:30:24 2010 +0100 @@ -55,8 +55,7 @@ sql = "SELECT #{sql_select}, tyear, tmonth, tweek, spent_on, SUM(hours) AS hours" sql << " FROM #{TimeEntry.table_name}" - sql << " LEFT JOIN #{Issue.table_name} ON #{TimeEntry.table_name}.issue_id = #{Issue.table_name}.id" - sql << " LEFT JOIN #{Project.table_name} ON #{TimeEntry.table_name}.project_id = #{Project.table_name}.id" + sql << time_report_joins sql << " WHERE" sql << " (%s) AND" % sql_condition sql << " (spent_on BETWEEN '%s' AND '%s')" % [ActiveRecord::Base.connection.quoted_date(@from), ActiveRecord::Base.connection.quoted_date(@to)] @@ -314,4 +313,12 @@ call_hook(:controller_timelog_available_criterias, { :available_criterias => @available_criterias, :project => @project }) @available_criterias end + + def time_report_joins + sql = '' + sql << " LEFT JOIN #{Issue.table_name} ON #{TimeEntry.table_name}.issue_id = #{Issue.table_name}.id" + sql << " LEFT JOIN #{Project.table_name} ON #{TimeEntry.table_name}.project_id = #{Project.table_name}.id" + call_hook(:controller_timelog_time_report_joins, {:sql => sql} ) + sql + end end diff -r 9cc62779c13a -r 1d32c0a0efbf app/controllers/.svn/text-base/users_controller.rb.svn-base --- a/app/controllers/.svn/text-base/users_controller.rb.svn-base Wed Jul 28 12:47:17 2010 +0100 +++ b/app/controllers/.svn/text-base/users_controller.rb.svn-base Wed Aug 25 16:30:24 2010 +0100 @@ -53,10 +53,8 @@ @user = User.find(params[:id]) @custom_values = @user.custom_values - # show only public projects and private projects that the logged in user is also a member of - @memberships = @user.memberships.select do |membership| - membership.project.is_public? || (User.current.member_of?(membership.project)) - end + # show projects based on current user visibility + @memberships = @user.memberships.all(:conditions => Project.visible_by(User.current)) events = Redmine::Activity::Fetcher.new(User.current, :author => @user).events(nil, nil, :limit => 10) @events_by_day = events.group_by(&:event_date) @@ -123,14 +121,22 @@ @membership = Member.edit_membership(params[:membership_id], params[:membership], @user) @membership.save if request.post? respond_to do |format| - format.html { redirect_to :controller => 'users', :action => 'edit', :id => @user, :tab => 'memberships' } - format.js { - render(:update) {|page| - page.replace_html "tab-content-memberships", :partial => 'users/memberships' - page.visual_effect(:highlight, "member-#{@membership.id}") - } - } - end + if @membership.valid? + format.html { redirect_to :controller => 'users', :action => 'edit', :id => @user, :tab => 'memberships' } + format.js { + render(:update) {|page| + page.replace_html "tab-content-memberships", :partial => 'users/memberships' + page.visual_effect(:highlight, "member-#{@membership.id}") + } + } + else + format.js { + render(:update) {|page| + page.alert(l(:notice_failed_to_save_members, :errors => @membership.errors.full_messages.join(', '))) + } + } + end + end end def destroy_membership diff -r 9cc62779c13a -r 1d32c0a0efbf app/controllers/account_controller.rb --- a/app/controllers/account_controller.rb Wed Jul 28 12:47:17 2010 +0100 +++ b/app/controllers/account_controller.rb Wed Aug 25 16:30:24 2010 +0100 @@ -83,9 +83,9 @@ else @user = User.new(params[:user]) @user.admin = false - @user.status = User::STATUS_REGISTERED + @user.register if session[:auth_source_registration] - @user.status = User::STATUS_ACTIVE + @user.activate @user.login = session[:auth_source_registration][:login] @user.auth_source_id = session[:auth_source_registration][:auth_source_id] if @user.save @@ -116,8 +116,8 @@ token = Token.find_by_action_and_value('register', params[:token]) redirect_to(home_url) && return unless token and !token.expired? user = token.user - redirect_to(home_url) && return unless user.status == User::STATUS_REGISTERED - user.status = User::STATUS_ACTIVE + redirect_to(home_url) && return unless user.registered? + user.activate if user.save token.destroy flash[:notice] = l(:notice_account_activated) @@ -170,7 +170,7 @@ 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 + user.register case Setting.self_registration when '1' @@ -241,7 +241,7 @@ # Pass a block for behavior when a user fails to save def register_automatically(user, &block) # Automatic activation - user.status = User::STATUS_ACTIVE + user.activate user.last_login_on = Time.now if user.save self.logged_user = user diff -r 9cc62779c13a -r 1d32c0a0efbf app/controllers/application_controller.rb --- a/app/controllers/application_controller.rb Wed Jul 28 12:47:17 2010 +0100 +++ b/app/controllers/application_controller.rb Wed Aug 25 16:30:24 2010 +0100 @@ -201,7 +201,23 @@ def self.model_object(model) write_inheritable_attribute('model_object', model) end - + + # Filter for bulk issue operations + def find_issues + @issues = Issue.find_all_by_id(params[:id] || params[:ids]) + raise ActiveRecord::RecordNotFound if @issues.empty? + projects = @issues.collect(&:project).compact.uniq + if projects.size == 1 + @project = projects.first + else + # TODO: let users bulk edit/move/destroy issues from different projects + render_error 'Can not bulk edit/move/destroy issues from different projects' + return false + end + rescue ActiveRecord::RecordNotFound + render_404 + end + # make sure that the user is a member of the project (or admin) if project is private # used as a before_filter for actions that do not require any particular permission on the project def check_project_privacy @@ -218,6 +234,10 @@ end end + def back_url + params[:back_url] || request.env['HTTP_REFERER'] + end + def redirect_back_or_default(default) back_url = CGI.unescape(params[:back_url].to_s) if !back_url.blank? @@ -238,7 +258,7 @@ def render_403 @project = nil respond_to do |format| - format.html { render :template => "common/403", :layout => (request.xhr? ? false : 'base'), :status => 403 } + format.html { render :template => "common/403", :layout => use_layout, :status => 403 } format.atom { head 403 } format.xml { head 403 } format.js { head 403 } @@ -249,7 +269,7 @@ def render_404 respond_to do |format| - format.html { render :template => "common/404", :layout => !request.xhr?, :status => 404 } + format.html { render :template => "common/404", :layout => use_layout, :status => 404 } format.atom { head 404 } format.xml { head 404 } format.js { head 404 } @@ -262,7 +282,7 @@ respond_to do |format| format.html { flash.now[:error] = msg - render :text => '', :layout => !request.xhr?, :status => 500 + render :text => '', :layout => use_layout, :status => 500 } format.atom { head 500 } format.xml { head 500 } @@ -270,6 +290,13 @@ format.json { head 500 } end end + + # Picks which layout to use based on the request + # + # @return [boolean, string] name of the layout to use or false for no layout + def use_layout + request.xhr? ? false : 'base' + end def invalid_authenticity_token if api_request? @@ -345,6 +372,21 @@ flash[:warning] = l(:warning_attachments_not_saved, obj.unsaved_attachments.size) if obj.unsaved_attachments.present? end + # Sets the `flash` notice or error based the number of issues that did not save + # + # @param [Array, Issue] issues all of the saved and unsaved Issues + # @param [Array, Integer] unsaved_issue_ids the issue ids that were not saved + def set_flash_from_bulk_issue_save(issues, unsaved_issue_ids) + if unsaved_issue_ids.empty? + flash[:notice] = l(:notice_successful_update) unless issues.empty? + else + flash[:error] = l(:notice_failed_to_save_issues, + :count => unsaved_issue_ids.size, + :total => issues.size, + :ids => '#' + unsaved_issue_ids.join(', #')) + end + end + # Rescues an invalid query statement. Just in case... def query_statement_invalid(exception) logger.error "Query::StatementInvalid: #{exception.message}" if logger diff -r 9cc62779c13a -r 1d32c0a0efbf app/controllers/auto_completes_controller.rb --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/app/controllers/auto_completes_controller.rb Wed Aug 25 16:30:24 2010 +0100 @@ -0,0 +1,25 @@ +class AutoCompletesController < ApplicationController + before_filter :find_project + + def issues + @issues = [] + q = params[:q].to_s + if q.match(/^\d+$/) + @issues << @project.issues.visible.find_by_id(q.to_i) + end + unless q.blank? + @issues += @project.issues.visible.find(:all, :conditions => ["LOWER(#{Issue.table_name}.subject) LIKE ?", "%#{q.downcase}%"], :limit => 10) + end + render :layout => false + end + + private + + def find_project + project_id = (params[:issue] && params[:issue][:project_id]) || params[:project_id] + @project = Project.find(project_id) + rescue ActiveRecord::RecordNotFound + render_404 + end + +end diff -r 9cc62779c13a -r 1d32c0a0efbf app/controllers/boards_controller.rb --- a/app/controllers/boards_controller.rb Wed Jul 28 12:47:17 2010 +0100 +++ b/app/controllers/boards_controller.rb Wed Aug 25 16:30:24 2010 +0100 @@ -18,6 +18,7 @@ class BoardsController < ApplicationController default_search_scope :messages before_filter :find_project, :find_board_if_available, :authorize + accept_key_auth :index, :show helper :messages include MessagesHelper diff -r 9cc62779c13a -r 1d32c0a0efbf app/controllers/calendars_controller.rb --- a/app/controllers/calendars_controller.rb Wed Jul 28 12:47:17 2010 +0100 +++ b/app/controllers/calendars_controller.rb Wed Aug 25 16:30:24 2010 +0100 @@ -1,4 +1,5 @@ class CalendarsController < ApplicationController + menu_item :calendar before_filter :find_optional_project rescue_from Query::StatementInvalid, :with => :query_statement_invalid diff -r 9cc62779c13a -r 1d32c0a0efbf app/controllers/context_menus_controller.rb --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/app/controllers/context_menus_controller.rb Wed Aug 25 16:30:24 2010 +0100 @@ -0,0 +1,33 @@ +class ContextMenusController < ApplicationController + helper :watchers + + def issues + @issues = Issue.find_all_by_id(params[:ids], :include => :project) + if (@issues.size == 1) + @issue = @issues.first + @allowed_statuses = @issue.new_statuses_allowed_to(User.current) + end + projects = @issues.collect(&:project).compact.uniq + @project = projects.first if projects.size == 1 + + @can = {:edit => (@project && User.current.allowed_to?(:edit_issues, @project)), + :log_time => (@project && User.current.allowed_to?(:log_time, @project)), + :update => (@project && (User.current.allowed_to?(:edit_issues, @project) || (User.current.allowed_to?(:change_status, @project) && @allowed_statuses && !@allowed_statuses.empty?))), + :move => (@project && User.current.allowed_to?(:move_issues, @project)), + :copy => (@issue && @project.trackers.include?(@issue.tracker) && User.current.allowed_to?(:add_issues, @project)), + :delete => (@project && User.current.allowed_to?(:delete_issues, @project)) + } + if @project + @assignables = @project.assignable_users + @assignables << @issue.assigned_to if @issue && @issue.assigned_to && !@assignables.include?(@issue.assigned_to) + @trackers = @project.trackers + end + + @priorities = IssuePriority.all.reverse + @statuses = IssueStatus.find(:all, :order => 'position') + @back = back_url + + render :layout => false + end + +end diff -r 9cc62779c13a -r 1d32c0a0efbf app/controllers/gantts_controller.rb --- a/app/controllers/gantts_controller.rb Wed Jul 28 12:47:17 2010 +0100 +++ b/app/controllers/gantts_controller.rb Wed Aug 25 16:30:24 2010 +0100 @@ -1,4 +1,5 @@ class GanttsController < ApplicationController + menu_item :gantt before_filter :find_optional_project rescue_from Query::StatementInvalid, :with => :query_statement_invalid diff -r 9cc62779c13a -r 1d32c0a0efbf app/controllers/groups_controller.rb --- a/app/controllers/groups_controller.rb Wed Jul 28 12:47:17 2010 +0100 +++ b/app/controllers/groups_controller.rb Wed Aug 25 16:30:24 2010 +0100 @@ -141,14 +141,22 @@ @membership = Member.edit_membership(params[:membership_id], params[:membership], @group) @membership.save if request.post? respond_to do |format| - format.html { redirect_to :controller => 'groups', :action => 'edit', :id => @group, :tab => 'memberships' } - format.js { - render(:update) {|page| - page.replace_html "tab-content-memberships", :partial => 'groups/memberships' - page.visual_effect(:highlight, "member-#{@membership.id}") - } - } - end + if @membership.valid? + format.html { redirect_to :controller => 'groups', :action => 'edit', :id => @group, :tab => 'memberships' } + format.js { + render(:update) {|page| + page.replace_html "tab-content-memberships", :partial => 'groups/memberships' + page.visual_effect(:highlight, "member-#{@membership.id}") + } + } + else + format.js { + render(:update) {|page| + page.alert(l(:notice_failed_to_save_members, :errors => @membership.errors.full_messages.join(', '))) + } + } + end + end end def destroy_membership diff -r 9cc62779c13a -r 1d32c0a0efbf app/controllers/issue_moves_controller.rb --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/app/controllers/issue_moves_controller.rb Wed Aug 25 16:30:24 2010 +0100 @@ -0,0 +1,65 @@ +class IssueMovesController < ApplicationController + default_search_scope :issues + before_filter :find_issues + before_filter :authorize + + def new + prepare_for_issue_move + render :layout => false if request.xhr? + end + + def create + prepare_for_issue_move + + if request.post? + new_tracker = params[:new_tracker_id].blank? ? nil : @target_project.trackers.find_by_id(params[:new_tracker_id]) + unsaved_issue_ids = [] + moved_issues = [] + @issues.each do |issue| + issue.reload + issue.init_journal(User.current) + call_hook(:controller_issues_move_before_save, { :params => params, :issue => issue, :target_project => @target_project, :copy => !!@copy }) + if r = issue.move_to_project(@target_project, new_tracker, {:copy => @copy, :attributes => extract_changed_attributes_for_move(params)}) + moved_issues << r + else + unsaved_issue_ids << issue.id + end + end + set_flash_from_bulk_issue_save(@issues, unsaved_issue_ids) + + if params[:follow] + if @issues.size == 1 && moved_issues.size == 1 + redirect_to :controller => 'issues', :action => 'show', :id => moved_issues.first + else + redirect_to :controller => 'issues', :action => 'index', :project_id => (@target_project || @project) + end + else + redirect_to :controller => 'issues', :action => 'index', :project_id => @project + end + return + end + end + + private + + def prepare_for_issue_move + @issues.sort! + @copy = params[:copy_options] && params[:copy_options][:copy] + @allowed_projects = Issue.allowed_target_projects_on_move + @target_project = @allowed_projects.detect {|p| p.id.to_s == params[:new_project_id]} if params[:new_project_id] + @target_project ||= @project + @trackers = @target_project.trackers + @available_statuses = Workflow.available_statuses(@project) + end + + def extract_changed_attributes_for_move(params) + changed_attributes = {} + [:assigned_to_id, :status_id, :start_date, :due_date].each do |valid_attribute| + unless params[valid_attribute].blank? + changed_attributes[valid_attribute] = (params[valid_attribute] == 'none' ? nil : params[valid_attribute]) + end + end + changed_attributes + end + +end diff -r 9cc62779c13a -r 1d32c0a0efbf app/controllers/issues_controller.rb --- a/app/controllers/issues_controller.rb Wed Jul 28 12:47:17 2010 +0100 +++ b/app/controllers/issues_controller.rb Wed Aug 25 16:30:24 2010 +0100 @@ -19,14 +19,14 @@ menu_item :new_issue, :only => [:new, :create] default_search_scope :issues - before_filter :find_issue, :only => [:show, :edit, :update, :reply] - before_filter :find_issues, :only => [:bulk_edit, :move, :destroy] - before_filter :find_project, :only => [:new, :create, :update_form, :preview, :auto_complete] - before_filter :authorize, :except => [:index, :changes, :preview, :context_menu] - before_filter :find_optional_project, :only => [:index, :changes] + before_filter :find_issue, :only => [:show, :edit, :update] + before_filter :find_issues, :only => [:bulk_edit, :bulk_update, :move, :perform_move, :destroy] + before_filter :find_project, :only => [:new, :create] + before_filter :authorize, :except => [:index] + before_filter :find_optional_project, :only => [:index] before_filter :check_for_default_issue_status, :only => [:new, :create] before_filter :build_new_issue_from_params, :only => [:new, :create] - accept_key_auth :index, :show, :changes + accept_key_auth :index, :show rescue_from Query::StatementInvalid, :with => :query_statement_invalid @@ -54,6 +54,7 @@ :render => { :nothing => true, :status => :method_not_allowed } verify :method => :post, :only => :create, :render => {:nothing => true, :status => :method_not_allowed } + verify :method => :post, :only => :bulk_update, :render => {:nothing => true, :status => :method_not_allowed } verify :method => :put, :only => :update, :render => {:nothing => true, :status => :method_not_allowed } def index @@ -95,21 +96,6 @@ render_404 end - def changes - retrieve_query - sort_init 'id', 'desc' - sort_update(@query.sortable_columns) - - if @query.valid? - @journals = @query.journals(:order => "#{Journal.table_name}.created_on DESC", - :limit => 25) - end - @title = (@project ? @project.name : Setting.app_title) + ": " + (@query.new_record? ? l(:label_changes_details) : @query.name) - render :layout => false, :content_type => 'application/atom+xml' - rescue ActiveRecord::RecordNotFound - render_404 - end - def show @journals = @issue.journals.find(:all, :include => [:user, :details], :order => "#{Journal.table_name}.created_on ASC") @journals.each_with_index {|j,i| j.indice = i+1} @@ -124,7 +110,7 @@ format.html { render :template => 'issues/show.rhtml' } format.xml { render :layout => false } format.json { render :text => @issue.to_json, :layout => false } - format.atom { render :action => 'changes', :layout => false, :content_type => 'application/atom+xml' } + format.atom { render :template => 'journals/index', :layout => false, :content_type => 'application/atom+xml' } format.pdf { send_data(issue_to_pdf(@issue), :type => 'application/pdf', :filename => "#{@project.identifier}-#{@issue.id}.pdf") } end end @@ -132,7 +118,10 @@ # Add a new issue # The new issue will be created from an existing one if copy_from parameter is given def new - render :action => 'new', :layout => !request.xhr? + respond_to do |format| + format.html { render :action => 'new', :layout => !request.xhr? } + format.js { render :partial => 'attributes' } + end end def create @@ -200,98 +189,30 @@ end end - def reply - journal = Journal.find(params[:journal_id]) if params[:journal_id] - if journal - user = journal.user - text = journal.notes - else - user = @issue.author - text = @issue.description - end - # Replaces pre blocks with [...] - text = text.to_s.strip.gsub(%r{
((.|\s)*?)}m, '[...]') - content = "#{ll(Setting.default_language, :text_user_wrote, user)}\n> " - content << text.gsub(/(\r?\n|\r\n?)/, "\n> ") + "\n\n" - - render(:update) { |page| - page.<< "$('notes').value = \"#{escape_javascript content}\";" - page.show 'update' - page << "Form.Element.focus('notes');" - page << "Element.scrollTo('update');" - page << "$('notes').scrollTop = $('notes').scrollHeight - $('notes').clientHeight;" - } - end - # Bulk edit a set of issues def bulk_edit @issues.sort! - if request.post? - attributes = (params[:issue] || {}).reject {|k,v| v.blank?} - attributes.keys.each {|k| attributes[k] = '' if attributes[k] == 'none'} - attributes[:custom_field_values].reject! {|k,v| v.blank?} if attributes[:custom_field_values] - - unsaved_issue_ids = [] - @issues.each do |issue| - issue.reload - journal = issue.init_journal(User.current, params[:notes]) - issue.safe_attributes = attributes - call_hook(:controller_issues_bulk_edit_before_save, { :params => params, :issue => issue }) - unless issue.save - # Keep unsaved issue ids to display them in flash error - unsaved_issue_ids << issue.id - end - end - set_flash_from_bulk_issue_save(@issues, unsaved_issue_ids) - redirect_back_or_default({:controller => 'issues', :action => 'index', :project_id => @project}) - return - end @available_statuses = Workflow.available_statuses(@project) @custom_fields = @project.all_issue_custom_fields end - def move + def bulk_update @issues.sort! - @copy = params[:copy_options] && params[:copy_options][:copy] - @allowed_projects = Issue.allowed_target_projects_on_move - @target_project = @allowed_projects.detect {|p| p.id.to_s == params[:new_project_id]} if params[:new_project_id] - @target_project ||= @project - @trackers = @target_project.trackers - @available_statuses = Workflow.available_statuses(@project) - if request.post? - new_tracker = params[:new_tracker_id].blank? ? nil : @target_project.trackers.find_by_id(params[:new_tracker_id]) - unsaved_issue_ids = [] - moved_issues = [] - @issues.each do |issue| - issue.reload - changed_attributes = {} - [:assigned_to_id, :status_id, :start_date, :due_date].each do |valid_attribute| - unless params[valid_attribute].blank? - changed_attributes[valid_attribute] = (params[valid_attribute] == 'none' ? nil : params[valid_attribute]) - end - end - issue.init_journal(User.current) - call_hook(:controller_issues_move_before_save, { :params => params, :issue => issue, :target_project => @target_project, :copy => !!@copy }) - if r = issue.move_to_project(@target_project, new_tracker, {:copy => @copy, :attributes => changed_attributes}) - moved_issues << r - else - unsaved_issue_ids << issue.id - end + attributes = parse_params_for_bulk_issue_attributes(params) + + unsaved_issue_ids = [] + @issues.each do |issue| + issue.reload + journal = issue.init_journal(User.current, params[:notes]) + issue.safe_attributes = attributes + call_hook(:controller_issues_bulk_edit_before_save, { :params => params, :issue => issue }) + unless issue.save + # Keep unsaved issue ids to display them in flash error + unsaved_issue_ids << issue.id end - set_flash_from_bulk_issue_save(@issues, unsaved_issue_ids) - - if params[:follow] - if @issues.size == 1 && moved_issues.size == 1 - redirect_to :controller => 'issues', :action => 'show', :id => moved_issues.first - else - redirect_to :controller => 'issues', :action => 'index', :project_id => (@target_project || @project) - end - else - redirect_to :controller => 'issues', :action => 'index', :project_id => @project - end - return end - render :layout => false if request.xhr? + set_flash_from_bulk_issue_save(@issues, unsaved_issue_ids) + redirect_back_or_default({:controller => 'issues', :action => 'index', :project_id => @project}) end def destroy @@ -324,77 +245,7 @@ format.json { head :ok } end end - - def context_menu - @issues = Issue.find_all_by_id(params[:ids], :include => :project) - if (@issues.size == 1) - @issue = @issues.first - @allowed_statuses = @issue.new_statuses_allowed_to(User.current) - end - projects = @issues.collect(&:project).compact.uniq - @project = projects.first if projects.size == 1 - @can = {:edit => (@project && User.current.allowed_to?(:edit_issues, @project)), - :log_time => (@project && User.current.allowed_to?(:log_time, @project)), - :update => (@project && (User.current.allowed_to?(:edit_issues, @project) || (User.current.allowed_to?(:change_status, @project) && @allowed_statuses && !@allowed_statuses.empty?))), - :move => (@project && User.current.allowed_to?(:move_issues, @project)), - :copy => (@issue && @project.trackers.include?(@issue.tracker) && User.current.allowed_to?(:add_issues, @project)), - :delete => (@project && User.current.allowed_to?(:delete_issues, @project)) - } - if @project - @assignables = @project.assignable_users - @assignables << @issue.assigned_to if @issue && @issue.assigned_to && !@assignables.include?(@issue.assigned_to) - @trackers = @project.trackers - end - - @priorities = IssuePriority.all.reverse - @statuses = IssueStatus.find(:all, :order => 'position') - @back = params[:back_url] || request.env['HTTP_REFERER'] - - render :layout => false - end - - def update_form - if params[:id].blank? - @issue = Issue.new - @issue.project = @project - else - @issue = @project.issues.visible.find(params[:id]) - end - @issue.attributes = params[:issue] - @allowed_statuses = ([@issue.status] + @issue.status.find_new_statuses_allowed_to(User.current.roles_for_project(@project), @issue.tracker)).uniq - @priorities = IssuePriority.all - - render :partial => 'attributes' - end - - def preview - @issue = @project.issues.find_by_id(params[:id]) unless params[:id].blank? - if @issue - @attachements = @issue.attachments - @description = params[:issue] && params[:issue][:description] - if @description && @description.gsub(/(\r?\n|\n\r?)/, "\n") == @issue.description.to_s.gsub(/(\r?\n|\n\r?)/, "\n") - @description = nil - end - @notes = params[:notes] - else - @description = (params[:issue] ? params[:issue][:description] : nil) - end - render :layout => false - end - - def auto_complete - @issues = [] - q = params[:q].to_s - if q.match(/^\d+$/) - @issues << @project.issues.visible.find_by_id(q.to_i) - end - unless q.blank? - @issues += @project.issues.visible.find(:all, :conditions => ["LOWER(#{Issue.table_name}.subject) LIKE ?", "%#{q.downcase}%"], :limit => 10) - end - render :layout => false - end - private def find_issue @issue = Issue.find(params[:id], :include => [:project, :tracker, :status, :author, :priority, :category]) @@ -403,22 +254,6 @@ render_404 end - # Filter for bulk operations - def find_issues - @issues = Issue.find_all_by_id(params[:id] || params[:ids]) - raise ActiveRecord::RecordNotFound if @issues.empty? - projects = @issues.collect(&:project).compact.uniq - if projects.size == 1 - @project = projects.first - else - # TODO: let users bulk edit/move/destroy issues from different projects - render_error 'Can not bulk edit/move/destroy issues from different projects' - return false - end - rescue ActiveRecord::RecordNotFound - render_404 - end - def find_project project_id = (params[:issue] && params[:issue][:project_id]) || params[:project_id] @project = Project.find(project_id) @@ -449,8 +284,14 @@ # TODO: Refactor, lots of extra code in here def build_new_issue_from_params - @issue = Issue.new - @issue.copy_from(params[:copy_from]) if params[:copy_from] + if params[:id].blank? + @issue = Issue.new + @issue.copy_from(params[:copy_from]) if params[:copy_from] + @issue.project = @project + else + @issue = @project.issues.visible.find(params[:id]) + end + @issue.project = @project # Tracker must be set before custom field values @issue.tracker ||= @project.trackers.find((params[:issue] && params[:issue][:tracker_id]) || params[:tracker_id] || :first) @@ -468,21 +309,17 @@ @allowed_statuses = @issue.new_statuses_allowed_to(User.current, true) end - def set_flash_from_bulk_issue_save(issues, unsaved_issue_ids) - if unsaved_issue_ids.empty? - flash[:notice] = l(:notice_successful_update) unless issues.empty? - else - flash[:error] = l(:notice_failed_to_save_issues, - :count => unsaved_issue_ids.size, - :total => issues.size, - :ids => '#' + unsaved_issue_ids.join(', #')) - end - end - def check_for_default_issue_status if IssueStatus.default.nil? render_error l(:error_no_default_issue_status) return false end end + + def parse_params_for_bulk_issue_attributes(params) + attributes = (params[:issue] || {}).reject {|k,v| v.blank?} + attributes.keys.each {|k| attributes[k] = '' if attributes[k] == 'none'} + attributes[:custom_field_values].reject! {|k,v| v.blank?} if attributes[:custom_field_values] + attributes + end end diff -r 9cc62779c13a -r 1d32c0a0efbf app/controllers/journals_controller.rb --- a/app/controllers/journals_controller.rb Wed Jul 28 12:47:17 2010 +0100 +++ b/app/controllers/journals_controller.rb Wed Aug 25 16:30:24 2010 +0100 @@ -16,7 +16,54 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. class JournalsController < ApplicationController - before_filter :find_journal + before_filter :find_journal, :only => [:edit] + before_filter :find_issue, :only => [:new] + before_filter :find_optional_project, :only => [:index] + accept_key_auth :index + + helper :issues + helper :queries + include QueriesHelper + helper :sort + include SortHelper + + def index + retrieve_query + sort_init 'id', 'desc' + sort_update(@query.sortable_columns) + + if @query.valid? + @journals = @query.journals(:order => "#{Journal.table_name}.created_on DESC", + :limit => 25) + end + @title = (@project ? @project.name : Setting.app_title) + ": " + (@query.new_record? ? l(:label_changes_details) : @query.name) + render :layout => false, :content_type => 'application/atom+xml' + rescue ActiveRecord::RecordNotFound + render_404 + end + + def new + journal = Journal.find(params[:journal_id]) if params[:journal_id] + if journal + user = journal.user + text = journal.notes + else + user = @issue.author + text = @issue.description + end + # Replaces pre blocks with [...] + text = text.to_s.strip.gsub(%r{
((.|\s)*?)}m, '[...]') + content = "#{ll(Setting.default_language, :text_user_wrote, user)}\n> " + content << text.gsub(/(\r?\n|\r\n?)/, "\n> ") + "\n\n" + + render(:update) { |page| + page.<< "$('notes').value = \"#{escape_javascript content}\";" + page.show 'update' + page << "Form.Element.focus('notes');" + page << "Element.scrollTo('update');" + page << "$('notes').scrollTop = $('notes').scrollHeight - $('notes').clientHeight;" + } + end def edit if request.post? @@ -38,4 +85,12 @@ rescue ActiveRecord::RecordNotFound render_404 end + + # TODO: duplicated in IssuesController + def find_issue + @issue = Issue.find(params[:id], :include => [:project, :tracker, :status, :author, :priority, :category]) + @project = @issue.project + rescue ActiveRecord::RecordNotFound + render_404 + end end diff -r 9cc62779c13a -r 1d32c0a0efbf app/controllers/previews_controller.rb --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/app/controllers/previews_controller.rb Wed Aug 25 16:30:24 2010 +0100 @@ -0,0 +1,28 @@ +class PreviewsController < ApplicationController + before_filter :find_project + + def issue + @issue = @project.issues.find_by_id(params[:id]) unless params[:id].blank? + if @issue + @attachements = @issue.attachments + @description = params[:issue] && params[:issue][:description] + if @description && @description.gsub(/(\r?\n|\n\r?)/, "\n") == @issue.description.to_s.gsub(/(\r?\n|\n\r?)/, "\n") + @description = nil + end + @notes = params[:notes] + else + @description = (params[:issue] ? params[:issue][:description] : nil) + end + render :layout => false + end + + private + + def find_project + project_id = (params[:issue] && params[:issue][:project_id]) || params[:project_id] + @project = Project.find(project_id) + rescue ActiveRecord::RecordNotFound + render_404 + end + +end diff -r 9cc62779c13a -r 1d32c0a0efbf app/controllers/timelog_controller.rb --- a/app/controllers/timelog_controller.rb Wed Jul 28 12:47:17 2010 +0100 +++ b/app/controllers/timelog_controller.rb Wed Aug 25 16:30:24 2010 +0100 @@ -55,8 +55,7 @@ sql = "SELECT #{sql_select}, tyear, tmonth, tweek, spent_on, SUM(hours) AS hours" sql << " FROM #{TimeEntry.table_name}" - sql << " LEFT JOIN #{Issue.table_name} ON #{TimeEntry.table_name}.issue_id = #{Issue.table_name}.id" - sql << " LEFT JOIN #{Project.table_name} ON #{TimeEntry.table_name}.project_id = #{Project.table_name}.id" + sql << time_report_joins sql << " WHERE" sql << " (%s) AND" % sql_condition sql << " (spent_on BETWEEN '%s' AND '%s')" % [ActiveRecord::Base.connection.quoted_date(@from), ActiveRecord::Base.connection.quoted_date(@to)] @@ -314,4 +313,12 @@ call_hook(:controller_timelog_available_criterias, { :available_criterias => @available_criterias, :project => @project }) @available_criterias end + + def time_report_joins + sql = '' + sql << " LEFT JOIN #{Issue.table_name} ON #{TimeEntry.table_name}.issue_id = #{Issue.table_name}.id" + sql << " LEFT JOIN #{Project.table_name} ON #{TimeEntry.table_name}.project_id = #{Project.table_name}.id" + call_hook(:controller_timelog_time_report_joins, {:sql => sql} ) + sql + end end diff -r 9cc62779c13a -r 1d32c0a0efbf app/controllers/users_controller.rb --- a/app/controllers/users_controller.rb Wed Jul 28 12:47:17 2010 +0100 +++ b/app/controllers/users_controller.rb Wed Aug 25 16:30:24 2010 +0100 @@ -53,10 +53,8 @@ @user = User.find(params[:id]) @custom_values = @user.custom_values - # show only public projects and private projects that the logged in user is also a member of - @memberships = @user.memberships.select do |membership| - membership.project.is_public? || (User.current.member_of?(membership.project)) - end + # show projects based on current user visibility + @memberships = @user.memberships.all(:conditions => Project.visible_by(User.current)) events = Redmine::Activity::Fetcher.new(User.current, :author => @user).events(nil, nil, :limit => 10) @events_by_day = events.group_by(&:event_date) @@ -123,14 +121,22 @@ @membership = Member.edit_membership(params[:membership_id], params[:membership], @user) @membership.save if request.post? respond_to do |format| - format.html { redirect_to :controller => 'users', :action => 'edit', :id => @user, :tab => 'memberships' } - format.js { - render(:update) {|page| - page.replace_html "tab-content-memberships", :partial => 'users/memberships' - page.visual_effect(:highlight, "member-#{@membership.id}") - } - } - end + if @membership.valid? + format.html { redirect_to :controller => 'users', :action => 'edit', :id => @user, :tab => 'memberships' } + format.js { + render(:update) {|page| + page.replace_html "tab-content-memberships", :partial => 'users/memberships' + page.visual_effect(:highlight, "member-#{@membership.id}") + } + } + else + format.js { + render(:update) {|page| + page.alert(l(:notice_failed_to_save_members, :errors => @membership.errors.full_messages.join(', '))) + } + } + end + end end def destroy_membership diff -r 9cc62779c13a -r 1d32c0a0efbf app/helpers/.svn/all-wcprops --- a/app/helpers/.svn/all-wcprops Wed Jul 28 12:47:17 2010 +0100 +++ b/app/helpers/.svn/all-wcprops Wed Aug 25 16:30:24 2010 +0100 @@ -1,7 +1,7 @@ K 25 svn:wc:ra_dav:version-url V 36 -/svn/!svn/ver/3824/trunk/app/helpers +/svn/!svn/ver/4014/trunk/app/helpers END trackers_helper.rb K 25 @@ -49,7 +49,7 @@ K 25 svn:wc:ra_dav:version-url V 55 -/svn/!svn/ver/3824/trunk/app/helpers/projects_helper.rb +/svn/!svn/ver/3924/trunk/app/helpers/projects_helper.rb END account_helper.rb K 25 @@ -85,13 +85,13 @@ K 25 svn:wc:ra_dav:version-url V 53 -/svn/!svn/ver/3757/trunk/app/helpers/issues_helper.rb +/svn/!svn/ver/3952/trunk/app/helpers/issues_helper.rb END queries_helper.rb K 25 svn:wc:ra_dav:version-url V 54 -/svn/!svn/ver/3691/trunk/app/helpers/queries_helper.rb +/svn/!svn/ver/3924/trunk/app/helpers/queries_helper.rb END mail_handler_helper.rb K 25 @@ -123,29 +123,29 @@ V 53 /svn/!svn/ver/333/trunk/app/helpers/welcome_helper.rb END -journals_helper.rb -K 25 -svn:wc:ra_dav:version-url -V 55 -/svn/!svn/ver/3594/trunk/app/helpers/journals_helper.rb -END workflows_helper.rb K 25 svn:wc:ra_dav:version-url V 56 /svn/!svn/ver/1914/trunk/app/helpers/workflows_helper.rb END +journals_helper.rb +K 25 +svn:wc:ra_dav:version-url +V 55 +/svn/!svn/ver/3941/trunk/app/helpers/journals_helper.rb +END reports_helper.rb K 25 svn:wc:ra_dav:version-url V 53 /svn/!svn/ver/629/trunk/app/helpers/reports_helper.rb END -timelog_helper.rb +custom_fields_helper.rb K 25 svn:wc:ra_dav:version-url -V 54 -/svn/!svn/ver/3708/trunk/app/helpers/timelog_helper.rb +V 60 +/svn/!svn/ver/3675/trunk/app/helpers/custom_fields_helper.rb END settings_helper.rb K 25 @@ -153,11 +153,11 @@ V 55 /svn/!svn/ver/3222/trunk/app/helpers/settings_helper.rb END -custom_fields_helper.rb +timelog_helper.rb K 25 svn:wc:ra_dav:version-url -V 60 -/svn/!svn/ver/3675/trunk/app/helpers/custom_fields_helper.rb +V 54 +/svn/!svn/ver/3708/trunk/app/helpers/timelog_helper.rb END users_helper.rb K 25 @@ -165,11 +165,17 @@ V 52 /svn/!svn/ver/3237/trunk/app/helpers/users_helper.rb END +issue_moves_helper.rb +K 25 +svn:wc:ra_dav:version-url +V 58 +/svn/!svn/ver/3936/trunk/app/helpers/issue_moves_helper.rb +END application_helper.rb K 25 svn:wc:ra_dav:version-url V 58 -/svn/!svn/ver/3602/trunk/app/helpers/application_helper.rb +/svn/!svn/ver/4014/trunk/app/helpers/application_helper.rb END auth_sources_helper.rb K 25 diff -r 9cc62779c13a -r 1d32c0a0efbf app/helpers/.svn/entries --- a/app/helpers/.svn/entries Wed Jul 28 12:47:17 2010 +0100 +++ b/app/helpers/.svn/entries Wed Aug 25 16:30:24 2010 +0100 @@ -1,14 +1,14 @@ 10 dir -3892 +4040 http://redmine.rubyforge.org/svn/trunk/app/helpers http://redmine.rubyforge.org/svn -2010-06-30T03:32:18.366274Z -3824 +2010-08-22T19:12:21.015911Z +4014 edavis10 @@ -270,11 +270,11 @@ -2010-07-23T14:49:44.280140Z -9608643e867e9efd13e8cbe7176c11fc -2010-06-30T03:32:18.366274Z -3824 -edavis10 +2010-08-25T15:27:52.882610Z +19efbd2b30f44f233d5140437e2707a0 +2010-08-08T07:07:20.961363Z +3924 +jbbarth has-props @@ -296,7 +296,7 @@ -5259 +5204 account_helper.rb file @@ -474,10 +474,10 @@ -2010-07-23T14:49:44.284157Z -f455841740387236808e8d7dac18fa5a -2010-05-26T22:48:35.495227Z -3757 +2010-08-25T15:27:52.886524Z +cee3b3c259a71a111285bdf4e756af29 +2010-08-19T03:43:33.818285Z +3952 edavis10 has-props @@ -500,7 +500,7 @@ -10621 +10746 queries_helper.rb file @@ -508,11 +508,11 @@ -2010-07-23T14:49:44.284157Z -c2a3a5fb10d8ac25ead2f2b3024e60cb -2010-04-27T15:28:52.371582Z -3691 -edavis10 +2010-08-25T15:27:52.886524Z +403463e02373d20faf0ed8988cfae033 +2010-08-08T07:07:20.961363Z +3924 +jbbarth has-props @@ -534,7 +534,7 @@ -3841 +3785 mail_handler_helper.rb file @@ -706,6 +706,40 @@ 817 +journals_helper.rb +file + + + + +2010-08-25T15:27:52.886524Z +b64edde44bceed7fb0911f87dba971aa +2010-08-16T16:25:04.741303Z +3941 +edavis10 +has-props + + + + + + + + + + + + + + + + + + + + +2266 + workflows_helper.rb file @@ -740,40 +774,6 @@ 824 -journals_helper.rb -file - - - - -2010-07-23T14:49:44.284157Z -82a2cae51bde677e659b2951289bc851 -2010-03-16T21:37:05.357843Z -3594 -jplang -has-props - - - - - - - - - - - - - - - - - - - - -2266 - reports_helper.rb file @@ -808,6 +808,74 @@ 1265 +timelog_helper.rb +file + + + + +2010-07-23T14:49:44.284157Z +1888fd2d4a1be5fa0d6ad8a63238e2b4 +2010-04-30T12:18:11.536180Z +3708 +jplang +has-props + + + + + + + + + + + + + + + + + + + + +6636 + +settings_helper.rb +file + + + + +2010-07-23T14:49:44.284157Z +72a1f70b395a94a9efae4d1046d5df2b +2009-12-23T17:56:39.685148Z +3222 +jplang +has-props + + + + + + + + + + + + + + + + + + + + +3350 + custom_fields_helper.rb file @@ -842,74 +910,6 @@ 5322 -settings_helper.rb -file - - - - -2010-07-23T14:49:44.284157Z -72a1f70b395a94a9efae4d1046d5df2b -2009-12-23T17:56:39.685148Z -3222 -jplang -has-props - - - - - - - - - - - - - - - - - - - - -3350 - -timelog_helper.rb -file - - - - -2010-07-23T14:49:44.284157Z -1888fd2d4a1be5fa0d6ad8a63238e2b4 -2010-04-30T12:18:11.536180Z -3708 -jplang -has-props - - - - - - - - - - - - - - - - - - - - -6636 - users_helper.rb file @@ -944,17 +944,51 @@ 2645 +issue_moves_helper.rb +file + + + + +2010-08-25T15:27:52.886524Z +66e99bd3af036c92a773a91e92a725f9 +2010-08-11T14:42:10.119704Z +3936 +edavis10 + + + + + + + + + + + + + + + + + + + + + +28 + application_helper.rb file -2010-07-23T14:49:44.288130Z -9c99e579991377ee9facd9ca51b7bc14 -2010-03-18T20:02:17.358992Z -3602 -jplang +2010-08-25T15:27:52.886524Z +93af3e244c658ab28a4da65322eba7ac +2010-08-22T19:12:21.015911Z +4014 +edavis10 has-props @@ -976,7 +1010,7 @@ -31219 +32211 auth_sources_helper.rb file diff -r 9cc62779c13a -r 1d32c0a0efbf app/helpers/.svn/text-base/application_helper.rb.svn-base --- a/app/helpers/.svn/text-base/application_helper.rb.svn-base Wed Jul 28 12:47:17 2010 +0100 +++ b/app/helpers/.svn/text-base/application_helper.rb.svn-base Wed Aug 25 16:30:24 2010 +0100 @@ -103,6 +103,23 @@ link_to(text, {:controller => 'repositories', :action => 'revision', :id => project, :rev => revision}, :title => l(:label_revision_id, revision)) end + # Generates a link to a project if active + # Examples: + # + # link_to_project(project) # => link to the specified project overview + # link_to_project(project, :action=>'settings') # => link to project settings + # link_to_project(project, {:only_path => false}, :class => "project") # => 3rd arg adds html options + # link_to_project(project, {}, :class => "project") # => html options with default url (project overview) + # + def link_to_project(project, options={}, html_options = nil) + if project.active? + url = {:controller => 'projects', :action => 'show', :id => project}.merge(options) + link_to(h(project), url, html_options) + else + h(project) + end + end + def toggle_link(name, id, options={}) onclick = "Element.toggle('#{id}'); " onclick << (options[:focus] ? "Form.Element.focus('#{options[:focus]}'); " : "this.blur(); ") @@ -368,12 +385,12 @@ ancestors = (@project.root? ? [] : @project.ancestors.visible) if ancestors.any? root = ancestors.shift - b << link_to(h(root), {:controller => 'projects', :action => 'show', :id => root, :jump => current_menu_item}, :class => 'root') + b << link_to_project(root, {:jump => current_menu_item}, :class => 'root') if ancestors.size > 2 b << '…' ancestors = ancestors[-2, 2] end - b += ancestors.collect {|p| link_to(h(p), {:controller => 'projects', :action => 'show', :id => p, :jump => current_menu_item}, :class => 'ancestor') } + b += ancestors.collect {|p| link_to_project(p, {:jump => current_menu_item}, :class => 'ancestor') } end b << h(@project) b.join(' » ') @@ -393,6 +410,19 @@ end end + # Returns the theme, controller name, and action as css classes for the + # HTML body. + def body_css_classes + css = [] + if theme = Redmine::Themes.theme(Setting.ui_theme) + css << 'theme-' + theme.name + end + + css << 'controller-' + params[:controller] + css << 'action-' + params[:action] + css.join(' ') + end + def accesskey(s) Redmine::AccessKeys.key_for s end @@ -592,8 +622,7 @@ end when 'project' if p = Project.visible.find_by_id(oid) - link = link_to h(p.name), {:only_path => only_path, :controller => 'projects', :action => 'show', :id => p}, - :class => 'project' + link = link_to_project(p, {:only_path => only_path}, :class => 'project') end end elsif sep == ':' @@ -635,8 +664,7 @@ end when 'project' if p = Project.visible.find(:first, :conditions => ["identifier = :s OR LOWER(name) = :s", {:s => name.downcase}]) - link = link_to h(p.name), {:only_path => only_path, :controller => 'projects', :action => 'show', :id => p}, - :class => 'project' + link = link_to_project(p, {:only_path => only_path}, :class => 'project') end end end @@ -709,6 +737,11 @@ javascript_include_tag('context_menu') + stylesheet_link_tag('context_menu') end + if l(:direction) == 'rtl' + content_for :header_tags do + stylesheet_link_tag('context_menu_rtl') + end + end @context_menu_included = true end javascript_tag "new ContextMenu('#{ url_for(url) }')" @@ -783,6 +816,10 @@ end end + def favicon + "" + end + private def wiki_helper diff -r 9cc62779c13a -r 1d32c0a0efbf app/helpers/.svn/text-base/issue_moves_helper.rb.svn-base --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/app/helpers/.svn/text-base/issue_moves_helper.rb.svn-base Wed Aug 25 16:30:24 2010 +0100 @@ -0,0 +1,2 @@ +module IssueMovesHelper +end diff -r 9cc62779c13a -r 1d32c0a0efbf app/helpers/.svn/text-base/issues_helper.rb.svn-base --- a/app/helpers/.svn/text-base/issues_helper.rb.svn-base Wed Jul 28 12:47:17 2010 +0100 +++ b/app/helpers/.svn/text-base/issues_helper.rb.svn-base Wed Aug 25 16:30:24 2010 +0100 @@ -30,12 +30,14 @@ end def render_issue_tooltip(issue) + @cached_label_status ||= l(:field_status) @cached_label_start_date ||= l(:field_start_date) @cached_label_due_date ||= l(:field_due_date) @cached_label_assigned_to ||= l(:field_assigned_to) @cached_label_priority ||= l(:field_priority) link_to_issue(issue) + "
' + l(:label_export_to)) + yield Redmine::Views::OtherFormatsBuilder.new(self) + concat('
') + end + + def page_header_title + if @project.nil? || @project.new_record? + h(Setting.app_title) + else + b = [] + ancestors = (@project.root? ? [] : @project.ancestors.visible) + if ancestors.any? + root = ancestors.shift + b << link_to(h(root), {:controller => 'projects', :action => 'show', :id => root, :jump => current_menu_item}, :class => 'root') + if ancestors.size > 2 + b << '…' + ancestors = ancestors[-2, 2] + end + b += ancestors.collect {|p| link_to(h(p), {:controller => 'projects', :action => 'show', :id => p, :jump => current_menu_item}, :class => 'ancestor') } + end + b << h(@project) + b.join(' » ') + end + end + + def html_title(*args) + if args.empty? + title = [] + title << @project.name if @project + title += @html_title if @html_title + title << Setting.app_title + title.select {|t| !t.blank? }.join(' - ') + else + @html_title ||= [] + @html_title += args + end + end + + def accesskey(s) + Redmine::AccessKeys.key_for s + end + + # Formats text according to system settings. + # 2 ways to call this method: + # * with a String: textilizable(text, options) + # * with an object and one of its attribute: textilizable(issue, :description, options) + def textilizable(*args) + options = args.last.is_a?(Hash) ? args.pop : {} + case args.size + when 1 + obj = options[:object] + text = args.shift + when 2 + obj = args.shift + attr = args.shift + text = obj.send(attr).to_s + else + raise ArgumentError, 'invalid arguments to textilizable' + end + return '' if text.blank? + project = options[:project] || @project || (obj && obj.respond_to?(:project) ? obj.project : nil) + only_path = options.delete(:only_path) == false ? false : true + + text = Redmine::WikiFormatting.to_html(Setting.text_formatting, text, :object => obj, :attribute => attr) { |macro, args| exec_macro(macro, obj, args) } + + parse_non_pre_blocks(text) do |text| + [:parse_inline_attachments, :parse_wiki_links, :parse_redmine_links].each do |method_name| + send method_name, text, project, obj, attr, only_path, options + end + end + end + + def parse_non_pre_blocks(text) + s = StringScanner.new(text) + tags = [] + parsed = '' + while !s.eos? + s.scan(/(.*?)(<(\/)?(pre|code)(.*?)>|\z)/im) + text, full_tag, closing, tag = s[1], s[2], s[3], s[4] + if tags.empty? + yield text + end + parsed << text + if tag + if closing + if tags.last == tag.downcase + tags.pop + end + else + tags << tag.downcase + end + parsed << full_tag + end + end + # Close any non closing tags + while tag = tags.pop + parsed << "#{tag}>" + end + parsed + end + + def parse_inline_attachments(text, project, obj, attr, only_path, options) + # when using an image link, try to use an attachment, if possible + if options[:attachments] || (obj && obj.respond_to?(:attachments)) + attachments = nil + text.gsub!(/src="([^\/"]+\.(bmp|gif|jpg|jpeg|png))"(\s+alt="([^"]*)")?/i) do |m| + filename, ext, alt, alttext = $1.downcase, $2, $3, $4 + attachments ||= (options[:attachments] || obj.attachments).sort_by(&:created_on).reverse + # search for the picture in attachments + if found = attachments.detect { |att| att.filename.downcase == filename } + image_url = url_for :only_path => only_path, :controller => 'attachments', :action => 'download', :id => found + desc = found.description.to_s.gsub('"', '') + if !desc.blank? && alttext.blank? + alt = " title=\"#{desc}\" alt=\"#{desc}\"" + end + "src=\"#{image_url}\"#{alt}" + else + m + end + end + end + end + + # Wiki links + # + # Examples: + # [[mypage]] + # [[mypage|mytext]] + # wiki links can refer other project wikis, using project name or identifier: + # [[project:]] -> wiki starting page + # [[project:|mytext]] + # [[project:mypage]] + # [[project:mypage|mytext]] + def parse_wiki_links(text, project, obj, attr, only_path, options) + text.gsub!(/(!)?(\[\[([^\]\n\|]+)(\|([^\]\n\|]+))?\]\])/) do |m| + link_project = project + esc, all, page, title = $1, $2, $3, $5 + if esc.nil? + if page =~ /^([^\:]+)\:(.*)$/ + link_project = Project.find_by_name($1) || Project.find_by_identifier($1) + page = $2 + title ||= $1 if page.blank? + end + + if link_project && link_project.wiki + # extract anchor + anchor = nil + if page =~ /^(.+?)\#(.+)$/ + page, anchor = $1, $2 + end + # check if page exists + wiki_page = link_project.wiki.find_page(page) + url = case options[:wiki_links] + when :local; "#{title}.html" + when :anchor; "##{title}" # used for single-file wiki export + else + url_for(:only_path => only_path, :controller => 'wiki', :action => 'index', :id => link_project, :page => Wiki.titleize(page), :anchor => anchor) + end + link_to((title || page), url, :class => ('wiki-page' + (wiki_page ? '' : ' new'))) + else + # project or wiki doesn't exist + all + end + else + all + end + end + end + + # Redmine links + # + # Examples: + # Issues: + # #52 -> Link to issue #52 + # Changesets: + # r52 -> Link to revision 52 + # commit:a85130f -> Link to scmid starting with a85130f + # Documents: + # document#17 -> Link to document with id 17 + # document:Greetings -> Link to the document with title "Greetings" + # document:"Some document" -> Link to the document with title "Some document" + # Versions: + # version#3 -> Link to version with id 3 + # version:1.0.0 -> Link to version named "1.0.0" + # version:"1.0 beta 2" -> Link to version named "1.0 beta 2" + # Attachments: + # attachment:file.zip -> Link to the attachment of the current object named file.zip + # Source files: + # source:some/file -> Link to the file located at /some/file in the project's repository + # source:some/file@52 -> Link to the file's revision 52 + # source:some/file#L120 -> Link to line 120 of the file + # source:some/file@52#L120 -> Link to line 120 of the file's revision 52 + # export:some/file -> Force the download of the file + # Forum messages: + # message#1218 -> Link to message with id 1218 + def parse_redmine_links(text, project, obj, attr, only_path, options) + text.gsub!(%r{([\s\(,\-\[\>]|^)(!)?(attachment|document|version|commit|source|export|message|project)?((#|r)(\d+)|(:)([^"\s<>][^\s<>]*?|"[^"]+?"))(?=(?=[[:punct:]]\W)|,|\s|\]|<|$)}) do |m| + leading, esc, prefix, sep, identifier = $1, $2, $3, $5 || $7, $6 || $8 + link = nil + if esc.nil? + if prefix.nil? && sep == 'r' + if project && (changeset = project.changesets.find_by_revision(identifier)) + link = link_to("r#{identifier}", {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => changeset.revision}, + :class => 'changeset', + :title => truncate_single_line(changeset.comments, :length => 100)) + end + elsif sep == '#' + oid = identifier.to_i + case prefix + when nil + if issue = Issue.visible.find_by_id(oid, :include => :status) + link = link_to("##{oid}", {:only_path => only_path, :controller => 'issues', :action => 'show', :id => oid}, + :class => issue.css_classes, + :title => "#{truncate(issue.subject, :length => 100)} (#{issue.status.name})") + end + when 'document' + if document = Document.find_by_id(oid, :include => [:project], :conditions => Project.visible_by(User.current)) + link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document}, + :class => 'document' + end + when 'version' + if version = Version.find_by_id(oid, :include => [:project], :conditions => Project.visible_by(User.current)) + link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version}, + :class => 'version' + end + when 'message' + if message = Message.find_by_id(oid, :include => [:parent, {:board => :project}], :conditions => Project.visible_by(User.current)) + link = link_to h(truncate(message.subject, :length => 60)), {:only_path => only_path, + :controller => 'messages', + :action => 'show', + :board_id => message.board, + :id => message.root, + :anchor => (message.parent ? "message-#{message.id}" : nil)}, + :class => 'message' + end + when 'project' + if p = Project.visible.find_by_id(oid) + link = link_to h(p.name), {:only_path => only_path, :controller => 'projects', :action => 'show', :id => p}, + :class => 'project' + end + end + elsif sep == ':' + # removes the double quotes if any + name = identifier.gsub(%r{^"(.*)"$}, "\\1") + case prefix + when 'document' + if project && document = project.documents.find_by_title(name) + link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document}, + :class => 'document' + end + when 'version' + if project && version = project.versions.find_by_name(name) + link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version}, + :class => 'version' + end + when 'commit' + if project && (changeset = project.changesets.find(:first, :conditions => ["scmid LIKE ?", "#{name}%"])) + link = link_to h("#{name}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => changeset.revision}, + :class => 'changeset', + :title => truncate_single_line(changeset.comments, :length => 100) + end + when 'source', 'export' + if project && project.repository + name =~ %r{^[/\\]*(.*?)(@([0-9a-f]+))?(#(L\d+))?$} + path, rev, anchor = $1, $3, $5 + link = link_to h("#{prefix}:#{name}"), {:controller => 'repositories', :action => 'entry', :id => project, + :path => to_path_param(path), + :rev => rev, + :anchor => anchor, + :format => (prefix == 'export' ? 'raw' : nil)}, + :class => (prefix == 'export' ? 'source download' : 'source') + end + when 'attachment' + attachments = options[:attachments] || (obj && obj.respond_to?(:attachments) ? obj.attachments : nil) + if attachments && attachment = attachments.detect {|a| a.filename == name } + link = link_to h(attachment.filename), {:only_path => only_path, :controller => 'attachments', :action => 'download', :id => attachment}, + :class => 'attachment' + end + when 'project' + if p = Project.visible.find(:first, :conditions => ["identifier = :s OR LOWER(name) = :s", {:s => name.downcase}]) + link = link_to h(p.name), {:only_path => only_path, :controller => 'projects', :action => 'show', :id => p}, + :class => 'project' + end + end + end + end + leading + (link || "#{prefix}#{sep}#{identifier}") + end + end + + # Same as Rails' simple_format helper without using paragraphs + def simple_format_without_paragraph(text) + text.to_s. + gsub(/\r\n?/, "\n"). # \r\n and \r -> \n + gsub(/\n\n+/, "+<%= select_tag "new_project_id", + project_tree_options_for_select(@allowed_projects, :selected => @target_project), + :onchange => remote_function(:url => { :action => 'new' }, + :method => :get, + :update => 'content', + :with => "Form.serialize('move_form')") %>
+ ++<%= select_tag "new_tracker_id", "" + options_from_collection_for_select(@trackers, "id", "name") %>
+ ++ + <%= select_tag('assigned_to_id', content_tag('option', l(:label_no_change_option), :value => '') + + content_tag('option', l(:label_nobody), :value => 'none') + + options_from_collection_for_select(@target_project.assignable_users, :id, :name)) %> +
+ ++ + <%= select_tag('status_id', "" + options_from_collection_for_select(@available_statuses, :id, :name)) %> +
+ ++ + <%= text_field_tag 'start_date', '', :size => 10 %><%= calendar_for('start_date') %> +
+ ++ + <%= text_field_tag 'due_date', '', :size => 10 %><%= calendar_for('due_date') %> +
+ +<%= call_hook(:view_issues_move_bottom, :issues => @issues, :target_project => @target_project, :copy => !!@copy) %> ++<%= select_tag "new_project_id", + project_tree_options_for_select(@allowed_projects, :selected => @target_project), + :onchange => remote_function(:url => { :action => 'new' }, + :method => :get, + :update => 'content', + :with => "Form.serialize('move_form')") %>
+ ++<%= select_tag "new_tracker_id", "" + options_from_collection_for_select(@trackers, "id", "name") %>
+ ++ + <%= select_tag('assigned_to_id', content_tag('option', l(:label_no_change_option), :value => '') + + content_tag('option', l(:label_nobody), :value => 'none') + + options_from_collection_for_select(@target_project.assignable_users, :id, :name)) %> +
+ ++ + <%= select_tag('status_id', "" + options_from_collection_for_select(@available_statuses, :id, :name)) %> +
+ ++ + <%= text_field_tag 'start_date', '', :size => 10 %><%= calendar_for('start_date') %> +
+ ++ + <%= text_field_tag 'due_date', '', :size => 10 %><%= calendar_for('due_date') %> +
+ +<%= call_hook(:view_issues_move_bottom, :issues => @issues, :target_project => @target_project, :copy => !!@copy) %> +<%= link_to("#{l(:label_revision)} #{changeset.revision}",
:controller => 'repositories', :action => 'revision', :id => changeset.project, :rev => changeset.revision) %>
<%= f.select :tracker_id, @project.trackers.collect {|t| [t.name, t.id]}, :required => true %>
-<%= observe_field :issue_tracker_id, :url => { :action => :update_form, :project_id => @project, :id => @issue }, +<%= observe_field :issue_tracker_id, :url => { :action => :new, :project_id => @project, :id => @issue }, :update => :attributes, :with => "Form.serialize('issue-form')" %> @@ -9,10 +9,7 @@ <% unless (@issue.new_record? && @issue.parent_issue_id.nil?) || !User.current.allowed_to?(:manage_subtasks, @project) %><%= f.text_field :parent_issue_id, :size => 10 %>
-<%= javascript_tag "observeParentIssueField('#{url_for(:controller => :issues, - :action => :auto_complete, - :id => @issue, - :project_id => @project) }')" %> +<%= javascript_tag "observeParentIssueField('#{auto_complete_issues_path(:id => @issue, :project_id => @project) }')" %> <% end %><%= f.text_area :description, diff -r 9cc62779c13a -r 1d32c0a0efbf app/views/issues/.svn/text-base/_history.rhtml.svn-base --- a/app/views/issues/.svn/text-base/_history.rhtml.svn-base Wed Jul 28 12:47:17 2010 +0100 +++ b/app/views/issues/.svn/text-base/_history.rhtml.svn-base Wed Aug 25 16:30:24 2010 +0100 @@ -1,7 +1,7 @@ <% reply_links = authorize_for('issues', 'edit') -%> <% for journal in journals %>
<%= link_to("#{l(:label_revision)} #{changeset.revision}",
:controller => 'repositories', :action => 'revision', :id => changeset.project, :rev => changeset.revision) %>
<%= f.select :tracker_id, @project.trackers.collect {|t| [t.name, t.id]}, :required => true %>
-<%= observe_field :issue_tracker_id, :url => { :action => :update_form, :project_id => @project, :id => @issue }, +<%= observe_field :issue_tracker_id, :url => { :action => :new, :project_id => @project, :id => @issue }, :update => :attributes, :with => "Form.serialize('issue-form')" %> @@ -9,10 +9,7 @@ <% unless (@issue.new_record? && @issue.parent_issue_id.nil?) || !User.current.allowed_to?(:manage_subtasks, @project) %><%= f.text_field :parent_issue_id, :size => 10 %>
-<%= javascript_tag "observeParentIssueField('#{url_for(:controller => :issues, - :action => :auto_complete, - :id => @issue, - :project_id => @project) }')" %> +<%= javascript_tag "observeParentIssueField('#{auto_complete_issues_path(:id => @issue, :project_id => @project) }')" %> <% end %><%= f.text_area :description, diff -r 9cc62779c13a -r 1d32c0a0efbf app/views/issues/_history.rhtml --- a/app/views/issues/_history.rhtml Wed Jul 28 12:47:17 2010 +0100 +++ b/app/views/issues/_history.rhtml Wed Aug 25 16:30:24 2010 +0100 @@ -1,7 +1,7 @@ <% reply_links = authorize_for('issues', 'edit') -%> <% for journal in journals %>
-<%= select_tag "new_project_id", - project_tree_options_for_select(@allowed_projects, :selected => @target_project), - :onchange => remote_function(:url => { :action => 'move' }, - :method => :get, - :update => 'content', - :with => "Form.serialize('move_form')") %>
- --<%= select_tag "new_tracker_id", "" + options_from_collection_for_select(@trackers, "id", "name") %>
- -- - <%= select_tag('assigned_to_id', content_tag('option', l(:label_no_change_option), :value => '') + - content_tag('option', l(:label_nobody), :value => 'none') + - options_from_collection_for_select(@target_project.assignable_users, :id, :name)) %> -
- -- - <%= select_tag('status_id', "" + options_from_collection_for_select(@available_statuses, :id, :name)) %> -
- -- - <%= text_field_tag 'start_date', '', :size => 10 %><%= calendar_for('start_date') %> -
- -- - <%= text_field_tag 'due_date', '', :size => 10 %><%= calendar_for('due_date') %> -
- -<%= call_hook(:view_issues_move_bottom, :issues => @issues, :target_project => @target_project, :copy => !!@copy) %> -