diff --git a/app/controllers/custom_workflows_controller.rb b/app/controllers/custom_workflows_controller.rb index e9c57cf..5346136 100644 --- a/app/controllers/custom_workflows_controller.rb +++ b/app/controllers/custom_workflows_controller.rb @@ -54,7 +54,7 @@ class CustomWorkflowsController < ApplicationController def create @workflow = CustomWorkflow.new(params[:custom_workflow]) respond_to do |format| - if @workflow.save + if params.has_key?(:commit) && @workflow.save flash[:notice] = l(:notice_successful_create) cookies[:custom_workflows_author] = @workflow.author format.html { redirect_to(custom_workflows_path) } @@ -74,7 +74,8 @@ class CustomWorkflowsController < ApplicationController def update respond_to do |format| - if @workflow.update_attributes(params[:custom_workflow]) + @workflow.assign_attributes(params[:custom_workflow]) + if params.has_key?(:commit) && @workflow.save flash[:notice] = l(:notice_successful_update) format.html { redirect_to(custom_workflows_path) } else diff --git a/app/models/custom_workflow.rb b/app/models/custom_workflow.rb index 2b3d66c..a158137 100644 --- a/app/models/custom_workflow.rb +++ b/app/models/custom_workflow.rb @@ -8,6 +8,9 @@ class WorkflowError < StandardError end class CustomWorkflow < ActiveRecord::Base + OBSERVABLES = [:issue, :user, :group, :group_users, :shared] + PROJECT_OBSERVABLES = [:issue] + COLLECTION_OBSERVABLES = [:group_users] attr_protected :id has_and_belongs_to_many :projects @@ -19,37 +22,104 @@ class CustomWorkflow < ActiveRecord::Base validate :validate_syntax default_scope { order(:position => :asc) } - scope :for_all, lambda { where(:is_for_all => true) } scope :active, lambda { where(:active => true) } + scope :for_project, (lambda do |project| + where("is_for_all OR EXISTS (SELECT * FROM #{reflect_on_association(:projects).join_table} WHERE project_id=? AND custom_workflow_id=id)", project.id) + end) + scope :observing, lambda { |observable| where(:observable => observable) } class << self def import_from_xml(xml) attributes = Hash.from_xml(xml).values.first attributes.delete('exported_at') + attributes.delete('plugin_version') + attributes.delete('ruby_version') + attributes.delete('rails_version') CustomWorkflow.new(attributes) end + + def run_shared_code(object) + workflows = CustomWorkflow.observing(:shared).active + Rails.logger.info "= Running shared code for #{object.class} \"#{object}\" (\##{object.id})" + workflows.each do |workflow| + return false unless workflow.run(object, :shared_code) + end + Rails.logger.info "= Finished running shared code for #{object.class} \"#{object}\" (\##{object.id})" + true + end + + def run_custom_workflows(observable, object, event) + workflows = CustomWorkflow.active.observing(observable) + if object.respond_to? :project + return true unless object.project + workflows = workflows.for_project(object.project) + end + return true unless workflows.any? + Rails.logger.info "= Running #{event} custom workflows for #{object.class} \"#{object}\" (\##{object.id})" + workflows.each do |workflow| + return false unless workflow.run(object, event) + end + Rails.logger.info "= Finished running #{event} custom workflows for #{object.class} \"#{object}\" (\##{object.id})" + true + end + end + + def run(object, event) + Rails.logger.info "== Running #{event} custom workflow \"#{name}\"" + object.instance_eval(read_attribute(event)) + true + rescue WorkflowError => e + Rails.logger.info "== User workflow error: #{e.message}" + object.errors.add :base, e.error + false + rescue Exception => e + Rails.logger.error "== Custom workflow exception: #{e.message}\n #{e.backtrace.join("\n ")}" + object.errors.add :base, :custom_workflow_error + false + end + + def has_projects_association? + PROJECT_OBSERVABLES.include? observable.to_sym + end + + def validate_syntax_for(object, event) + object.instance_eval(read_attribute(event)) if respond_to?(event) && read_attribute(event) + rescue WorkflowError => e + rescue Exception => e + errors.add event, :invalid_script, :error => e end def validate_syntax - issue = Issue.new - issue.send :instance_variable_set, :@issue, issue # compatibility with 0.0.1 - begin - issue.instance_eval(before_save) if respond_to?(:before_save) && before_save - rescue WorkflowError => e - rescue Exception => e - errors.add :before_save, :invalid_script, :error => e - end - begin - issue.instance_eval(after_save) if respond_to?(:after_save) && after_save - rescue WorkflowError => e - rescue Exception => e - errors.add :after_save, :invalid_script, :error => e + case observable + when 'shared' + CustomWorkflow.run_shared_code(self) + validate_syntax_for(self, :shared_code) + when 'user', 'group', 'issue' + object = observable.camelize.constantize.new + object.send :instance_variable_set, "@#{observable}", object # compatibility with 0.0.1 + CustomWorkflow.run_shared_code(object) + validate_syntax_for(object, :before_save) + validate_syntax_for(object, :after_save) + when 'group_users' + @user = User.new + @group = Group.new + CustomWorkflow.run_shared_code(self) + validate_syntax_for(self, :before_add) + validate_syntax_for(self, :before_remove) + validate_syntax_for(self, :after_add) + validate_syntax_for(self, :after_remove) end end def export_as_xml - to_xml :only => [:author, :name, :description, :before_save, :after_save, :created_at] do |xml| + only = [:author, :name, :description, :before_save, :after_save, :shared_code, :observable, + :before_add, :after_add, :before_remove, :after_remove, :created_at] + only = only.select { |p| self[p] } + to_xml :only => only do |xml| xml.tag! 'exported-at', Time.current.xmlschema + xml.tag! 'plugin-version', Redmine::Plugin.find(:redmine_custom_workflows).version + xml.tag! 'ruby-version', "#{RUBY_VERSION}-p#{RUBY_PATCHLEVEL}" + xml.tag! 'rails-version', Rails::VERSION::STRING end end diff --git a/app/views/custom_workflows/_form.html.erb b/app/views/custom_workflows/_form.html.erb index ffdc292..44508dd 100644 --- a/app/views/custom_workflows/_form.html.erb +++ b/app/views/custom_workflows/_form.html.erb @@ -5,48 +5,89 @@
<%= f.text_field :author, :size => 50, :label => :field_custom_workflow_author %> <%= l(:text_custom_workflow_author) %>
+<%= f.select :observable, + CustomWorkflow::OBSERVABLES.collect {|o| [l("custom_workflow_observable_#{o}"), o]}, {}, + :onchange => 'this.form.submit()', + :disabled => !@workflow.new_record? %>
<%= f.text_area :description, :cols => 40, :rows => 5 %>
-<%= f.check_box :is_for_all, :onclick => "checkAndDisable('custom_workflow_enabled_projects', this.checked);", :label => :field_enabled_for_all_projects %>
+ <% if @workflow.has_projects_association? %> +<%= f.check_box :is_for_all, :onclick => "checkAndDisable('custom_workflow_enabled_projects', this.checked);", :label => :field_enabled_for_all_projects %>
+ <% end %><%= f.check_box :active, :label => :field_custom_workflow_active %>
-