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
layout 'admin'
before_filter :require_admin
before_filter :find_workflow, :only => [:show, :edit, :update, :destroy, :export, :change_status]
before_action :require_admin
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
@workflows = CustomWorkflow.includes(:projects).all
@ -52,14 +76,30 @@ class CustomWorkflowsController < ApplicationController
end
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|
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) }
else
format.html { render :action => "new" }
format.html { render action: :new }
end
end
end
@ -70,26 +110,38 @@ class CustomWorkflowsController < ApplicationController
flash[:notice] = l(:notice_successful_status_change)
format.html { redirect_to(custom_workflows_path) }
else
format.html { render :action => :edit }
format.html { render action: :edit }
end
end
end
def update
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
flash[:notice] = l(:notice_successful_update)
format.html { redirect_to(custom_workflows_path) }
else
format.html { render :action => :edit }
format.html { render action: :edit }
end
end
end
def destroy
@workflow.destroy
respond_to do |format|
flash[:notice] = l(:notice_successful_delete)
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]
SINGLE_OBSERVABLES = [:issue, :user, :group, :attachment, :project, :wiki_content, :time_entry, :version]
attr_protected :id
has_and_belongs_to_many :projects
acts_as_list
@ -105,6 +104,7 @@ class CustomWorkflow < ActiveRecord::Base
def validate_syntax_for(object, event)
object.instance_eval(read_attribute(event)) if respond_to?(event) && read_attribute(event)
rescue WorkflowError => e
errors.add event, :invalid_script, :error => e
rescue Exception => e
errors.add event, :invalid_script, :error => e
end

View File

