mirror of
https://github.com/anteo/redmine_custom_workflows.git
synced 2026-01-26 00:04:20 +00:00
#15: Added observables User, Group, Group Users
This commit is contained in:
parent
d4999fba06
commit
209329b319
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -5,48 +5,89 @@
|
||||
<p><%= f.text_field :author, :size => 50, :label => :field_custom_workflow_author %>
|
||||
<em class="info"><%= l(:text_custom_workflow_author) %></em>
|
||||
</p>
|
||||
<p><%= f.select :observable,
|
||||
CustomWorkflow::OBSERVABLES.collect {|o| [l("custom_workflow_observable_#{o}"), o]}, {},
|
||||
:onchange => 'this.form.submit()',
|
||||
:disabled => !@workflow.new_record? %></p>
|
||||
<p><%= f.text_area :description, :cols => 40, :rows => 5 %></p>
|
||||
<p><%= f.check_box :is_for_all, :onclick => "checkAndDisable('custom_workflow_enabled_projects', this.checked);", :label => :field_enabled_for_all_projects %></p>
|
||||
<% if @workflow.has_projects_association? %>
|
||||
<p><%= f.check_box :is_for_all, :onclick => "checkAndDisable('custom_workflow_enabled_projects', this.checked);", :label => :field_enabled_for_all_projects %></p>
|
||||
<% end %>
|
||||
<p><%= f.check_box :active, :label => :field_custom_workflow_active %></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="splitcontentright">
|
||||
<fieldset class="box" id="custom_workflow_enabled_projects">
|
||||
<legend><%= l(:label_enabled_projects) %></legend>
|
||||
<%= custom_workflows_render_nested_projects(Project.visible.active) do |p|
|
||||
content_tag('label', check_box_tag('custom_workflow[project_ids][]', p.id, @workflow.project_ids.include?(p.id) || @workflow.is_for_all?,
|
||||
:id => nil, :disabled => @workflow.is_for_all?) + ' ' + h(p), :class => 'block')
|
||||
end %>
|
||||
<%= hidden_field_tag('custom_workflow[project_ids][]', '', :id => nil) %>
|
||||
<p><%= check_all_links 'custom_workflow_enabled_projects' %></p>
|
||||
</fieldset>
|
||||
</div>
|
||||
<% if @workflow.has_projects_association? %>
|
||||
<div class="splitcontentright">
|
||||
<fieldset class="box" id="custom_workflow_enabled_projects">
|
||||
<legend><%= l(:label_enabled_projects) %></legend>
|
||||
<%= custom_workflows_render_nested_projects(Project.visible.active) do |p|
|
||||
content_tag('label', check_box_tag('custom_workflow[project_ids][]', p.id, @workflow.project_ids.include?(p.id) || @workflow.is_for_all?,
|
||||
:id => nil, :disabled => @workflow.is_for_all?) + ' ' + h(p), :class => 'block')
|
||||
end %>
|
||||
<%= hidden_field_tag('custom_workflow[project_ids][]', '', :id => nil) %>
|
||||
<p><%= check_all_links 'custom_workflow_enabled_projects' %></p>
|
||||
</fieldset>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<div style="clear: left;"></div>
|
||||
|
||||
<fieldset class="box">
|
||||
<legend><%= l(:label_workflow_scripts) %></legend>
|
||||
<div class="splitcontent">
|
||||
<div class="splitcontentleft">
|
||||
<%= f.text_area :before_save, :cols => 40, :rows => 20, :wrap => 'off' %>
|
||||
<em class="info"><%= l(:text_custom_workflow_before_save_note) %></em>
|
||||
<% case @workflow.observable %>
|
||||
<% when 'shared' %>
|
||||
<%= f.text_area :shared_code, :cols => 40, :rows => 20, :wrap => 'off', :class => 'custom_workflow_script' %>
|
||||
<% when 'group_users' %>
|
||||
<div class="splitcontent">
|
||||
<div class="splitcontentleft">
|
||||
<%= f.text_area :before_add, :cols => 40, :rows => 20, :wrap => 'off', :class => 'custom_workflow_script' %>
|
||||
</div>
|
||||
<div class="splitcontentright">
|
||||
<%= f.text_area :after_add, :cols => 40, :rows => 20, :wrap => 'off', :class => 'custom_workflow_script' %>
|
||||
</div>
|
||||
</div>
|
||||
<div class="splitcontentright">
|
||||
<%= f.text_area :after_save, :cols => 40, :rows => 20, :wrap => 'off' %>
|
||||
<em class="info"><%= l(:text_custom_workflow_after_save_note) %></em>
|
||||
<div style="clear: left;"></div>
|
||||
<div class="splitcontent">
|
||||
<div class="splitcontentleft">
|
||||
<%= f.text_area :before_remove, :cols => 40, :rows => 20, :wrap => 'off', :class => 'custom_workflow_script' %>
|
||||
</div>
|
||||
<div class="splitcontentright">
|
||||
<%= f.text_area :after_remove, :cols => 40, :rows => 20, :wrap => 'off', :class => 'custom_workflow_script' %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="clear: left;"></div>
|
||||
<div style="clear: left;"></div>
|
||||
<% when 'user', 'group' %>
|
||||
<div class="splitcontent">
|
||||
<div class="splitcontentleft">
|
||||
<%= f.text_area :before_save, :cols => 40, :rows => 20, :wrap => 'off', :class => 'custom_workflow_script' %>
|
||||
</div>
|
||||
<div class="splitcontentright">
|
||||
<%= f.text_area :after_save, :cols => 40, :rows => 20, :wrap => 'off', :class => 'custom_workflow_script' %>
|
||||
</div>
|
||||
</div>
|
||||
<div style="clear: left;"></div>
|
||||
<% when 'issue' %>
|
||||
<div class="splitcontent">
|
||||
<div class="splitcontentleft">
|
||||
<%= f.text_area :before_save, :cols => 40, :rows => 20, :wrap => 'off', :class => 'custom_workflow_script' %>
|
||||
<em class="info"><%= l(:text_custom_workflow_before_save_note) %></em>
|
||||
</div>
|
||||
<div class="splitcontentright">
|
||||
<%= f.text_area :after_save, :cols => 40, :rows => 20, :wrap => 'off', :class => 'custom_workflow_script' %>
|
||||
<em class="info"><%= l(:text_custom_workflow_after_save_note) %></em>
|
||||
</div>
|
||||
</div>
|
||||
<div style="clear: left;"></div>
|
||||
<% end %>
|
||||
<p>
|
||||
<em class="info"><%= l(:text_custom_workflow_general_note) %></em>
|
||||
<em class="info"><%= l("text_custom_workflow_#{@workflow.observable}_code_note") %></em>
|
||||
</p>
|
||||
</fieldset>
|
||||
|
||||
<script type="text/javascript">
|
||||
jQuery('#custom_workflow_before_save').taboverride(2, true);
|
||||
jQuery('#custom_workflow_after_save').taboverride(2, true);
|
||||
jQuery('.custom_workflow_script').taboverride(2, true);
|
||||
function checkAndDisable(id, checked) {
|
||||
if (checked) {
|
||||
jQuery('#' + id).find('input[type=checkbox]').attr('checked', true).attr('disabled', true);
|
||||
|
||||
@ -11,12 +11,13 @@
|
||||
<table class="custom-workflows list">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><%=l(:field_name)%></th>
|
||||
<th><%=l(:field_description)%></th>
|
||||
<th><%=l(:field_author)%></th>
|
||||
<th><%=l(:label_project_plural)%></th>
|
||||
<th style="width:15%;"><%= l(:button_sort) %></th>
|
||||
<th style="width:10%;"></th>
|
||||
<th><%= l(:field_name) %></th>
|
||||
<th><%= l(:field_description) %></th>
|
||||
<th><%= l(:field_observable) %></th>
|
||||
<th><%= l(:field_author) %></th>
|
||||
<th><%= l(:label_project_plural) %></th>
|
||||
<th><%= l(:button_sort) %></th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@ -24,9 +25,12 @@
|
||||
<tr class="<%= cycle("odd", "even") %> <%= 'disabled' unless workflow.active? %>">
|
||||
<td class="name"><%= link_to(workflow.name, edit_custom_workflow_path(workflow)) %></td>
|
||||
<td class="description"><%= textilizable(workflow.description) %></td>
|
||||
<td class="observable"><%= l("custom_workflow_observable_#{workflow.observable}") %></td>
|
||||
<td class="author"><%= mail_to workflow.author if workflow.author.present? %></td>
|
||||
<td>
|
||||
<% if workflow.is_for_all? %>
|
||||
<% if not workflow.has_projects_association? %>
|
||||
-
|
||||
<% elsif workflow.is_for_all? %>
|
||||
<%= l(:field_enabled_for_all_projects) %>
|
||||
<% elsif workflow.projects.empty? %>
|
||||
<%= l(:text_no_enabled_projects) %>
|
||||
@ -34,7 +38,7 @@
|
||||
<%= workflow.projects.map(&:name).join(", ") %>
|
||||
<% end %>
|
||||
</td>
|
||||
<td align="center"><%= reorder_links("custom_workflow", {:action => 'update', :id => workflow}) %></td>
|
||||
<td align="center"><%= reorder_links("custom_workflow", {:action => 'update', :id => workflow, :commit => true}) %></td>
|
||||
<td class="buttons">
|
||||
<% if workflow.active? %>
|
||||
<%= link_to(l(:button_custom_workflow_deactivate), custom_workflow_status_path(workflow, :active => false), :class => 'icon icon-inactive', :method => :post) %>
|
||||
|
||||
@ -3,9 +3,9 @@
|
||||
<%= hidden_field_tag 'project[custom_workflow_ids][]', '' %>
|
||||
<fieldset>
|
||||
<legend><%= l(:text_select_project_custom_workflows) %></legend>
|
||||
<% if CustomWorkflow.exists? %>
|
||||
<% if CustomWorkflow.observing(CustomWorkflow::PROJECT_OBSERVABLES).exists? %>
|
||||
<dl>
|
||||
<% CustomWorkflow.all.each do |w| %>
|
||||
<% CustomWorkflow.observing(CustomWorkflow::PROJECT_OBSERVABLES).each do |w| %>
|
||||
<dt class="<%= 'disabled' unless w.active? %>">
|
||||
<label>
|
||||
<%= check_box_tag 'project[custom_workflow_ids][]', w.id, @project.custom_workflow_ids.include?(w.id) || w.is_for_all?, :disabled => w.is_for_all? %> <%= w.name %>
|
||||
|
||||
@ -12,6 +12,13 @@ table.list.custom-workflows td {
|
||||
|
||||
table.list.custom-workflows td.description {
|
||||
text-align: left;
|
||||
width: 40%;
|
||||
}
|
||||
|
||||
table.list.custom-workflows td.buttons {
|
||||
white-space: normal;
|
||||
width: 10%;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
table.list.custom-workflows tr.disabled {
|
||||
@ -22,7 +29,7 @@ table.list.custom-workflows tr.disabled {
|
||||
width: 98%;
|
||||
}
|
||||
|
||||
#custom_workflow_before_save, #custom_workflow_after_save {
|
||||
.custom_workflow_script {
|
||||
width: 98%;
|
||||
font-size: 11px;
|
||||
}
|
||||
@ -32,7 +39,18 @@ table.list.custom-workflows tr.disabled {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.icon-export { background-image: url(../images/export.png); }
|
||||
.icon-import { background-image: url(../images/import.png); }
|
||||
.icon-active { background-image: url(../images/active.png); }
|
||||
.icon-inactive { background-image: url(../images/inactive.png); }
|
||||
.icon-export {
|
||||
background-image: url(../images/export.png);
|
||||
}
|
||||
|
||||
.icon-import {
|
||||
background-image: url(../images/import.png);
|
||||
}
|
||||
|
||||
.icon-active {
|
||||
background-image: url(../images/active.png);
|
||||
}
|
||||
|
||||
.icon-inactive {
|
||||
background-image: url(../images/inactive.png);
|
||||
}
|
||||
|
||||
@ -14,8 +14,14 @@ en:
|
||||
button_custom_workflow_activate: "Activate"
|
||||
button_custom_workflow_deactivate: "Deactivate"
|
||||
|
||||
field_after_save: "Workflow script executable after saving the issue"
|
||||
field_before_save: "Workflow script executable before saving the issue"
|
||||
field_after_save: "Workflow script executable after saving observable object"
|
||||
field_before_save: "Workflow script executable before saving observable object"
|
||||
field_after_add: "Workflow script executable after adding observable object to collection"
|
||||
field_before_add: "Workflow script executable before adding observable object to collection"
|
||||
field_after_remove: "Workflow script executable after removing observable object from collection"
|
||||
field_before_remove: "Workflow script executable before removing observable object from collection"
|
||||
field_shared_code: "Shared code"
|
||||
field_observable: "Observable object"
|
||||
field_is_enabled: "Enabled"
|
||||
field_enabled_for_all_projects: "Enabled for all projects"
|
||||
field_custom_workflow_author: "Author's e-mail"
|
||||
@ -38,8 +44,18 @@ en:
|
||||
text_select_project_custom_workflows: Select project custom workflows
|
||||
text_custom_workflow_before_save_note: You can change properties of the issues here. Do not create or update related issues in this script. To finish with error, use raise WorkflowError, "Message to user".
|
||||
text_custom_workflow_after_save_note: You can update or create related issues here. Note that this script will be also executed for the newly created issues. So make appropriate checks to prevent infinite recursion.
|
||||
text_custom_workflow_general_note: Both scripts are executed in the context of the issue like ordinary before_save and after_save callbacks. So use methods and properties of the issue directly (or through "self"). Instance variables (@variable) are also allowed and may be used if needed.
|
||||
text_custom_workflow_issue_code_note: Both scripts are executed in the context of the issue like ordinary before_save and after_save callbacks. So use methods and properties of the issue directly (or through "self"). Instance variables (@variable) are also allowed and may be used if needed.
|
||||
text_custom_workflow_shared_code_note: This code will run before any other workflow and may contain shared code, e.g. functions and classes needed by other workflows
|
||||
text_custom_workflow_user_code_note: Both scripts are executed in the context of the user object when user object changes. Use methods and properties of the user directly (or through "self")
|
||||
text_custom_workflow_group_code_note: Both scripts are executed in the context of the group object when group object changes. Use methods and properties of the group directly (or through "self")
|
||||
text_custom_workflow_group_users_code_note: These scripts are executed when user being added to group/removed from group. Use variables @user and @group to access appropriate objects in your scripts.
|
||||
text_no_enabled_projects: No projects
|
||||
text_custom_workflow_author: Will be included in exported XML
|
||||
text_custom_workflow_disabled: disabled by admin
|
||||
text_custom_workflow_is_for_all: enabled for all projects
|
||||
text_custom_workflow_is_for_all: enabled for all projects
|
||||
|
||||
custom_workflow_observable_shared: "<shared code>"
|
||||
custom_workflow_observable_issue: "Issue"
|
||||
custom_workflow_observable_group: "Group"
|
||||
custom_workflow_observable_user: "User"
|
||||
custom_workflow_observable_group_users: "Group Users"
|
||||
@ -14,8 +14,14 @@ pt-br:
|
||||
button_custom_workflow_activate: "Ativar"
|
||||
button_custom_workflow_deactivate: "Desativar"
|
||||
|
||||
field_after_save: "Script do fluxo de trabalho executado depois de salvar a Tarefa"
|
||||
field_before_save: "Script do fluxo de trabalho executado antes de salvar a Tarefa"
|
||||
field_after_save: "Workflow script executable after saving observable object"
|
||||
field_before_save: "Workflow script executable before saving observable object"
|
||||
field_after_add: "Workflow script executable after adding observable object to collection"
|
||||
field_before_add: "Workflow script executable before adding observable object to collection"
|
||||
field_after_remove: "Workflow script executable after removing observable object from collection"
|
||||
field_before_remove: "Workflow script executable before removing observable object from collection"
|
||||
field_shared_code: "Shared code"
|
||||
field_observable: "Observable object"
|
||||
field_is_enabled: "Ativado"
|
||||
field_enabled_for_all_projects: "Ativado para todos os projetos"
|
||||
field_custom_workflow_author: "E-mail do autor"
|
||||
@ -38,8 +44,18 @@ pt-br:
|
||||
text_select_project_custom_workflows: Selecione o projeto para os fluxos de trabalho
|
||||
text_custom_workflow_before_save_note: Você pode alterar as propriedades das tarefas aqui. Não crie ou atualize tarefas relacionadas neste script. Para terminar com o erro, utilize a exceção WorkflowError, "Mensagem para o usuário".
|
||||
text_custom_workflow_after_save_note: Você pode atualizar ou criar tarefas relacionadas aqui. Note que esse script também será executado para as tarefas criadas recentemente. Então, faça as verificações adequadas para evitar que entre em recursividade infinita.
|
||||
text_custom_workflow_general_note: Ambos os scripts são executados no contexto da tarefa pelas chamadas before_save com retornos para a chamada after_save. Portanto, use métodos e propriedades da tarefa diretamente (ou através de "self"). Variáveis de instância (@variable) também são permitidos e podem ser utilizados, se necessário.
|
||||
text_custom_workflow_issue_code_note: Ambos os scripts são executados no contexto da tarefa pelas chamadas before_save com retornos para a chamada after_save. Portanto, use métodos e propriedades da tarefa diretamente (ou através de "self"). Variáveis de instância (@variable) também são permitidos e podem ser utilizados, se necessário.
|
||||
text_custom_workflow_shared_code_note: This code will run before any other workflow and may contain shared code, e.g. functions and classes needed by other workflows.
|
||||
text_custom_workflow_user_code_note: Both scripts are executed in the context of the user object when user object changes. Use methods and properties of the user directly (or through "self")
|
||||
text_custom_workflow_group_code_note: Both scripts are executed in the context of the group object when group object changes. Use methods and properties of the group directly (or through "self")
|
||||
text_custom_workflow_group_users_code_note: These scripts are executed when user being added to group/removed from group. Use variables @user and @group to access appropriate objects in your scripts.
|
||||
text_no_enabled_projects: Não há projeto
|
||||
text_custom_workflow_author: Será incluído no XML exportado
|
||||
text_custom_workflow_disabled: desabilitado por admin
|
||||
text_custom_workflow_is_for_all: Ativado para todos os projetos
|
||||
|
||||
custom_workflow_observable_shared: "<shared code>"
|
||||
custom_workflow_observable_issue: "Issue"
|
||||
custom_workflow_observable_group: "Group"
|
||||
custom_workflow_observable_user: "User"
|
||||
custom_workflow_observable_group_users: "Group Users"
|
||||
@ -14,8 +14,14 @@ ru:
|
||||
button_custom_workflow_activate: "Активировать"
|
||||
button_custom_workflow_deactivate: "Деактивировать"
|
||||
|
||||
field_after_save: "Сценарий выполняемый после сохранения задачи"
|
||||
field_before_save: "Сценарий выполняемый перед сохранением задачи"
|
||||
field_after_save: "Сценарий выполняемый после сохранения наблюдаемого объекта"
|
||||
field_before_save: "Сценарий выполняемый перед сохранением наблюдаемого объекта"
|
||||
field_after_add: "Сценарий выполняемый после добавления наблюдаемого объекта в коллекцию"
|
||||
field_before_add: "Сценарий выполняемый перед добавлением наблюдаемого объекта в коллекцию"
|
||||
field_after_remove: "Сценарий выполняемый после удаления наблюдаемого объекта из коллекции"
|
||||
field_before_remove: "Сценарий выполняемый перед удалением наблюдаемого объекта из коллекции"
|
||||
field_shared_code: "Общий код"
|
||||
field_observable: "Наблюдаемый объект"
|
||||
field_is_enabled: "Разрешено"
|
||||
field_enabled_for_all_projects: "Разрешен для всех проектов"
|
||||
field_custom_workflow_author: "E-Mail адрес автора"
|
||||
@ -38,8 +44,18 @@ ru:
|
||||
text_select_project_custom_workflows: Выберите процессы для данного проекта
|
||||
text_custom_workflow_before_save_note: Здесь вы можете изменять свойства задачи. Не создавайте и не обновляйте связанные задачи в этом сценарии. Чтобы завершить сценарий с произвольной ошибкой, используйте raise WorkflowError, "Message to user".
|
||||
text_custom_workflow_after_save_note: Вы можете обновлять и создавать задачи (в том числе и связанные задачи) здесь. Обратите внимание, что данный сценарий будет также выполняться и для вновь создаваемых задач. Поэтому используйте дополнительные проверки, чтобы избежать бесконечной рекурсии.
|
||||
text_custom_workflow_general_note: Оба сценария исполняются в контексте задачи, как и обычные обратные вызовы before_save и after_save. Поэтому используйте методы и свойства задачи напрямую или через ключевое слово self.
|
||||
text_custom_workflow_issue_code_note: Оба сценария исполняются в контексте задачи, как и обычные обратные вызовы before_save и after_save. Поэтому используйте методы и свойства задачи напрямую или через ключевое слово self.
|
||||
text_custom_workflow_shared_code_note: Этот код будет исполняться перед любым другим процессом и может содержать общий код, например, функции и классы, необходимые для работы других процессов.
|
||||
text_custom_workflow_user_code_note: Оба сценария исполняются в контексте объекта пользователя когда объект пользователя изменяется. Используйте методы и свойства объекта пользователя напрямую или через ключевое слово self.
|
||||
text_custom_workflow_group_code_note: Оба сценария исполняются в контексте объекта группы когда объект группы изменяется. Используйте методы и свойства объекта группы напрямую или через ключевое слово self.
|
||||
text_custom_workflow_group_users_code_note: Эти скрипты выполняются когда пользователь добавляется в группу/удаляется из группы. Используйте переменные @user и @group для доступа к соответствующим объектам из Ваших сценариев.
|
||||
text_no_enabled_projects: Нет проектов
|
||||
text_custom_workflow_author: Будет использован в XML файле при экспорте
|
||||
text_custom_workflow_disabled: отключен администратором
|
||||
text_custom_workflow_is_for_all: разрешен для всех проектов
|
||||
text_custom_workflow_is_for_all: разрешен для всех проектов
|
||||
|
||||
custom_workflow_observable_shared: "<общий код>"
|
||||
custom_workflow_observable_issue: "Задача"
|
||||
custom_workflow_observable_group: "Группа"
|
||||
custom_workflow_observable_user: "Пользователь"
|
||||
custom_workflow_observable_group_users: "Пользователи группы"
|
||||
@ -0,0 +1,5 @@
|
||||
class AddObservableFieldToCustomWorkflows < ActiveRecord::Migration
|
||||
def change
|
||||
add_column :custom_workflows, :observable, :string, :null => false, :default => 'issue'
|
||||
end
|
||||
end
|
||||
@ -0,0 +1,9 @@
|
||||
class AddAdditionalScriptFieldsToCustomWorkflows < ActiveRecord::Migration
|
||||
def change
|
||||
add_column :custom_workflows, :shared_code, :text, :null => true
|
||||
add_column :custom_workflows, :before_add, :text, :null => true
|
||||
add_column :custom_workflows, :after_add, :text, :null => true
|
||||
add_column :custom_workflows, :before_remove, :text, :null => true
|
||||
add_column :custom_workflows, :after_remove, :text, :null => true
|
||||
end
|
||||
end
|
||||
6
init.rb
6
init.rb
@ -11,6 +11,12 @@ Rails.application.config.to_prepare do
|
||||
unless Issue.include?(RedmineCustomWorkflows::IssuePatch)
|
||||
Issue.send(:include, RedmineCustomWorkflows::IssuePatch)
|
||||
end
|
||||
unless User.include?(RedmineCustomWorkflows::UserPatch)
|
||||
User.send(:include, RedmineCustomWorkflows::UserPatch)
|
||||
end
|
||||
unless Group.include?(RedmineCustomWorkflows::GroupPatch)
|
||||
Group.send(:include, RedmineCustomWorkflows::GroupPatch)
|
||||
end
|
||||
unless ActionView::Base.include?(RedmineCustomWorkflows::Helper)
|
||||
ActionView::Base.send(:include, RedmineCustomWorkflows::Helper)
|
||||
end
|
||||
|
||||
39
lib/redmine_custom_workflows/group_patch.rb
Normal file
39
lib/redmine_custom_workflows/group_patch.rb
Normal file
@ -0,0 +1,39 @@
|
||||
module RedmineCustomWorkflows
|
||||
module GroupPatch
|
||||
|
||||
def self.included(base)
|
||||
base.send(:include, InstanceMethods)
|
||||
base.class_eval do
|
||||
before_save :before_save_custom_workflows
|
||||
after_save :after_save_custom_workflows
|
||||
|
||||
callback = lambda do |event, group, user|
|
||||
group.instance_variable_set(:@group, group)
|
||||
group.instance_variable_set(:@user, user)
|
||||
CustomWorkflow.run_shared_code(group) if event.to_s.starts_with? 'before_'
|
||||
CustomWorkflow.run_custom_workflows(:group_users, group, event)
|
||||
end
|
||||
|
||||
before_add_for_users << callback
|
||||
before_remove_for_users << callback
|
||||
after_add_for_users << callback
|
||||
after_remove_for_users << callback
|
||||
end
|
||||
end
|
||||
|
||||
module InstanceMethods
|
||||
def before_save_custom_workflows
|
||||
@group = self
|
||||
@saved_attributes = attributes.dup
|
||||
CustomWorkflow.run_shared_code(self)
|
||||
result = CustomWorkflow.run_custom_workflows(:group, self, :before_save) && (@saved_attributes == attributes || valid?)
|
||||
@saved_attributes = nil
|
||||
result
|
||||
end
|
||||
|
||||
def after_save_custom_workflows
|
||||
CustomWorkflow.run_custom_workflows(:group, self, :after_save)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -23,39 +23,17 @@ module RedmineCustomWorkflows
|
||||
:new_status => status_new && status_new.name
|
||||
end
|
||||
|
||||
def run_custom_workflows(on)
|
||||
return true unless project
|
||||
workflows = project.enabled_custom_workflows
|
||||
return true unless workflows.any?
|
||||
@issue = self # compatibility with 0.0.1
|
||||
Rails.logger.info "= Running #{on} custom workflows for issue \"#{subject}\" (##{id})"
|
||||
workflows.each do |workflow|
|
||||
begin
|
||||
Rails.logger.info "== Running #{on} custom workflow \"#{workflow.name}\""
|
||||
instance_eval(workflow.read_attribute(on))
|
||||
rescue WorkflowError => e
|
||||
Rails.logger.info "== User workflow error: #{e.message}"
|
||||
errors.add :base, e.error
|
||||
return false
|
||||
rescue Exception => e
|
||||
Rails.logger.error "== Custom workflow exception: #{e.message}\n #{e.backtrace.join("\n ")}"
|
||||
errors.add :base, :custom_workflow_error
|
||||
return false
|
||||
end
|
||||
end
|
||||
Rails.logger.info "= Finished running #{on} custom workflows for issue \"#{subject}\" (##{id})."
|
||||
true
|
||||
end
|
||||
|
||||
def before_save_custom_workflows
|
||||
@issue = self
|
||||
@saved_attributes = attributes.dup
|
||||
result = run_custom_workflows(:before_save) && (@saved_attributes == attributes || valid?)
|
||||
CustomWorkflow.run_shared_code(self)
|
||||
result = CustomWorkflow.run_custom_workflows(:issue, self, :before_save) && (@saved_attributes == attributes || valid?)
|
||||
@saved_attributes = nil
|
||||
result
|
||||
end
|
||||
|
||||
def after_save_custom_workflows
|
||||
run_custom_workflows(:after_save)
|
||||
CustomWorkflow.run_custom_workflows(:issue, self, :after_save)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
35
lib/redmine_custom_workflows/mailer_patch.rb
Normal file
35
lib/redmine_custom_workflows/mailer_patch.rb
Normal file
@ -0,0 +1,35 @@
|
||||
module RedmineCustomWorkflows
|
||||
module MailerPatch
|
||||
|
||||
def self.included(base)
|
||||
base.send(:include, InstanceMethods)
|
||||
base.class_eval do
|
||||
end
|
||||
end
|
||||
|
||||
module InstanceMethods
|
||||
def custom_email(headers={})
|
||||
user = headers.delete :user
|
||||
headers[:to] = user.mail if user
|
||||
text_body = headers.delete :text_body
|
||||
html_body = headers.delete :html_body
|
||||
template_name = headers.delete :template_name
|
||||
template_params = headers.delete :template_params || {}
|
||||
if text_body || html_body
|
||||
mail headers do |format|
|
||||
format.text { render :text => text_body } if text_body
|
||||
format.html { render :html => html_body } if html_body
|
||||
end
|
||||
elsif template_name
|
||||
template_params.each { |k,v| instance_variable_set("@#{k}", v) }
|
||||
mail headers do |format|
|
||||
format.text { render template_name }
|
||||
format.html { render template_name } unless Setting.plain_text_mail?
|
||||
end
|
||||
else
|
||||
raise 'Nor :text_body, :html_body or :template_name specified'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -12,9 +12,6 @@ module RedmineCustomWorkflows
|
||||
end
|
||||
|
||||
module InstanceMethods
|
||||
def enabled_custom_workflows
|
||||
(CustomWorkflow.for_all + custom_workflows).select { |w| w.active? }.uniq.sort
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
27
lib/redmine_custom_workflows/user_patch.rb
Normal file
27
lib/redmine_custom_workflows/user_patch.rb
Normal file
@ -0,0 +1,27 @@
|
||||
module RedmineCustomWorkflows
|
||||
module UserPatch
|
||||
|
||||
def self.included(base)
|
||||
base.send(:include, InstanceMethods)
|
||||
base.class_eval do
|
||||
before_save :before_save_custom_workflows
|
||||
after_save :after_save_custom_workflows
|
||||
end
|
||||
end
|
||||
|
||||
module InstanceMethods
|
||||
def before_save_custom_workflows
|
||||
@user = self
|
||||
@saved_attributes = attributes.dup
|
||||
CustomWorkflow.run_shared_code(self)
|
||||
result = CustomWorkflow.run_custom_workflows(:user, self, :before_save) && (@saved_attributes == attributes || valid?)
|
||||
@saved_attributes = nil
|
||||
result
|
||||
end
|
||||
|
||||
def after_save_custom_workflows
|
||||
CustomWorkflow.run_custom_workflows(:user, self, :after_save)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Loading…
x
Reference in New Issue
Block a user