Compatibility with Redmine 4

This commit is contained in:
Karel Pičman 2018-12-19 13:37:12 +01:00
parent 7aadd331ca
commit 3f11d9eec5
25 changed files with 155 additions and 110 deletions

4
Gemfile Normal file
View File

@ -0,0 +1,4 @@
source 'https://rubygems.org'
gem 'acts_as_list'
gem 'activemodel-serializers-xml'

View File

@ -1,8 +1,32 @@
class CustomWorkflowsController < ApplicationController class CustomWorkflowsController < ApplicationController
layout 'admin' layout 'admin'
before_filter :require_admin before_action :require_admin
before_filter :find_workflow, :only => [:show, :edit, :update, :destroy, :export, :change_status] before_action :find_workflow, only: [:show, :edit, :update, :destroy, :export, :change_status, :reorder]
def reorder
from = @workflow.position
to = params[:custom_workflow][:position].to_i
CustomWorkflow.transaction do
CustomWorkflow.find_each do |wf|
if wf.position == from
wf.update_attribute :position, to
elsif wf.position >= to && wf.position < from
# Move up
wf.update_attribute :position, wf.position + 1
elsif wf.position <= to && wf.position > from
# Move down
wf.update_attribute :position, wf.position - 1
end
end
end
respond_to do |format|
format.html
format.js {
render inline: "location.replace('#{custom_workflows_path}');"
}
end
end
def index def index
@workflows = CustomWorkflow.includes(:projects).all @workflows = CustomWorkflow.includes(:projects).all
@ -52,14 +76,30 @@ class CustomWorkflowsController < ApplicationController
end end
def create def create
@workflow = CustomWorkflow.new(params[:custom_workflow]) @workflow = CustomWorkflow.new
@workflow.before_save = params[:custom_workflow][:before_save]
@workflow.after_save = params[:custom_workflow][:after_save]
@workflow.name = params[:custom_workflow][:name]
@workflow.description = params[:custom_workflow][:description]
@workflow.position = CustomWorkflow.count + 1
@workflow.is_for_all = params[:custom_workflow][:is_for_all].present?
@workflow.author = params[:custom_workflow][:author]
@workflow.active = params[:custom_workflow][:active]
@workflow.observable = params[:custom_workflow][:observable]
@workflow.shared_code = params[:custom_workflow][:shared_code]
@workflow.before_add = params[:custom_workflow][:before_add]
@workflow.after_add = params[:custom_workflow][:after_add]
@workflow.before_remove = params[:custom_workflow][:before_remove]
@workflow.after_remove = params[:custom_workflow][:after_remove]
@workflow.before_destroy = params[:custom_workflow][:before_destroy]
@workflow.after_destroy = params[:custom_workflow][:after_destroy]
respond_to do |format| respond_to do |format|
if params.has_key?(:commit) && @workflow.save if params.has_key?(:commit) && @workflow.save
flash[:notice] = l(:notice_successful_create) flash[:notice] = l(:notice_successful_create)
cookies[:custom_workflows_author] = @workflow.author cookies[:custom_workflows_author] = @workflow.author
format.html { redirect_to(custom_workflows_path) } format.html { redirect_to(custom_workflows_path) }
else else
format.html { render :action => "new" } format.html { render action: :new }
end end
end end
end end
@ -70,26 +110,38 @@ class CustomWorkflowsController < ApplicationController
flash[:notice] = l(:notice_successful_status_change) flash[:notice] = l(:notice_successful_status_change)
format.html { redirect_to(custom_workflows_path) } format.html { redirect_to(custom_workflows_path) }
else else
format.html { render :action => :edit } format.html { render action: :edit }
end end
end end
end end
def update def update
respond_to do |format| respond_to do |format|
@workflow.assign_attributes(params[:custom_workflow]) @workflow.before_save = params[:custom_workflow][:before_save]
@workflow.after_save = params[:custom_workflow][:after_save]
@workflow.name = params[:custom_workflow][:name]
@workflow.description = params[:custom_workflow][:description]
@workflow.is_for_all = params[:custom_workflow][:is_for_all].present?
@workflow.author = params[:custom_workflow][:author]
@workflow.active = params[:custom_workflow][:active]
@workflow.shared_code = params[:custom_workflow][:shared_code]
@workflow.before_add = params[:custom_workflow][:before_add]
@workflow.after_add = params[:custom_workflow][:after_add]
@workflow.before_remove = params[:custom_workflow][:before_remove]
@workflow.after_remove = params[:custom_workflow][:after_remove]
@workflow.before_destroy = params[:custom_workflow][:before_destroy]
@workflow.after_destroy = params[:custom_workflow][:after_destroy]
if params.has_key?(:commit) && @workflow.save if params.has_key?(:commit) && @workflow.save
flash[:notice] = l(:notice_successful_update) flash[:notice] = l(:notice_successful_update)
format.html { redirect_to(custom_workflows_path) } format.html { redirect_to(custom_workflows_path) }
else else
format.html { render :action => :edit } format.html { render action: :edit }
end end
end end
end end
def destroy def destroy
@workflow.destroy @workflow.destroy
respond_to do |format| respond_to do |format|
flash[:notice] = l(:notice_successful_delete) flash[:notice] = l(:notice_successful_delete)
format.html { redirect_to(custom_workflows_path) } format.html { redirect_to(custom_workflows_path) }