@ -9,7 +9,7 @@
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.text_area :description, cols: 40, rows: 5 , class: 'wiki-edit' %></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 %>
@ -44,69 +44,73 @@
<% when :shared %>
<%= f.text_area :shared_code, :cols => 40, :rows => 20, :wrap => 'off', :class => 'custom_workflow_script' %>
<% when *CustomWorkflow::COLLECTION_OBSERVABLES %>
<%= render :layout => 'layouts/collapsible',
:locals => {
:collapsed => (not (@workflow.before_add.present? or @workflow.after_add.present? or @workflow.errors[:base].present?)),
:label => l(:label_add_workflows)} do %>
<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' %>
<% collapsed = (not (@workflow.before_add.present? or @workflow.after_add.present? or @workflow.errors[:base].present?)) %>
<fieldset class="collapsible <%= collapsed ? 'collapsed' : '' %>">
<legend onclick="toggleFieldset(this);"><%= l(:label_add_workflows) %></legend>
<div style="<%= collapsed ? 'display: none' : '' %>">
<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>
<% end %>
<%= render :layout => 'layouts/collapsible',
:locals => {
:collapsed => (not (@workflow.before_remove.present? or @workflow.after_remove.present?)),
:label => l(:label_remove_workflows)} do %>
<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' %>
</fieldset>
<% collapsed = (not (@workflow.before_remove.present? or @workflow.after_remove.present?)) %>
<fieldset class="collapsible <%= collapsed ? 'collapsed' : '' %>">
<legend onclick="toggleFieldset(this);"><%= l(:label_remove_workflows) %></legend>
<div style="<%= collapsed ? 'display: none' : '' %>">
<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>
<% end %>
</fieldset>
<% when *CustomWorkflow::SINGLE_OBSERVABLES %>
<%= render :layout => 'layouts/collapsible',
:locals => {
:collapsed => (not (@workflow.before_save.present? or @workflow.after_save.present? or @workflow.errors[:base].present?)),
:label => l(:label_save_workflows)} do %>
<div class="splitcontent">
<div class="splitcontentleft">
<%= f.text_area :before_save, :cols => 40, :rows => 20, :wrap => 'off', :class => 'custom_workflow_script' %>
<% if observable == :issue %>
<em class="info"><%= l(:text_custom_workflow_before_save_note) %></em>
<% end %>
</div>
<div class="splitcontentright">
<%= f.text_area :after_save, :cols => 40, :rows => 20, :wrap => 'off', :class => 'custom_workflow_script' %>
<% if observable == :issue %>
<em class="info"><%= l(:text_custom_workflow_after_save_note) %></em>
<% end %>
<% collapsed = (not (@workflow.before_save.present? or @workflow.after_save.present? or @workflow.errors[:base].present?)) %>
<fieldset class="collapsible <%= collapsed ? 'collapsed' : '' %>">
<legend onclick="toggleFieldset(this);"><%= l(:label_save_workflows) %></legend>
<div style="<%= collapsed ? 'display: none' : '' %>">
<div class="splitcontent">
<div class="splitcontentleft">
<%= f.text_area :before_save, :cols => 40, :rows => 20, :wrap => 'off', :class => 'custom_workflow_script' %>
<% if observable == :issue %>
<em class="info"><%= l(:text_custom_workflow_before_save_note) %></em>
<% end %>
</div>
<div class="splitcontentright">
<%= f.text_area :after_save, :cols => 40, :rows => 20, :wrap => 'off', :class => 'custom_workflow_script' %>
<% if observable == :issue %>
<em class="info"><%= l(:text_custom_workflow_after_save_note) %></em>
<% end %>
</div>
</div>
</div>
<% end %>
<%= render :layout => 'layouts/collapsible',
:locals => {
:collapsed => (not (@workflow.before_destroy.present? or @workflow.after_destroy.present?)),
:label => l(:label_destroy_workflows)} do %>
<div class="splitcontent">
<div class="splitcontentleft">
<%= f.text_area :before_destroy, :cols => 40, :rows => 20, :wrap => 'off', :class => 'custom_workflow_script' %>
</div>
<div class="splitcontentright">
<%= f.text_area :after_destroy, :cols => 40, :rows => 20, :wrap => 'off', :class => 'custom_workflow_script' %>
</fieldset>
<% collapsed = (not (@workflow.before_destroy.present? or @workflow.after_destroy.present?)) %>
<fieldset class="collapsible <%= collapsed ? 'collapsed' : '' %>">
<legend onclick="toggleFieldset(this);"><%= l(:label_destroy_workflows) %></legend>
<div style="<%= collapsed ? 'display: none' : '' %>">
<div class="splitcontent">
<div class="splitcontentleft">
<%= f.text_area :before_destroy, :cols => 40, :rows => 20, :wrap => 'off', :class => 'custom_workflow_script' %>
</div>
<div class="splitcontentright">
<%= f.text_area :after_destroy, :cols => 40, :rows => 20, :wrap => 'off', :class => 'custom_workflow_script' %>
</div>
</div>
</div>
<% end %>
</fieldset>
<% end %>
</fieldset>
<script type="text/javascript">
<script>
jQuery('.custom_workflow_script').taboverride(2, true);
function checkAndDisable(id, checked) {
if (checked) {

View File

@ -38,7 +38,9 @@
<%= workflow.projects.map(&:name).join(", ") %>
<% end %>
</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">
<% if workflow.active? %>
<%= link_to(l(:button_custom_workflow_deactivate), custom_workflow_status_path(workflow, :active => false), :class => 'icon icon-inactive', :method => :post) %>
@ -73,4 +75,8 @@
<%= submit_tag l(:button_cancel), :name => nil, :onclick => "hideModal(this);", :type => 'button' %>
</p>
<% 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 'project[custom_workflow_ids][]', '' %>
<fieldset>

View File

@ -6,7 +6,7 @@
};
function TabOverride(element, tabSize, autoIndent) {
var ta = document.createElement('textarea');
let ta = document.createElement('textarea');
ta.value = '\n';
this.newline = ta.value;
@ -43,12 +43,11 @@
* @function
*/
setTabSize:function (size) {
var i;
if (!size) { // size is 0 or not specified (or falsy)
this.aTab = '\t';
} else if (typeof size === 'number' && size > 0) {
this.aTab = '';
for (i = 0; i < size; i += 1) {
for (let i = 0; i < size; i += 1) {
this.aTab += ' ';
}
}
@ -64,7 +63,7 @@
* @private
*/
overrideKeyPress:function (e) {
var key = e.keyCode;
let key = e.keyCode;
if ((key === 9 || (key === 13 && this.autoIndent && !this.inWhitespace)) && !e.ctrlKey && !e.altKey) {
e.preventDefault();
}
@ -78,7 +77,7 @@
* @private
*/
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
tabLen, // the length of a tab
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'
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'
put '/custom_workflows/:id/reorder', :to => 'custom_workflows#reorder'
end

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,4 +1,4 @@
class CreateExampleWorkflow < ActiveRecord::Migration
class CreateExampleWorkflow < ActiveRecord::Migration[4.2]
def self.up
CustomWorkflow.reset_column_information
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
add_column :custom_workflows, :active, :boolean, :null => false, :default => true
end

View File

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

View File

@ -1,4 +1,4 @@
class AddAdditionalScriptFieldsToCustomWorkflows < ActiveRecord::Migration
class AddAdditionalScriptFieldsToCustomWorkflows < ActiveRecord::Migration[4.2]
def change
add_column :custom_workflows, :shared_code, :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
add_column :custom_workflows, :before_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'
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'},
:if => Proc.new { User.current.admin? }, :caption => :label_custom_workflow_plural,
:html => {:class => 'icon icon-workflows'}

View File

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