View File

@ -14,7 +14,6 @@ class CustomWorkflow < ActiveRecord::Base
COLLECTION_OBSERVABLES = [:group_users, :issue_attachments, :project_attachments, :wiki_page_attachments] COLLECTION_OBSERVABLES = [:group_users, :issue_attachments, :project_attachments, :wiki_page_attachments]
SINGLE_OBSERVABLES = [:issue, :user, :group, :attachment, :project, :wiki_content, :time_entry, :version] SINGLE_OBSERVABLES = [:issue, :user, :group, :attachment, :project, :wiki_content, :time_entry, :version]
attr_protected :id
has_and_belongs_to_many :projects has_and_belongs_to_many :projects
acts_as_list acts_as_list
@ -105,6 +104,7 @@ class CustomWorkflow < ActiveRecord::Base
def validate_syntax_for(object, event) def validate_syntax_for(object, event)
object.instance_eval(read_attribute(event)) if respond_to?(event) && read_attribute(event) object.instance_eval(read_attribute(event)) if respond_to?(event) && read_attribute(event)
rescue WorkflowError => e rescue WorkflowError => e
errors.add event, :invalid_script, :error => e
rescue Exception => e rescue Exception => e
errors.add event, :invalid_script, :error => e errors.add event, :invalid_script, :error => e
end end

View File

@ -9,7 +9,7 @@
CustomWorkflow::OBSERVABLES.collect {|o| [l("custom_workflow_observable_#{o}"), o]}, {}, CustomWorkflow::OBSERVABLES.collect {|o| [l("custom_workflow_observable_#{o}"), o]}, {},
:onchange => 'this.form.submit()', :onchange => 'this.form.submit()',
:disabled => !@workflow.new_record? %></p> :disabled => !@workflow.new_record? %></p>
<p><%= f.text_area :description, :cols => 40, :rows => 5 %></p> <p><%= f.text_area :description, cols: 40, rows: 5 , class: 'wiki-edit' %></p>
<% if @workflow.has_projects_association? %> <% 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> <p><%= f.check_box :is_for_all, :onclick => "checkAndDisable('custom_workflow_enabled_projects', this.checked);", :label => :field_enabled_for_all_projects %></p>
<% end %> <% end %>
@ -44,10 +44,10 @@
<% when :shared %> <% when :shared %>
<%= f.text_area :shared_code, :cols => 40, :rows => 20, :wrap => 'off', :class => 'custom_workflow_script' %> <%= f.text_area :shared_code, :cols => 40, :rows => 20, :wrap => 'off', :class => 'custom_workflow_script' %>
<% when *CustomWorkflow::COLLECTION_OBSERVABLES %> <% when *CustomWorkflow::COLLECTION_OBSERVABLES %>
<%= render :layout => 'layouts/collapsible', <% collapsed = (not (@workflow.before_add.present? or @workflow.after_add.present? or @workflow.errors[:base].present?)) %>
:locals => { <fieldset class="collapsible <%= collapsed ? 'collapsed' : '' %>">
:collapsed => (not (@workflow.before_add.present? or @workflow.after_add.present? or @workflow.errors[:base].present?)), <legend onclick="toggleFieldset(this);"><%= l(:label_add_workflows) %></legend>
:label => l(:label_add_workflows)} do %> <div style="<%= collapsed ? 'display: none' : '' %>">
<div class="splitcontent"> <div class="splitcontent">
<div class="splitcontentleft"> <div class="splitcontentleft">
<%= f.text_area :before_add, :cols => 40, :rows => 20, :wrap => 'off', :class => 'custom_workflow_script' %> <%= f.text_area :before_add, :cols => 40, :rows => 20, :wrap => 'off', :class => 'custom_workflow_script' %>
@ -56,11 +56,12 @@
<%= f.text_area :after_add, :cols => 40, :rows => 20, :wrap => 'off', :class => 'custom_workflow_script' %> <%= f.text_area :after_add, :cols => 40, :rows => 20, :wrap => 'off', :class => 'custom_workflow_script' %>
</div> </div>
</div> </div>
<% end %> </div>
<%= render :layout => 'layouts/collapsible', </fieldset>
:locals => { <% collapsed = (not (@workflow.before_remove.present? or @workflow.after_remove.present?)) %>
:collapsed => (not (@workflow.before_remove.present? or @workflow.after_remove.present?)), <fieldset class="collapsible <%= collapsed ? 'collapsed' : '' %>">
:label => l(:label_remove_workflows)} do %> <legend onclick="toggleFieldset(this);"><%= l(:label_remove_workflows) %></legend>
<div style="<%= collapsed ? 'display: none' : '' %>">
<div class="splitcontent"> <div class="splitcontent">
<div class="splitcontentleft"> <div class="splitcontentleft">
<%= f.text_area :before_remove, :cols => 40, :rows => 20, :wrap => 'off', :class => 'custom_workflow_script' %> <%= f.text_area :before_remove, :cols => 40, :rows => 20, :wrap => 'off', :class => 'custom_workflow_script' %>
@ -69,12 +70,13 @@
<%= f.text_area :after_remove, :cols => 40, :rows => 20, :wrap => 'off', :class => 'custom_workflow_script' %> <%= f.text_area :after_remove, :cols => 40, :rows => 20, :wrap => 'off', :class => 'custom_workflow_script' %>
</div> </div>
</div> </div>
<% end %> </div>
</fieldset>
<% when *CustomWorkflow::SINGLE_OBSERVABLES %> <% when *CustomWorkflow::SINGLE_OBSERVABLES %>
<%= render :layout => 'layouts/collapsible', <% collapsed = (not (@workflow.before_save.present? or @workflow.after_save.present? or @workflow.errors[:base].present?)) %>
:locals => { <fieldset class="collapsible <%= collapsed ? 'collapsed' : '' %>">
:collapsed => (not (@workflow.before_save.present? or @workflow.after_save.present? or @workflow.errors[:base].present?)), <legend onclick="toggleFieldset(this);"><%= l(:label_save_workflows) %></legend>
:label => l(:label_save_workflows)} do %> <div style="<%= collapsed ? 'display: none' : '' %>">
<div class="splitcontent"> <div class="splitcontent">
<div class="splitcontentleft"> <div class="splitcontentleft">
<%= f.text_area :before_save, :cols => 40, :rows => 20, :wrap => 'off', :class => 'custom_workflow_script' %> <%= f.text_area :before_save, :cols => 40, :rows => 20, :wrap => 'off', :class => 'custom_workflow_script' %>
@ -89,11 +91,12 @@
<% end %> <% end %>
</div> </div>
</div> </div>
<% end %> </div>
<%= render :layout => 'layouts/collapsible', </fieldset>
:locals => { <% collapsed = (not (@workflow.before_destroy.present? or @workflow.after_destroy.present?)) %>
:collapsed => (not (@workflow.before_destroy.present? or @workflow.after_destroy.present?)), <fieldset class="collapsible <%= collapsed ? 'collapsed' : '' %>">
:label => l(:label_destroy_workflows)} do %> <legend onclick="toggleFieldset(this);"><%= l(:label_destroy_workflows) %></legend>
<div style="<%= collapsed ? 'display: none' : '' %>">
<div class="splitcontent"> <div class="splitcontent">
<div class="splitcontentleft"> <div class="splitcontentleft">
<%= f.text_area :before_destroy, :cols => 40, :rows => 20, :wrap => 'off', :class => 'custom_workflow_script' %> <%= f.text_area :before_destroy, :cols => 40, :rows => 20, :wrap => 'off', :class => 'custom_workflow_script' %>
@ -102,11 +105,12 @@
<%= f.text_area :after_destroy, :cols => 40, :rows => 20, :wrap => 'off', :class => 'custom_workflow_script' %> <%= f.text_area :after_destroy, :cols => 40, :rows => 20, :wrap => 'off', :class => 'custom_workflow_script' %>
</div> </div>
</div> </div>
<% end %> </div>
</fieldset>
<% end %> <% end %>
</fieldset> </fieldset>
<script type="text/javascript"> <script>
jQuery('.custom_workflow_script').taboverride(2, true); jQuery('.custom_workflow_script').taboverride(2, true);
function checkAndDisable(id, checked) { function checkAndDisable(id, checked) {
if (checked) { if (checked) {

View File

@ -38,7 +38,9 @@
<%= workflow.projects.map(&:name).join(", ") %> <%= workflow.projects.map(&:name).join(", ") %>
<% end %> <% end %>
</td> </td>
<td align="center" nowrap><%= reorder_links("custom_workflow", {:action => 'update', :id => workflow, :commit => true}) %></td> <td align="center" nowrap>
<%= reorder_handle(workflow, url: url_for(action: 'reorder', id: workflow) ) %>
</td>
<td class="buttons"> <td class="buttons">
<% if workflow.active? %> <% if workflow.active? %>
<%= link_to(l(:button_custom_workflow_deactivate), custom_workflow_status_path(workflow, :active => false), :class => 'icon icon-inactive', :method => :post) %> <%= link_to(l(:button_custom_workflow_deactivate), custom_workflow_status_path(workflow, :active => false), :class => 'icon icon-inactive', :method => :post) %>
@ -74,3 +76,7 @@
</p> </p>
<% end %> <% end %>
</div> </div>
<%= javascript_tag do %>
$(function() { $("table.custom-workflows tbody").positionedItems(); });
<% end %>

View File

@ -1,6 +0,0 @@
<fieldset class="collapsible <%= collapsed ? 'collapsed' : '' %>">
<legend onclick="toggleFieldset(this);"><%= label %></legend>
<div style="<%= collapsed ? 'display: none' : '' %>">
<%= yield %>
</div>
</fieldset>

View File

@ -1,4 +1,4 @@
<%= form_for @project do |f| %> <%= form_for @project do %>
<%= hidden_field_tag :tab, 'custom_workflow' %> <%= hidden_field_tag :tab, 'custom_workflow' %>
<%= hidden_field_tag 'project[custom_workflow_ids][]', '' %> <%= hidden_field_tag 'project[custom_workflow_ids][]', '' %>
<fieldset> <fieldset>

View File

@ -6,7 +6,7 @@
}; };
function TabOverride(element, tabSize, autoIndent) { function TabOverride(element, tabSize, autoIndent) {
var ta = document.createElement('textarea'); let ta = document.createElement('textarea');
ta.value = '\n'; ta.value = '\n';
this.newline = ta.value; this.newline = ta.value;
@ -43,12 +43,11 @@
* @function * @function
*/ */
setTabSize:function (size) { setTabSize:function (size) {
var i;
if (!size) { // size is 0 or not specified (or falsy) if (!size) { // size is 0 or not specified (or falsy)
this.aTab = '\t'; this.aTab = '\t';
} else if (typeof size === 'number' && size > 0) { } else if (typeof size === 'number' && size > 0) {
this.aTab = ''; this.aTab = '';
for (i = 0; i < size; i += 1) { for (let i = 0; i < size; i += 1) {
this.aTab += ' '; this.aTab += ' ';
} }
} }
@ -64,7 +63,7 @@
* @private * @private
*/ */
overrideKeyPress:function (e) { overrideKeyPress:function (e) {
var key = e.keyCode; let key = e.keyCode;
if ((key === 9 || (key === 13 && this.autoIndent && !this.inWhitespace)) && !e.ctrlKey && !e.altKey) { if ((key === 9 || (key === 13 && this.autoIndent && !this.inWhitespace)) && !e.ctrlKey && !e.altKey) {
e.preventDefault(); e.preventDefault();
} }
@ -78,7 +77,7 @@
* @private * @private
*/ */
overrideKeyDown:function (e) { overrideKeyDown:function (e) {
var key = e.keyCode, // the key code for the key that was pressed let key = e.keyCode, // the key code for the key that was pressed
tab, // the string representing a tab tab, // the string representing a tab
tabLen, // the length of a tab tabLen, // the length of a tab
text, // initial text in the textarea text, // initial text in the textarea

View File

@ -4,4 +4,5 @@ RedmineApp::Application.routes.draw do
post '/custom_workflows/:id', :to => 'custom_workflows#update' post '/custom_workflows/:id', :to => 'custom_workflows#update'
get '/custom_workflows/:id/export', :to => 'custom_workflows#export', :as => 'export_custom_workflow' get '/custom_workflows/:id/export', :to => 'custom_workflows#export', :as => 'export_custom_workflow'
post '/custom_workflows/:id/change_status', :to => 'custom_workflows#change_status', :as => 'custom_workflow_status' post '/custom_workflows/:id/change_status', :to => 'custom_workflows#change_status', :as => 'custom_workflow_status'
put '/custom_workflows/:id/reorder', :to => 'custom_workflows#reorder'
end end

View File

@ -1,5 +1,5 @@
class CreateCustomWorkflows < ActiveRecord::Migration class CreateCustomWorkflows < ActiveRecord::Migration[4.2]
def self.up def change
create_table :custom_workflows, :force => true do |t| create_table :custom_workflows, :force => true do |t|
t.references :project t.references :project
t.text :script, :null => true, :default => nil t.text :script, :null => true, :default => nil
@ -8,7 +8,4 @@ class CreateCustomWorkflows < ActiveRecord::Migration
add_index :custom_workflows, [:project_id], :unique => true add_index :custom_workflows, [:project_id], :unique => true
end end
def self.down
drop_table :custom_workflows
end
end end

View File

@ -1,4 +1,4 @@
class AlterCustomWorkflows < ActiveRecord::Migration class AlterCustomWorkflows < ActiveRecord::Migration[4.2]
def self.up def self.up
remove_column :custom_workflows, :project_id remove_column :custom_workflows, :project_id
remove_column :custom_workflows, :is_enabled remove_column :custom_workflows, :is_enabled
@ -7,6 +7,4 @@ class AlterCustomWorkflows < ActiveRecord::Migration
add_column :custom_workflows, :position, :integer, :null => false, :default => 1 add_column :custom_workflows, :position, :integer, :null => false, :default => 1
end end
def self.down
end
end end

View File

@ -1,5 +1,5 @@
class CreateCustomWorkflowsProjects < ActiveRecord::Migration class CreateCustomWorkflowsProjects < ActiveRecord::Migration[4.2]
def self.up def change
create_table :custom_workflows_projects, :force => true, :id => false do |t| create_table :custom_workflows_projects, :force => true, :id => false do |t|
t.references :project t.references :project
t.references :custom_workflow t.references :custom_workflow
@ -8,7 +8,4 @@ class CreateCustomWorkflowsProjects < ActiveRecord::Migration
add_index :custom_workflows_projects, [:custom_workflow_id] add_index :custom_workflows_projects, [:custom_workflow_id]
end end
def self.down
drop_table :custom_workflows_projects
end
end end

View File

@ -1,8 +1,6 @@
class ChangeCustomWorkflowsDescriptionType < ActiveRecord::Migration class ChangeCustomWorkflowsDescriptionType < ActiveRecord::Migration[4.2]
def self.up def self.up
change_column :custom_workflows, :description, :text, :null => true, :default => nil change_column :custom_workflows, :description, :text, :null => true, :default => nil
end end
def self.down
end
end end

View File

@ -1,4 +1,4 @@
class AddAfterSaveToCustomWorkflows < ActiveRecord::Migration class AddAfterSaveToCustomWorkflows < ActiveRecord::Migration[4.2]
def self.up def self.up
rename_column :custom_workflows, :script, :before_save rename_column :custom_workflows, :script, :before_save
change_column :custom_workflows, :before_save, :text, :null => true change_column :custom_workflows, :before_save, :text, :null => true

View File

@ -1,8 +1,5 @@
class AddIsForAllToCustomWorkflows < ActiveRecord::Migration class AddIsForAllToCustomWorkflows < ActiveRecord::Migration[4.2]
def self.up def change
add_column :custom_workflows, :is_for_all, :boolean, :null => false, :default => false add_column :custom_workflows, :is_for_all, :boolean, :null => false, :default => false
end end
def self.down
remove_column :custom_workflows, :is_for_all
end
end end

View File

@ -1,8 +1,6 @@
class MakeAfterSaveAndBeforeSaveNullable < ActiveRecord::Migration class MakeAfterSaveAndBeforeSaveNullable < ActiveRecord::Migration[4.2]
def up def up
change_column :custom_workflows, :before_save, :text, :null => true, :default => nil change_column :custom_workflows, :before_save, :text, :null => true, :default => nil
change_column :custom_workflows, :after_save, :text, :null => true, :default => nil change_column :custom_workflows, :after_save, :text, :null => true, :default => nil
end end
def down
end
end end

View File

@ -1,7 +1,5 @@
class SetPositionFieldNullable < ActiveRecord::Migration class SetPositionFieldNullable < ActiveRecord::Migration[4.2]
def up def up
change_column :custom_workflows, :position, :integer, :null => true change_column :custom_workflows, :position, :integer, :null => true
end end
def down
end
end end

View File

@ -1,4 +1,4 @@
class AddAuthorAndTimestampsToCustomWorkflows < ActiveRecord::Migration class AddAuthorAndTimestampsToCustomWorkflows < ActiveRecord::Migration[4.2]
def change def change
add_column :custom_workflows, :author, :string, :null => true add_column :custom_workflows, :author, :string, :null => true
add_timestamps :custom_workflows add_timestamps :custom_workflows

View File

@ -1,4 +1,4 @@
class CreateExampleWorkflow < ActiveRecord::Migration class CreateExampleWorkflow < ActiveRecord::Migration[4.2]
def self.up def self.up
CustomWorkflow.reset_column_information CustomWorkflow.reset_column_information
old = CustomWorkflow.where(:name => 'Duration/Done Ratio/Status correlation').first old = CustomWorkflow.where(:name => 'Duration/Done Ratio/Status correlation').first

View File

@ -1,4 +1,4 @@
class AddActiveFieldToCustomWorkflows < ActiveRecord::Migration class AddActiveFieldToCustomWorkflows < ActiveRecord::Migration[4.2]
def change def change
add_column :custom_workflows, :active, :boolean, :null => false, :default => true add_column :custom_workflows, :active, :boolean, :null => false, :default => true
end end

View File

@ -1,4 +1,4 @@
class AddObservableFieldToCustomWorkflows < ActiveRecord::Migration class AddObservableFieldToCustomWorkflows < ActiveRecord::Migration[4.2]
def change def change
add_column :custom_workflows, :observable, :string, :null => false, :default => 'issue' add_column :custom_workflows, :observable, :string, :null => false, :default => 'issue'
end end

View File

@ -1,4 +1,4 @@
class AddAdditionalScriptFieldsToCustomWorkflows < ActiveRecord::Migration class AddAdditionalScriptFieldsToCustomWorkflows < ActiveRecord::Migration[4.2]
def change def change
add_column :custom_workflows, :shared_code, :text, :null => true add_column :custom_workflows, :shared_code, :text, :null => true
add_column :custom_workflows, :before_add, :text, :null => true add_column :custom_workflows, :before_add, :text, :null => true

View File

@ -1,4 +1,4 @@
class AddBeforeAndAfterDestroyToCustomWorkflows < ActiveRecord::Migration class AddBeforeAndAfterDestroyToCustomWorkflows < ActiveRecord::Migration[4.2]
def change def change
add_column :custom_workflows, :before_destroy, :text, :null => true add_column :custom_workflows, :before_destroy, :text, :null => true
add_column :custom_workflows, :after_destroy, :text, :null => true add_column :custom_workflows, :after_destroy, :text, :null => true

View File

@ -7,6 +7,8 @@ Redmine::Plugin.register :redmine_custom_workflows do
version '0.1.6' version '0.1.6'
url 'http://www.redmine.org/plugins/custom-workflows' url 'http://www.redmine.org/plugins/custom-workflows'
requires_redmine version_or_higher: '4.0.0'
menu :admin_menu, :custom_workflows, {:controller => 'custom_workflows', :action => 'index'}, menu :admin_menu, :custom_workflows, {:controller => 'custom_workflows', :action => 'index'},
:if => Proc.new { User.current.admin? }, :caption => :label_custom_workflow_plural, :if => Proc.new { User.current.admin? }, :caption => :label_custom_workflow_plural,
:html => {:class => 'icon icon-workflows'} :html => {:class => 'icon icon-workflows'}

View File

@ -8,7 +8,7 @@ module RedmineCustomWorkflows
end end
module InstanceMethods module InstanceMethods
def custom_email(headers={}) def self.deliver_custom_email(headers={})
user = headers.delete :user user = headers.delete :user
headers[:to] = user.mail if user headers[:to] = user.mail if user
text_body = headers.delete :text_body text_body = headers.delete :text_body