Redmine look & feel

This commit is contained in:
Karel Pičman 2020-04-02 15:43:51 +02:00
parent 640f3aa077
commit 3f901f6bbb
42 changed files with 620 additions and 853 deletions

View File

@ -1,7 +1,7 @@
Custom Workflows plug-in
========================
The current version of Redmine CustomWorkflows is **1.0.2** [![Build Status](https://api.travis-ci.org/anteo/redmine_custom_workflows.png)](https://travis-ci.org/anteo/redmine_custom_workflows)
The current version of Redmine CustomWorkflows is **1.0.3 devel** [![Build Status](https://api.travis-ci.org/anteo/redmine_custom_workflows.png)](https://travis-ci.org/anteo/redmine_custom_workflows)
This plug-in provides a great functionality for those who is familiar with the Ruby language.
It allows to customize workflow by defining own rules for issues processing. It's possible:

View File

@ -99,23 +99,9 @@ class CustomWorkflowsController < ApplicationController
def create
@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] == '1'
@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]
@workflow.project_ids = params[:custom_workflow][:project_ids]
update_from_params
respond_to do |format|
if params.has_key?(:commit) && @workflow.save
flash[:notice] = l(:notice_successful_create)
@ -140,21 +126,7 @@ class CustomWorkflowsController < ApplicationController
def update
respond_to do |format|
@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] == '1'
@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]
@workflow.project_ids = params[:custom_workflow][:project_ids]
update_from_params
if params.has_key?(:commit) && @workflow.save
flash[:notice] = l(:notice_successful_update)
format.html { redirect_to(custom_workflows_path) }
@ -180,4 +152,22 @@ class CustomWorkflowsController < ApplicationController
render_404
end
def update_from_params
@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] == '1'
@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]
@workflow.project_ids = params[:custom_workflow][:project_ids]
end
end

View File

@ -31,11 +31,11 @@ class CustomWorkflow < ActiveRecord::Base
acts_as_list
validates_presence_of :name
validates_uniqueness_of :name, :case_sensitive => false
validates_format_of :author, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i, :allow_blank => true
validate :validate_syntax, :validate_scripts_presence, :if => Proc.new {|workflow| workflow.respond_to?(:observable) and workflow.active?}
validates_uniqueness_of :name, case_sensitive: false
validates_format_of :author, with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i, allow_blank: true
validate :validate_syntax, :validate_scripts_presence, if: Proc.new { |workflow| workflow.respond_to?(:observable) and workflow.active? }
scope :active, lambda { where(:active => 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)",
true, project.id)
@ -43,11 +43,11 @@ class CustomWorkflow < ActiveRecord::Base
def self.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)
attributes.delete 'exported_at'
attributes.delete 'plugin_version'
attributes.delete 'ruby_version'
attributes.delete 'rails_version'
CustomWorkflow.new attributes
end
def self.log_message(str, object)

View File

@ -1,22 +1,45 @@
<%
# encoding: utf-8
#
# Redmine plugin for Custom Workflows
#
# Copyright © 2015-19 Anton Argirov
# Copyright © 2019-20 Karel Pičman <karel.picman@kontron.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
%>
<div class="splitcontent">
<div class="splitcontentleft">
<div class="box tabular">
<p><%= f.text_field :name, :required => true, :size => 50 %></p>
<p><%= f.text_field :author, :size => 50, :label => :field_custom_workflow_author %>
<p><%= f.text_field :name, required: true %></p>
<p><%= f.text_field :author, 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 , class: 'wiki-edit' %></p>
onchange: 'this.form.submit()',
disabled: !@workflow.new_record? %></p>
<p><%= f.text_area :description, 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 %>
<p><%= f.check_box :active, :label => :field_custom_workflow_active %></p>
<p><%= f.check_box :active, label: l(:field_active) %></p>
</div>
</div>
<% if @workflow.has_projects_association? %>
@ -28,54 +51,53 @@
<%= l(:label_project_plural) %>
</legend>
<%= render_project_nested_lists(Project.visible.active) do |p| %>
<%= content_tag('label',
<%= 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') %>
disabled: @workflow.is_for_all?) + ' ' + h(p), class: 'block' %>
<% end %>
<%= hidden_field_tag('custom_workflow[project_ids][]', '', id: nil) %>
<%= hidden_field_tag 'custom_workflow[project_ids][]', '', id: nil %>
<% end %>
</div>
</div>
<% end %>
</div>
<div style="clear: left;"></div>
<fieldset class="box">
<div id="custom_workflow_scripts" class="box tabular">
<fieldset>
<legend><%= l(:label_workflow_scripts) %></legend>
<% observable = @workflow.observable.to_sym %>
<p>
<div class="custom-workflow-info">
<em class="info"><%= l("text_custom_workflow_#{observable}_code_note") %></em>
</p>
</div>
<% case observable %>
<% 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 %>
<% collapsed = (not (@workflow.before_add.present? or @workflow.after_add.present? or @workflow.errors[:base].present?)) %>
<% collapsed = !(@workflow.before_add.present? || @workflow.after_add.present? || @workflow.errors[:base].present?) %>
<fieldset class="collapsible <%= collapsed ? 'collapsed' : '' %>">
<legend onclick="toggleFieldset(this);"><%= l(:label_add_workflows) %></legend>
<div style="<%= collapsed ? 'display: none' : '' %>">
<legend onclick="toggleFieldset(this);" class="icon icon-collapsed"><%= l(:label_add_workflows) %></legend>
<div class="<%= collapsed ? 'custom-workflow-hidden' : '' %>">
<div class="splitcontent">
<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' %>
</div>
<div class="splitcontentright">
<%= 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>
</fieldset>
<% collapsed = (not (@workflow.before_remove.present? or @workflow.after_remove.present?)) %>
<% collapsed = !(@workflow.before_remove.present? || @workflow.after_remove.present?) %>
<fieldset class="collapsible <%= collapsed ? 'collapsed' : '' %>">
<legend onclick="toggleFieldset(this);"><%= l(:label_remove_workflows) %></legend>
<div style="<%= collapsed ? 'display: none' : '' %>">
<legend onclick="toggleFieldset(this);" class="icon icon-collapsed"><%= l(:label_remove_workflows) %></legend>
<div class="<%= collapsed ? 'custom-workflow-hidden' : '' %>">
<div class="splitcontent">
<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' %>
</div>
<div class="splitcontentright">
<%= 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>
@ -83,17 +105,17 @@
<% when *CustomWorkflow::SINGLE_OBSERVABLES %>
<% 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' : '' %>">
<legend onclick="toggleFieldset(this);" class="icon icon-collapsed"><%= l(:label_save_workflows) %></legend>
<div class="<%= collapsed ? 'custom-workflow-hidden' : '' %>">
<div class="splitcontent">
<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' %>
<% 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' %>
<%= 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 %>
@ -101,29 +123,22 @@
</div>
</div>
</fieldset>
<% collapsed = (not (@workflow.before_destroy.present? or @workflow.after_destroy.present?)) %>
<% collapsed = !(@workflow.before_destroy.present? || @workflow.after_destroy.present?) %>
<fieldset class="collapsible <%= collapsed ? 'collapsed' : '' %>">
<legend onclick="toggleFieldset(this);"><%= l(:label_destroy_workflows) %></legend>
<div style="<%= collapsed ? 'display: none' : '' %>">
<legend onclick="toggleFieldset(this);" class="icon icon-collapsed"><%= l(:label_destroy_workflows) %></legend>
<div class="<%= collapsed ? 'custom-workflow-hidden' : '' %>">
<div class="splitcontent">
<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' %>
</div>
<div class="splitcontentright">
<%= 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>
</fieldset>
<% end %>
</fieldset>
<script>
$('.custom_workflow_script').taboverride(2, true);
</script>
</div>
<%= wikitoolbar_for :custom_workflow_description %>
<% content_for :header_tags do %>
<%= javascript_include_tag 'tab_override', plugin: 'redmine_custom_workflows' %>
<% end %>

View File

@ -1,10 +1,33 @@
<%
# encoding: utf-8
#
# Redmine plugin for Custom Workflows
#
# Copyright © 2015-19 Anton Argirov
# Copyright © 2019-20 Karel Pičman <karel.picman@kontron.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
%>
<h2><%= link_to l(:label_custom_workflow_plural), custom_workflows_path %> &#187; <%= @workflow %></h2>
<%= error_messages_for 'workflow' %>
<%= labelled_form_for @workflow do |f| %>
<%= render :partial => 'form', :locals => {:f => f} %>
<%= render partial: 'form', locals: { f: f } %>
<%= submit_tag l(:button_save) %>
<% end %>
<% html_title(l(:label_custom_workflow_plural), @workflow, l(:label_administration)) -%>
<% html_title l(:label_custom_workflow_plural), @workflow, l(:label_administration) %>

View File

@ -1,57 +1,67 @@
<% html_title(l(:label_custom_workflow_plural)) -%>
<%
# encoding: utf-8
#
# Redmine plugin for Custom Workflows
#
# Copyright © 2015-19 Anton Argirov
# Copyright © 2019-20 Karel Pičman <karel.picman@kontron.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
%>
<% html_title l(:label_custom_workflow_plural) %>
<div class="contextual">
<%= link_to l(:label_custom_workflow_import), '#', :class => 'icon icon-import', :onclick => "showModal('import-dialog', '450px'); return false;" %>
<%= link_to l(:label_custom_workflow_new), new_custom_workflow_path, :class => 'icon icon-add' %>
<%= link_to l(:label_custom_workflow_new), new_custom_workflow_path, class: 'icon icon-add' %>
<%= actions_dropdown do %>
<%= link_to l(:label_custom_workflow_import), '#', class: 'icon icon-move',
onclick: "showModal('import-dialog', '450px'); return false;" %>
<% end %>
</div>
<h2><%= l(:label_custom_workflow_plural) %></h2>
<div class="autoscroll">
<% if @workflows.any? %>
<table class="custom-workflows list">
<table class="custom-workflows list" data-hascontextmenu="true">
<thead>
<tr>
<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>
<% @workflows.each do |workflow| %>
<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>
<tr class="<%= cycle('odd', 'even') %> <%= 'disabled' unless workflow.active? %>">
<td class="name"><%= link_to workflow.name, edit_custom_workflow_path(workflow) %></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 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) %>
<% else %>
<%= workflow.projects.map(&:name).join(", ") %>
<% end %>
</td>
<td align="center" nowrap>
<%= reorder_handle(workflow, url: url_for(action: 'reorder', id: workflow) ) %>
</td>
<td class="buttons">
<div class="icon"></div>
<%= reorder_handle workflow, url: url_for(action: 'reorder', id: workflow) %>
<% if workflow.active? %>
<%= link_to(l(:button_custom_workflow_deactivate), custom_workflow_status_path(workflow, active: false), class: 'icon icon-lock', method: :post) %>
<%= link_to l(:button_custom_workflow_deactivate), custom_workflow_status_path(workflow, active: false),
class: 'icon icon-lock', method: :post %>
<% else %>
<%= link_to(l(:button_custom_workflow_activate), custom_workflow_status_path(workflow, active: true), class: 'icon icon-unlock', method: :post) %>
<%= link_to l(:button_activate), custom_workflow_status_path(workflow, active: true),
class: 'icon icon-unlock', method: :post %>
<% end %>
<br/>
<%= link_to(l(:label_custom_workflow_export), export_custom_workflow_path(workflow), class: 'icon icon-export', method: :get) %>
<br/>
<%= link_to(l(:button_delete), workflow, class: 'icon icon-del', data: { confirm: l(:text_are_you_sure) }, confirm: l(:text_are_you_sure), method: :delete) %>
<%= link_to l(:label_custom_workflow_export), export_custom_workflow_path(workflow),
class: 'icon icon-download', method: :get %>
<%= link_to l(:button_delete), workflow, class: 'icon icon-del', data: { confirm: l(:text_are_you_sure) },
confirm: l(:text_are_you_sure), method: :delete %>
</td>
</tr>
<% end %>
@ -61,25 +71,24 @@
<p class="nodata"><%= l(:label_no_data) %></p>
<% end %>
</div>
<div id="import-dialog" style="display: none">
<div id="import-dialog" class="custom-workflow-hidden">
<h3 class="title"><%= l(:label_custom_workflow_import) %></h3>
<%= form_tag import_custom_workflow_path, :multipart => true do %>
<p>
<%= l(:field_custom_workflow_file) %>:
</p>
<p>
<%= file_field_tag 'file', :accept => 'application/xml' %>
<%= file_field_tag 'file', accept: 'application/xml' %>
</p>
<p class="buttons">
<%= submit_tag l(:button_import), :name => nil, :onclick => "hideModal(this);" %>
<%= submit_tag l(:button_cancel), :name => nil, :onclick => "hideModal(this);", :type => 'button' %>
<%= submit_tag l(:button_import), name: nil, onclick: 'hideModal(this);' %>
<%= submit_tag l(:button_cancel), name: nil, onclick: 'hideModal(this);', type: 'button' %>
</p>
<% end %>
</div>
<%= javascript_tag do %>
$(function() { $("table.custom-workflows tbody").positionedItems(); });
$(function() {
$("table.custom-workflows tbody").positionedItems();
});
<% end %>

View File

@ -1,10 +1,33 @@
<%
# encoding: utf-8
#
# Redmine plugin for Custom Workflows
#
# Copyright © 2015-19 Anton Argirov
# Copyright © 2019-20 Karel Pičman <karel.picman@kontron.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
%>
<h2><%= link_to l(:label_custom_workflow_plural), custom_workflows_path %> &#187; <%= l(:label_custom_workflow_new) %></h2>
<%= error_messages_for 'workflow' %>
<%= labelled_form_for @workflow do |f| %>
<%= render :partial => 'form', :locals => {:f => f} %>
<%= render partial: 'form', locals: { f: f } %>
<%= submit_tag l(:button_create) %>
<% end %>
<% html_title(l(:label_custom_workflow_plural), l(:label_custom_workflow_new), l(:label_administration)) -%>
<% html_title l(:label_custom_workflow_plural), l(:label_custom_workflow_new), l(:label_administration) %>

View File

@ -1,3 +1,26 @@
<%
# encoding: utf-8
#
# Redmine plugin for Custom Workflows
#
# Copyright © 2015-19 Anton Argirov
# Copyright © 2019-20 Karel Pičman <karel.picman@kontron.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
%>
<%= form_for @project do %>
<%= hidden_field_tag :tab, 'custom_workflow' %>
<%= hidden_field_tag 'project[custom_workflow_ids][]', '' %>
@ -18,7 +41,7 @@
<% end %>
</label>
</dt>
<dd class="<%= 'disabled' unless w.active? %>"><em><%= textilizable(w.description) %></em></dd>
<dd class="<%= 'disabled' unless w.active? %>"><em><%= textilizable w.description %></em></dd>
<% end %>
</dl>
<% else %>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 247 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 259 B

View File

@ -1,353 +0,0 @@
/* encoding: utf-8
*
* Redmine plugin for Custom Workflows
*
* Copyright © 2015-19 Anton Argirov
* Copyright © 2019-20 Karel Pičman <karel.picman@kontron.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
(function($) {
$.fn.taboverride = function(tabSize, autoIndent) {
this.each(function() {
$(this).data('taboverride', new TabOverride(this, tabSize, autoIndent));
});
};
function TabOverride(element, tabSize, autoIndent) {
let ta = document.createElement('textarea');
ta.value = '\n';
this.newline = ta.value;
this.newlineLen = this.newline.length;
this.autoIndent = autoIndent;
this.inWhitespace = false;
this.element = element;
this.setTabSize(tabSize);
$(element).on('keypress', $.proxy(this.overrideKeyPress, this));
$(element).on('keydown', $.proxy(this.overrideKeyDown, this));
}
TabOverride.prototype = {
/**
* Returns the current tab size. 0 represents the tab character.
*
* @return {Number} the size (length) of the tab string or 0 for the tab character
*
* @name getTabSize
* @function
*/
getTabSize:function () {
return this.aTab === '\t' ? 0 : this.aTab.length;
},
/**
* Sets the tab size for all elements that have Tab Override enabled.
* 0 represents the tab character.
*
* @param {Number} size the tab size (default = 0)
*
* @name setTabSize
* @function
*/
setTabSize:function (size) {
if (!size) { // size is 0 or not specified (or falsy)
this.aTab = '\t';
} else if (typeof size === 'number' && size > 0) {
this.aTab = '';
for (let i = 0; i < size; i += 1) {
this.aTab += ' ';
}
}
},
/**
* Prevents the default action for the keyPress event when tab or enter are
* pressed. Opera (and Firefox) also fire a keypress event when the tab or
* enter key is pressed. Opera requires that the default action be prevented
* on this event or the textarea will lose focus.
*
* @param {Event} e the event object
* @private
*/
overrideKeyPress:function (e) {
let key = e.keyCode;
if ((key === 9 || (key === 13 && this.autoIndent && !this.inWhitespace)) && !e.ctrlKey && !e.altKey) {
e.preventDefault();
}
},
/**
* Inserts / removes tabs and newlines on the keyDown event for the tab or enter key.
*
* @param {Event} e the event object
*
* @private
*/
overrideKeyDown:function (e) {
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
range, // the IE TextRange object
tempRange, // used to calculate selection start and end positions in IE
preNewlines, // the number of newline character sequences before the selection start (for IE)
selNewlines, // the number of newline character sequences within the selection (for IE)
initScrollTop, // initial scrollTop value used to fix scrolling in Firefox
selStart, // the selection start position
selEnd, // the selection end position
sel, // the selected text
startLine, // for multi-line selections, the first character position of the first line
endLine, // for multi-line selections, the last character position of the last line
numTabs, // the number of tabs inserted / removed in the selection
startTab, // if a tab was removed from the start of the first line
preTab, // if a tab was removed before the start of the selection
whitespace, // the whitespace at the beginning of the first selected line
whitespaceLen; // the length of the whitespace at the beginning of the first selected line
// don't do any unnecessary work
if ((key !== 9 && (key !== 13 || !this.autoIndent)) || e.ctrlKey || e.altKey) {
return;
}
// initialize variables used for tab and enter keys
this.inWhitespace = false; // this will be set to true if enter is pressed in the leading whitespace
text = this.element.value;
// this is really just for Firefox, but will be used by all browsers that support
// selectionStart and selectionEnd - whenever the textarea value property is reset,
// Firefox scrolls back to the top - this is used to set it back to the original value
// scrollTop is nonstandard, but supported by all modern browsers
initScrollTop = this.element.scrollTop;
// get the text selection
// prefer the nonstandard document.selection way since it allows for
// automatic scrolling to the cursor via the range.select() method
if (document.selection) { // IE
range = document.selection.createRange();
sel = range.text;
tempRange = range.duplicate();
tempRange.moveToElementText(this.element);
tempRange.setEndPoint('EndToEnd', range);
selEnd = tempRange.text.length;
selStart = selEnd - sel.length;
// whenever the value of the textarea is changed, the range needs to be reset
// IE <9 (and Opera) use both \r and \n for newlines - this adds an extra character
// that needs to be accounted for when doing position calculations with ranges
// these values are used to offset the selection start and end positions
if (this.newlineLen > 1) {
preNewlines = text.slice(0, selStart).split(this.newline).length - 1;
selNewlines = sel.split(this.newline).length - 1;
} else {
preNewlines = selNewlines = 0;
}
} else if (typeof this.element.selectionStart !== 'undefined') {
selStart = this.element.selectionStart;
selEnd = this.element.selectionEnd;
sel = text.slice(selStart, selEnd);
} else {
return; // cannot access textarea selection - do nothing
}
// tab key - insert / remove tab
if (key === 9) {
// initialize tab variables
tab = this.aTab;
tabLen = tab.length;
numTabs = 0;
startTab = 0;
preTab = 0;
// multi-line selection
if (selStart !== selEnd && sel.indexOf('\n') !== -1) {
if (text.charAt(selEnd - 1) === '\n') {
selEnd = selEnd - this.newlineLen;
sel = text.slice(selStart, selEnd);
}
// for multiple lines, only insert / remove tabs from the beginning of each line
// find the start of the first selected line
if (selStart === 0 || text.charAt(selStart - 1) === '\n') {
// the selection starts at the beginning of a line
startLine = selStart;
} else {
// the selection starts after the beginning of a line
// set startLine to the beginning of the first partially selected line
// subtract 1 from selStart in case the cursor is at the newline character,
// for instance, if the very end of the previous line was selected
// add 1 to get the next character after the newline
// if there is none before the selection, lastIndexOf returns -1
// when 1 is added to that it becomes 0 and the first character is used
startLine = text.lastIndexOf('\n', selStart - 1) + 1;
}
// find the end of the last selected line
if (selEnd === text.length || text.charAt(selEnd) === '\n') {
// the selection ends at the end of a line
endLine = selEnd;
} else {
// the selection ends before the end of a line
// set endLine to the end of the last partially selected line
endLine = text.indexOf('\n', selEnd);
if (endLine === -1) {
endLine = text.length;
}
}
// if the shift key was pressed, remove tabs instead of inserting them
if (e.shiftKey) {
if (text.slice(startLine).indexOf(tab) === 0) {
// is this tab part of the selection?
if (startLine === selStart) {
// it is, remove it
sel = sel.slice(tabLen);
} else {
// the tab comes before the selection
preTab = tabLen;
}
startTab = tabLen;
}
this.element.value = text.slice(0, startLine) + text.slice(startLine + preTab, selStart) +
sel.replace(new RegExp('\n' + tab, 'g'), function () {
numTabs += 1;
return '\n';
}) + text.slice(selEnd);
// set start and end points
if (range) { // IE
// setting end first makes calculations easier
range.collapse();
range.moveEnd('character', selEnd - startTab - (numTabs * tabLen) - selNewlines - preNewlines);
range.moveStart('character', selStart - preTab - preNewlines);
range.select();
} else {
// set start first for Opera
this.element.selectionStart = selStart - preTab; // preTab is 0 or tabLen
// move the selection end over by the total number of tabs removed
this.element.selectionEnd = selEnd - startTab - (numTabs * tabLen);
}
} else { // no shift key
numTabs = 1; // for the first tab
// insert tabs at the beginning of each line of the selection
this.element.value = text.slice(0, startLine) + tab + text.slice(startLine, selStart) +
sel.replace(/\n/g, function () {
numTabs += 1;
return '\n' + tab;
}) + text.slice(selEnd);
// set start and end points
if (range) { // IE
range.collapse();
range.moveEnd('character', selEnd + (numTabs * tabLen) - selNewlines - preNewlines);
range.moveStart('character', selStart + tabLen - preNewlines);
range.select();
} else {
// the selection start is always moved by 1 character
this.element.selectionStart = selStart + (selStart == startLine ? 0 : tabLen);
// move the selection end over by the total number of tabs inserted
this.element.selectionEnd = selEnd + (numTabs * tabLen);
this.element.scrollTop = initScrollTop;
}
}
} else { // single line selection
// if the shift key was pressed, remove a tab instead of inserting one
if (e.shiftKey) {
// if the character before the selection is a tab, remove it
if (text.slice(selStart - tabLen).indexOf(tab) === 0) {
this.element.value = text.slice(0, selStart - tabLen) + text.slice(selStart);
// set start and end points
if (range) { // IE
// collapses range and moves it by -1 tab
range.move('character', selStart - tabLen - preNewlines);
range.select();
} else {
this.element.selectionEnd = this.element.selectionStart = selStart - tabLen;
this.element.scrollTop = initScrollTop;
}
}
} else { // no shift key - insert a tab
if (range) { // IE
range.text = tab;
range.select();
} else {
this.element.value = text.slice(0, selStart) + tab + text.slice(selEnd);
this.element.selectionEnd = this.element.selectionStart = selStart + tabLen;
this.element.scrollTop = initScrollTop;
}
}
}
} else if (this.autoIndent) { // Enter key
// insert a newline and copy the whitespace from the beginning of the line
// find the start of the first selected line
if (selStart === 0 || text.charAt(selStart - 1) === '\n') {
// the selection starts at the beginning of a line
// do nothing special
this.inWhitespace = true;
return;
} else {
// see explanation under "multi-line selection" above
startLine = text.lastIndexOf('\n', selStart - 1) + 1;
}
// find the end of the first selected line
endLine = text.indexOf('\n', selStart);
// if no newline is found, set endLine to the end of the text
if (endLine === -1) {
endLine = text.length;
}
// get the whitespace at the beginning of the first selected line (spaces and tabs only)
whitespace = text.slice(startLine, endLine).match(/^[ \t]*/)[0];
whitespaceLen = whitespace.length;
// the cursor (selStart) is in the whitespace at beginning of the line
// do nothing special
if (selStart < startLine + whitespaceLen) {
this.inWhitespace = true;
return;
}
if (range) { // IE
// insert the newline and whitespace
range.text = '\n' + whitespace;
range.select();
} else {
// insert the newline and whitespace
this.element.value = text.slice(0, selStart) + '\n' + whitespace + text.slice(selEnd);
// Opera uses \r\n for a newline, instead of \n,
// so use newlineLen instead of a hard-coded value
this.element.selectionEnd = this.element.selectionStart = selStart + this.newlineLen + whitespaceLen;
this.element.scrollTop = initScrollTop;
}
}
e.preventDefault();
}
};
})(jQuery);
function checkAndDisable(id, checked) {
if (checked) {
$('#' + id).find('input[type=checkbox]').attr('checked', true).attr('disabled', true);
} else {
$('#' + id).find('input[type=checkbox]').removeAttr('checked').removeAttr('disabled');
}
}

View File

@ -23,44 +23,27 @@
color: silver;
}
table.list.custom-workflows td {
vertical-align: middle;
}
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 {
color: silver;
}
#custom_workflow_description, #custom_workflow_name {
width: 98%;
}
.custom_workflow_script {
width: 98%;
font-size: 11px;
}
#custom_workflow_enabled_projects ul.root {
max-height: 400px;
overflow-y: auto;
}
/* Icons */
.icon-import {
background-image: url(../images/export.png);
.custom-workflow-hidden {
display: none;
}
.icon-export {
background-image: url(../images/import.png);
.custom-workflow-info {
text-align: justify;
margin-bottom: 1em;
}
div#custom_workflow_scripts label {
font-weight: normal;
text-align: left;
margin-left: 0;
width: auto;
}

View File

@ -27,7 +27,6 @@ cs:
label_custom_workflow_plural: Uživatelské procesy
label_custom_workflow_new: Vytvořit uživatelský proces
label_workflow_scripts: Skript procesu
label_enabled_projects: Povoleno pro projekt(y)
label_custom_workflow_export: Export
label_custom_workflow_import: Importovat proces
label_save_workflows: Uložení sledovaných objektů
@ -35,8 +34,6 @@ cs:
label_add_workflows: Přidání sledovaných objektů do sezanmu
label_remove_workflows: Odstranění sledovaných objektů za seznamu
button_import: Import
button_custom_workflow_activate: Aktivovat
button_custom_workflow_deactivate: Deaktivovat
field_after_save: Skript spuštěný po uložení sledovaného objektu
@ -49,11 +46,9 @@ cs:
field_before_remove: Skript spuštěný před odstraněním sledovaného objektu ze seznamu
field_shared_code: Sdílený kód
field_observable: Sledovaný objekt
field_is_enabled: Povoleno
field_enabled_for_all_projects: Povoleno pro všechny projekty
field_custom_workflow_author: Autorův e-mail
field_custom_workflow_file: Select the XML file previously exported process
field_custom_workflow_active: Aktivní
field_custom_workflow:
script: Skript
@ -70,8 +65,8 @@ cs:
scripts_absent: Musí být definován alespoň jeden skript
text_select_project_custom_workflows: Vyberte uživatelský skript projektu
text_custom_workflow_before_save_note: "Zde můžete měnit vlastnosti úkolu. Ve skriptu nevytvářejte ani neměňte
související úkoly. Pro ukončení skriptu chybou použijte: `raise WorkflowError, 'Zpráva uživateli'`."
text_custom_workflow_before_save_note: Zde můžete měnit vlastnosti úkolu. Ve skriptu nevytvářejte ani neměňte
související úkoly. Pro ukončení skriptu chybou použijte `raise WorkflowError, 'Zpráva uživateli'`.
text_custom_workflow_after_save_note: Zde můžete aktualizovat nebo vytvářet souvissející úkoly. Mějte na paměti, že
tento skript bude také vykonán pro nově vytvořené úkoly. Takže nezapomeňte udělat vhodné kontroly pro zabránění
rekurzivnímu volání.

View File

@ -20,87 +20,101 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
en:
project_module_custom_workflows_module: "Custom workflows"
permission_manage_project_workflow: "Manage project custom workflows"
project_module_custom_workflows_module: Custom workflows
permission_manage_project_workflow: Manage project custom workflows
label_custom_workflow: "Custom workflows"
label_custom_workflow_plural: "Custom workflows"
label_custom_workflow_new: "Create a custom workflow"
label_workflow_scripts: "Workflow scripts"
label_enabled_projects: "Enabled for project(s)"
label_custom_workflow_export: "Export"
label_custom_workflow_import: "Import workflow"
label_save_workflows: "Saving observable objects"
label_destroy_workflows: "Destroying observable objects"
label_add_workflows: "Adding observable objects to collection"
label_remove_workflows: "Removing observable objects from collection"
label_custom_workflow: Custom workflow
label_custom_workflow_plural: Custom workflows
label_custom_workflow_new: Create a custom workflow
label_workflow_scripts: Workflow scripts
label_custom_workflow_export: Export
label_custom_workflow_import: Import workflow
label_save_workflows: Saving observable objects
label_destroy_workflows: Destroying observable objects
label_add_workflows: Adding observable objects to collection
label_remove_workflows: Removing observable objects from collection
button_import: "Import"
button_custom_workflow_activate: "Activate"
button_custom_workflow_deactivate: "Deactivate"
button_custom_workflow_deactivate: Deactivate
field_after_save: "Workflow script executable after saving observable object"
field_before_save: "Workflow script executable before saving observable object"
field_after_destroy: "Workflow script executable after destroying observable object"
field_before_destroy: "Workflow script executable before destroying 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"
field_custom_workflow_file: "Select the XML file previously exported process"
field_custom_workflow_active: "Active"
field_after_save: Workflow script executable after saving observable object
field_before_save: Workflow script executable before saving observable object
field_after_destroy: Workflow script executable after destroying observable object
field_before_destroy: Workflow script executable before destroying 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_enabled_for_all_projects: Enabled for all projects
field_custom_workflow_author: Author's e-mail
field_custom_workflow_file: Select the XML file previously exported process
field_custom_workflow:
script: "Workflow script"
script: Workflow script
notice_successful_import: "Custom workflow has successfully imported"
notice_successful_status_change: "Status has successfully changed"
error_failed_import: "Error importing custom workflow (unknown format? please see log)"
notice_successful_import: Custom workflow has successfully imported
notice_successful_status_change: Status has successfully changed
error_failed_import: Error importing custom workflow (unknown format? please see log)
activerecord:
errors:
messages:
invalid_script: "contains error: %{error}"
custom_workflow_error: "Custom workflow error (please contact administrator)"
custom_workflow_error: Custom workflow error (Please contact an administrator)
new_status_invalid: "transition from '%{old_status}' to '%{new_status}' is prohibited"
scripts_absent: "At least one script should be defined"
scripts_absent: At least one script should be defined
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_issue_code_note: Scripts are executed in the context of Issue object 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: Scripts are executed in the context of User object when user object changes (destroys). Use methods and properties of the user directly (or through "self")
text_custom_workflow_group_code_note: Scripts are executed in the context of Group object when group object changes (destroys). 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_custom_workflow_attachment_code_note: Scripts are executed in the context of Attachment object when attachment object changes (destroys). Use methods and properties of the attachment object directly (or through "self"). Note that these scripts will affect all attachment types (issue, document, wiki pages and etc), so you should check 'container_type' field additionally in your script or select specific "... Attachments" observable object.
text_custom_workflow_issue_attachments_code_note: These scripts are executed when attachment being added to issue/removed from issue. Use variables @issue and @attachment to access appropriate objects in your scripts.
text_custom_workflow_project_code_note: Scripts are executed in the context of Project object when project object changes (destroys). Use methods and properties of the project directly (or through "self")
text_custom_workflow_project_attachments_code_note: These scripts are executed when a file being added to project/removed from project. Use variables @project and @attachment to access appropriate objects in your scripts.
text_custom_workflow_wiki_content_code_note: Scripts are executed in the context of Wiki Content object when project object changes (destroys). Use methods and properties of the project directly (or through "self")
text_custom_workflow_wiki_page_attachments_code_note: These scripts are executed when a file being added to wiki page/removed from wiki page. Use variables @page and @attachment to access appropriate objects in your scripts.
text_custom_workflow_time_entry_code_note: Scripts are executed in the context of TimeEntry object when time enty object changes (destroys). Use methods and properties of the time entry directly (or through "self")
text_custom_workflow_version_code_note: Scripts are executed in the context of Version object when version object changes (destroys). Use methods and properties of the version directly (or through "self")
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_issue_code_note: Scripts are executed in the context of Issue object 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: Scripts are executed in the context of User object when user object changes
(destroys). Use methods and properties of the user directly (or through `self`)
text_custom_workflow_group_code_note: Scripts are executed in the context of Group object when group object changes
(destroys). 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_custom_workflow_attachment_code_note: Scripts are executed in the context of Attachment object when attachment
object changes (destroys). Use methods and properties of the attachment object directly (or through `self`). Note that
these scripts will affect all attachment types (issue, document, wiki pages and etc), so you should check
'container_type' field additionally in your script or select specific '... Attachments' observable object.
text_custom_workflow_issue_attachments_code_note: These scripts are executed when attachment being added to
issue/removed from issue. Use variables @issue and @attachment to access appropriate objects in your scripts.
text_custom_workflow_project_code_note: Scripts are executed in the context of Project object when project object
changes (destroys). Use methods and properties of the project directly (or through `self`)
text_custom_workflow_project_attachments_code_note: These scripts are executed when a file being added to
project/removed from project. Use variables @project and @attachment to access appropriate objects in your scripts.
text_custom_workflow_wiki_content_code_note: Scripts are executed in the context of Wiki Content object when project
object changes (destroys). Use methods and properties of the project directly (or through `self`)
text_custom_workflow_wiki_page_attachments_code_note: These scripts are executed when a file being added to wiki
page/removed from wiki page. Use variables @page and @attachment to access appropriate objects in your scripts.
text_custom_workflow_time_entry_code_note: Scripts are executed in the context of TimeEntry object when time enty
object changes (destroys). Use methods and properties of the time entry directly (or through `self`)
text_custom_workflow_version_code_note: Scripts are executed in the context of Version object when version object
changes (destroys). Use methods and properties of the version directly (or through `self`)
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
custom_workflow_observable_shared: "<shared code>"
custom_workflow_observable_issue: "Issue"
custom_workflow_observable_issue_attachments: "Issue Attachments"
custom_workflow_observable_group: "Group"
custom_workflow_observable_user: "User"
custom_workflow_observable_attachment: "Attachment"
custom_workflow_observable_project: "Project"
custom_workflow_observable_project_attachments: "Project Attachments / Files"
custom_workflow_observable_wiki_content: "Wiki Content"
custom_workflow_observable_wiki_page_attachments: "Wiki Page Attachments"
custom_workflow_observable_group_users: "Group Users"
custom_workflow_observable_time_entry: "Time Entry"
custom_workflow_observable_version: "Version"
custom_workflow_observable_shared: <shared code>
custom_workflow_observable_issue: Issue
custom_workflow_observable_issue_attachments: Issue Attachments
custom_workflow_observable_group: Group
custom_workflow_observable_user: User
custom_workflow_observable_attachment: Attachment
custom_workflow_observable_project: Project
custom_workflow_observable_project_attachments: Project Attachments / Files
custom_workflow_observable_wiki_content: Wiki Content
custom_workflow_observable_wiki_page_attachments: Wiki Page Attachments
custom_workflow_observable_group_users: Group Users
custom_workflow_observable_time_entry: Time Entry
custom_workflow_observable_version: Version

View File

@ -20,63 +20,101 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
pt-BR:
project_module_custom_workflows_module: "Fluxo de Trabalho Personalizado"
permission_manage_project_workflow: "Gerenciar fluxos de trabalho personalizados de projeto"
project_module_custom_workflows_module: Fluxo de Trabalho Personalizado
permission_manage_project_workflow: Gerenciar fluxos de trabalho personalizados de projeto
label_custom_workflow: "Fluxo de Trabalho personalizado"
label_custom_workflow_plural: "Fluxos de Trabalho personalizados"
label_custom_workflow_new: "Criar um fluxo de trabalho personalizado"
label_workflow_scripts: "Scripts de fluxo de trabalho"
label_enabled_projects: "Habilitado para o(s) projeto(s)"
label_custom_workflow_export: "Exportar"
label_custom_workflow_import: "Importar fluxo de trabalho"
label_custom_workflow: Fluxo de Trabalho personalizado
label_custom_workflow_plural: Fluxos de Trabalho personalizados
label_custom_workflow_new: Criar um fluxo de trabalho personalizado
label_workflow_scripts: Scripts de fluxo de trabalho
label_custom_workflow_export: Exportar
label_custom_workflow_import: Importar fluxo de trabalho
label_save_workflows: Saving observable objects
label_destroy_workflows: Destroying observable objects
label_add_workflows: Adding observable objects to collection
label_remove_workflows: Removing observable objects from collection
button_import: "Importar"
button_custom_workflow_activate: "Ativar"
button_custom_workflow_deactivate: "Desativar"
button_custom_workflow_deactivate: Desativar
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"
field_custom_workflow_file: "Selecione o arquivo XML exportado anteriormente"
field_custom_workflow_active: "Ativo"
field_after_save: Workflow script executable after saving observable object
field_before_save: Workflow script executable before saving observable object
field_after_destroy: Workflow script executable after destroying observable object
field_before_destroy: Workflow script executable before destroying 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_enabled_for_all_projects: Ativado para todos os projetos
field_custom_workflow_author: E-mail do autor
field_custom_workflow_file: Selecione o arquivo XML exportado anteriormente
field_custom_workflow:
script: "Script do Fluxo de Trabalho"
script: Script do Fluxo de Trabalho
notice_successful_import: "Fluxo de Trabalho Personalizado foi importado com sucesso"
notice_successful_status_change: "Situação foi alterada com sucesso"
error_failed_import: "Erro ao importar o fluxo de trabalho customizado (Formato desconhecido? Por favor verifique o log)"
notice_successful_import: Fluxo de Trabalho Personalizado foi importado com sucesso
notice_successful_status_change: Situação foi alterada com sucesso
error_failed_import: Erro ao importar o fluxo de trabalho customizado (Formato desconhecido? Por favor verifique o log)
activerecord:
errors:
messages:
invalid_script: "contém erro: %{error}"
custom_workflow_error: "Erro no fluxo de trabalho customizado (por favor contate o administrador)"
custom_workflow_error: Erro no fluxo de trabalho customizado (Por favor contate o administrador)
new_status_invalid: "Transição de '%{old_status}' para '%{new_status}' é proibida"
scripts_absent: At least one script should be defined
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_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_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 `raise 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_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_custom_workflow_attachment_code_note: Scripts are executed in the context of Attachment object when attachment
object changes (destroys). Use methods and properties of the attachment object directly (or through `self`). Note that
these scripts will affect all attachment types (issue, document, wiki pages and etc), so you should check
'container_type' field additionally in your script or select specific '... Attachments' observable object.
text_custom_workflow_issue_attachments_code_note: These scripts are executed when attachment being added to
issue/removed from issue. Use variables @issue and @attachment to access appropriate objects in your scripts.
text_custom_workflow_project_code_note: Scripts are executed in the context of Project object when project object
changes (destroys). Use methods and properties of the project directly (or through `self`)
text_custom_workflow_project_attachments_code_note: These scripts are executed when a file being added to
project/removed from project. Use variables @project and @attachment to access appropriate objects in your scripts.
text_custom_workflow_wiki_content_code_note: Scripts are executed in the context of Wiki Content object when project
object changes (destroys). Use methods and properties of the project directly (or through `self`)
text_custom_workflow_wiki_page_attachments_code_note: These scripts are executed when a file being added to wiki
page/removed from wiki page. Use variables @page and @attachment to access appropriate objects in your scripts.
text_custom_workflow_time_entry_code_note: Scripts are executed in the context of TimeEntry object when time enty
object changes (destroys). Use methods and properties of the time entry directly (or through `self`)
text_custom_workflow_version_code_note: Scripts are executed in the context of Version object when version object
changes (destroys). Use methods and properties of the version directly (or through `self`)
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"
custom_workflow_observable_shared: <shared code>
custom_workflow_observable_issue: Issue
custom_workflow_observable_issue_attachments: Issue Attachments
custom_workflow_observable_group: Group
custom_workflow_observable_user: User
custom_workflow_observable_attachment: Attachment
custom_workflow_observable_project: Project
custom_workflow_observable_project_attachments: Project Attachments / Files
custom_workflow_observable_wiki_content: Wiki Content
custom_workflow_observable_wiki_page_attachments: Wiki Page Attachments
custom_workflow_observable_group_users: Group Users
custom_workflow_observable_time_entry: Time Entry
custom_workflow_observable_version: Version

View File

@ -20,87 +20,101 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
ru:
project_module_custom_workflows_module: "Пользовательские рабочие процессы"
permission_manage_project_workflow: "Управление пользовательскими рабочими процессами в проекте"
project_module_custom_workflows_module: Пользовательские рабочие процессы
permission_manage_project_workflow: Управление пользовательскими рабочими процессами в проекте
label_custom_workflow: "Пользовательский рабочий процесс"
label_custom_workflow_plural: "Пользовательские рабочие процессы"
label_custom_workflow_new: "Новый процесс"
label_workflow_scripts: "Сценарии"
label_enabled_projects: "Разрешен в проектах"
label_custom_workflow_export: "Экспорт"
label_custom_workflow_import: "Импорт процесса"
label_save_workflows: "Сохранение наблюдаемых объектов"
label_destroy_workflows: "Уничтожение наблюдаемых объектов"
label_add_workflows: "Добавление наблюдаемых объектов в коллекцию"
label_remove_workflows: "Удаление наблюдаемых объектов из коллекции"
label_custom_workflow: Пользовательский рабочий процесс
label_custom_workflow_plural: Пользовательские рабочие процессы
label_custom_workflow_new: Новый процесс
label_workflow_scripts: Сценарии
label_custom_workflow_export: Экспорт
label_custom_workflow_import: Импорт процесса
label_save_workflows: Сохранение наблюдаемых объектов
label_destroy_workflows: Уничтожение наблюдаемых объектов
label_add_workflows: Добавление наблюдаемых объектов в коллекцию
label_remove_workflows: Удаление наблюдаемых объектов из коллекции
button_import: "Импортировать"
button_custom_workflow_activate: "Активировать"
button_custom_workflow_deactivate: "Деактивировать"
button_custom_workflow_deactivate: Деактивировать
field_after_save: "Сценарий выполняемый после сохранения наблюдаемого объекта"
field_before_save: "Сценарий выполняемый перед сохранением наблюдаемого объекта"
field_after_destroy: "Сценарий выполняемый после уничтожения наблюдаемого объекта"
field_before_destroy: "Сценарий выполняемый перед уничтожением наблюдаемого объекта"
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 адрес автора"
field_custom_workflow_file: "Выберите XML файл ранее экспортированного процесса"
field_custom_workflow_active: "Активен"
field_after_save: Сценарий выполняемый после сохранения наблюдаемого объекта
field_before_save: Сценарий выполняемый перед сохранением наблюдаемого объекта
field_after_destroy: Сценарий выполняемый после уничтожения наблюдаемого объекта
field_before_destroy: Сценарий выполняемый перед уничтожением наблюдаемого объекта
field_after_add: Сценарий выполняемый после добавления наблюдаемого объекта в коллекцию
field_before_add: Сценарий выполняемый перед добавлением наблюдаемого объекта в коллекцию
field_after_remove: Сценарий выполняемый после удаления наблюдаемого объекта из коллекции
field_before_remove: Сценарий выполняемый перед удалением наблюдаемого объекта из коллекции
field_shared_code: Общий код
field_observable: Наблюдаемый объект
field_enabled_for_all_projects: Разрешен для всех проектов
field_custom_workflow_author: E-Mail адрес автора
field_custom_workflow_file: Выберите XML файл ранее экспортированного процесса
field_custom_workflow:
script: "Сценарий"
script: Сценарий
notice_successful_import: "Рабочий процесс успешно импортирован"
notice_successful_status_change: "Статус успешно изменен"
error_failed_import: "Ошибка импорта рабочего процесса (неверный формат? смотри журнал)"
notice_successful_import: Рабочий процесс успешно импортирован
notice_successful_status_change: Статус успешно изменен
error_failed_import: Ошибка импорта рабочего процесса (неверный формат? смотри журнал)
activerecord:
errors:
messages:
invalid_script: "содержит ошибку: %{error}"
custom_workflow_error: "Ошибка в сценарии рабочего процесса (обратитесь к администратору)"
custom_workflow_error: Ошибка в сценарии рабочего процесса (Обратитесь к администратору)
new_status_invalid: "- переход от '%{old_status}' к '%{new_status}' невозможен"
scripts_absent: "Хотя бы один скрипт должен быть определен"
scripts_absent: Хотя бы один скрипт должен быть определен
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_issue_code_note: Эти сценарии исполняются в контексте задачи, как и обычные обратные вызовы before_save и after_save. Поэтому используйте методы и свойства задачи (Issue) напрямую или через ключевое слово self.
text_custom_workflow_shared_code_note: Этот код будет исполняться перед любым другим процессом и может содержать общий код, например, функции и классы, необходимые для работы других процессов.
text_custom_workflow_user_code_note: Эти сценарии исполняются в контексте объекта пользователя когда объект пользователя изменяется (удаляется). Используйте методы и свойства объекта пользователя (User) напрямую или через ключевое слово self.
text_custom_workflow_group_code_note: Эти сценарии исполняются в контексте объекта группы когда объект группы изменяется (удаляется). Используйте методы и свойства объекта группы (Group) напрямую или через ключевое слово self.
text_custom_workflow_group_users_code_note: Эти сценарии выполняются когда пользователь добавляется в группу/удаляется из группы. Используйте переменные @user и @group для доступа к соответствующим объектам из Ваших сценариев.
text_custom_workflow_attachment_code_note: Эти сценарии исполняются в контексте объекта вложения когда объект вложения изменяется (удаляется). Используйте методы и свойства объекта вложения (Attachment) напрямую или через ключевое слово self. Обратите внимание на то, что данные сценарии выполняются при сохранении (удалении) вложения любого типа (задача, документ, страница Wiki и т.д.), поэтому следует дополнительно проверять в коде поле container_type либо в качестве наблюдаемого объекта выбрать конкретный тип вложения.
text_custom_workflow_issue_attachments_code_note: Эти сценарии выполняются когда вложение прикладывается к задаче/удаляется из задачи. Используйте переменные @issue и @attachment для доступа к соответствующим объектам из Ваших сценариев.
text_custom_workflow_project_code_note: Эти сценарии исполняются в контексте объекта проекта когда объект проекта изменяется (удаляется). Используйте методы и свойства объекта группы (Project) напрямую или через ключевое слово self.
text_custom_workflow_project_attachments_code_note: Эти сценарии выполняются когда файл загружается в проект/удаляется из проекта. Используйте переменные @project и @attachment для доступа к соответствующим объектам из Ваших сценариев.
text_custom_workflow_wiki_content_code_note: Эти сценарии исполняются в контексте объекта Wiki содержания когда объект Wiki содержания изменяется (удаляется). Используйте методы и свойства объекта содержания Wiki (WikiContent) напрямую или через ключевое слово self.
text_custom_workflow_wiki_page_attachments_code_note: Эти сценарии выполняются когда файл загружается на Wiki страницу/удаляется с Wiki страницы. Используйте переменные @page и @attachment для доступа к соответствующим объектам из Ваших сценариев.
text_custom_workflow_time_entry_code_note: Эти сценарии исполняются в контексте объекта затраченного времени когда объект изменяется (удаляется). Используйте методы и свойства объекта затраченного времени (TimeEntry) напрямую или через ключевое слово self.
text_custom_workflow_version_code_note: Эти сценарии исполняются в контексте объекта версии когда объект изменяется (удаляется). Используйте методы и свойства объекта версии (Version) напрямую или через ключевое слово self.
text_custom_workflow_before_save_note: Здесь вы можете изменять свойства задачи. Не создавайте и не обновляйте
связанные задачи в этом сценарии. Чтобы завершить сценарий с произвольной ошибкой, используйте `raise WorkflowError, 'Message to user'`.
text_custom_workflow_after_save_note: Вы можете обновлять и создавать задачи (в том числе и связанные задачи) здесь.
Обратите внимание, что данный сценарий будет также выполняться и для вновь создаваемых задач. Поэтому используйте
дополнительные проверки, чтобы избежать бесконечной рекурсии.
text_custom_workflow_issue_code_note: Эти сценарии исполняются в контексте задачи, как и обычные обратные вызовы
before_save и after_save. Поэтому используйте методы и свойства задачи (Issue) напрямую или через ключевое слово
self.
text_custom_workflow_shared_code_note: Этот код будет исполняться перед любым другим процессом и может содержать общий
код, например, функции и классы, необходимые для работы других процессов.
text_custom_workflow_user_code_note: Эти сценарии исполняются в контексте объекта пользователя когда объект
пользователя изменяется (удаляется). Используйте методы и свойства объекта пользователя (User) напрямую или через ключевое слово `self`.
text_custom_workflow_group_code_note: Эти сценарии исполняются в контексте объекта группы когда объект группы
изменяется (удаляется). Используйте методы и свойства объекта группы (Group) напрямую или через ключевое слово `self`.
text_custom_workflow_group_users_code_note: Эти сценарии выполняются когда пользователь добавляется в группу/удаляется
из группы. Используйте переменные @user и @group для доступа к соответствующим объектам из Ваших сценариев.
text_custom_workflow_attachment_code_note: Эти сценарии исполняются в контексте объекта вложения когда объект вложения
изменяется (удаляется). Используйте методы и свойства объекта вложения (Attachment) напрямую или через ключевое
слово self. Обратите внимание на то, что данные сценарии выполняются при сохранении (удалении) вложения любого типа
(задача, документ, страница Wiki и т.д.), поэтому следует дополнительно проверять в коде поле container_type либо в качестве наблюдаемого объекта выбрать конкретный тип вложения.
text_custom_workflow_issue_attachments_code_note: Эти сценарии выполняются когда вложение прикладывается
к задаче/удаляется из задачи. Используйте переменные @issue и @attachment для доступа к соответствующим объектам из Ваших сценариев.
text_custom_workflow_project_code_note: Эти сценарии исполняются в контексте объекта проекта когда объект проекта
изменяется (удаляется). Используйте методы и свойства объекта группы (Project) напрямую или через ключевое слово `self`.
text_custom_workflow_project_attachments_code_note: Эти сценарии выполняются когда файл загружается в проект/удаляется
из проекта. Используйте переменные @project и @attachment для доступа к соответствующим объектам из Ваших сценариев.
text_custom_workflow_wiki_content_code_note: Эти сценарии исполняются в контексте объекта Wiki содержания когда объект
Wiki содержания изменяется (удаляется). Используйте методы и свойства объекта содержания Wiki (WikiContent) напрямую или через ключевое слово self.
text_custom_workflow_wiki_page_attachments_code_note: Эти сценарии выполняются когда файл загружается на Wiki
страницу/удаляется с Wiki страницы. Используйте переменные @page и @attachment для доступа к соответствующим объектам из Ваших сценариев.
text_custom_workflow_time_entry_code_note: Эти сценарии исполняются в контексте объекта затраченного времени когда
объект изменяется (удаляется). Используйте методы и свойства объекта затраченного времени (TimeEntry) напрямую или через ключевое слово self.
text_custom_workflow_version_code_note: Эти сценарии исполняются в контексте объекта версии когда объект изменяется
(удаляется). Используйте методы и свойства объекта версии (Version) напрямую или через ключевое слово `self`.
text_no_enabled_projects: Нет проектов
text_custom_workflow_author: Будет использован в XML файле при экспорте
text_custom_workflow_disabled: отключен администратором
text_custom_workflow_is_for_all: разрешен для всех проектов
custom_workflow_observable_shared: "<общий код>"
custom_workflow_observable_issue: "Задача"
custom_workflow_observable_issue_attachments: "Вложения задач"
custom_workflow_observable_group: "Группа"
custom_workflow_observable_user: "Пользователь"
custom_workflow_observable_attachment: "Вложение"
custom_workflow_observable_project: "Проект"
custom_workflow_observable_project_attachments: "Вложения проекта / Файлы"
custom_workflow_observable_wiki_content: "Содержание Wiki"
custom_workflow_observable_wiki_page_attachments: "Вложения страниц Wiki"
custom_workflow_observable_group_users: "Пользователи группы"
custom_workflow_observable_time_entry: "Затраченное время"
custom_workflow_observable_version: "Версия"
custom_workflow_observable_shared: <общий код>
custom_workflow_observable_issue: Задача
custom_workflow_observable_issue_attachments: Вложения задач
custom_workflow_observable_group: Группа
custom_workflow_observable_user: Пользователь
custom_workflow_observable_attachment: Вложение
custom_workflow_observable_project: Проект
custom_workflow_observable_project_attachments: Вложения проекта / Файлы
custom_workflow_observable_wiki_content: Содержание Wiki
custom_workflow_observable_wiki_page_attachments: Вложения страниц Wiki
custom_workflow_observable_group_users: Пользователи группы
custom_workflow_observable_time_entry: Затраченное время
custom_workflow_observable_version: Версия

View File

@ -28,9 +28,10 @@ RedmineApp::Application.routes.draw do
end
end
post '/custom_workflows/import', :to => 'custom_workflows#import', :as => 'import_custom_workflow'
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'
post '/custom_workflows/import', to: 'custom_workflows#import', as: 'import_custom_workflow'
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

@ -22,12 +22,12 @@
class CreateCustomWorkflows < ActiveRecord::Migration[4.2]
def change
create_table :custom_workflows, :force => true do |t|
create_table :custom_workflows, force: true do |t|
t.references :project
t.text :script, :null => true, :default => nil
t.text :script, null: true, default: nil
t.boolean :is_enabled
end
add_index :custom_workflows, [:project_id], :unique => true
add_index :custom_workflows, [:project_id], unique: true
end
end

View File

@ -24,9 +24,9 @@ class AlterCustomWorkflows < ActiveRecord::Migration[4.2]
def self.up
remove_column :custom_workflows, :project_id
remove_column :custom_workflows, :is_enabled
add_column :custom_workflows, :name, :string, :null => false, :default => ''
add_column :custom_workflows, :description, :string, :null => false, :default => ''
add_column :custom_workflows, :position, :integer, :null => false, :default => 1
add_column :custom_workflows, :name, :string, null: false, default: ''
add_column :custom_workflows, :description, :string, null: false, default: ''
add_column :custom_workflows, :position, :integer, null: false, default: 1
end
end

View File

@ -22,7 +22,7 @@
class CreateCustomWorkflowsProjects < ActiveRecord::Migration[4.2]
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 :custom_workflow
end

View File

@ -21,7 +21,7 @@
class ChangeCustomWorkflowsDescriptionType < ActiveRecord::Migration[4.2]
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

View File

@ -23,8 +23,8 @@ class AddAfterSaveToCustomWorkflows < ActiveRecord::Migration[4.2]
def up
rename_column :custom_workflows, :script, :before_save
change_column :custom_workflows, :before_save, :text, :null => true
add_column :custom_workflows, :after_save, :text, :null => true, :after => :before_save
change_column :custom_workflows, :before_save, :text, null: true
add_column :custom_workflows, :after_save, :text, null: true, after: :before_save
end
def down

View File

@ -22,7 +22,7 @@
class AddIsForAllToCustomWorkflows < ActiveRecord::Migration[4.2]
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

View File

@ -22,8 +22,8 @@
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
change_column :custom_workflows, :before_save, :text, null: true, default: nil
change_column :custom_workflows, :after_save, :text, null: true, default: nil
end
end

View File

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

View File

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

View File

@ -22,7 +22,7 @@
class AddActiveFieldToCustomWorkflows < ActiveRecord::Migration[4.2]
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

@ -22,7 +22,7 @@
class AddObservableFieldToCustomWorkflows < ActiveRecord::Migration[4.2]
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

@ -22,11 +22,11 @@
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
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
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

View File

@ -22,8 +22,8 @@
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
add_column :custom_workflows, :before_destroy, :text, null: true
add_column :custom_workflows, :after_destroy, :text, null: true
end
end

View File

@ -36,7 +36,7 @@ Redmine::Plugin.register :redmine_custom_workflows do
requires_redmine version_or_higher: '4.1.0'
permission :manage_project_workflow, {}, :require => :member
permission :manage_project_workflow, {}, require: :member
end
unless Redmine::Plugin.installed?(:easy_extensions)

View File

@ -24,19 +24,11 @@ module RedmineCustomWorkflows
class Hooks < Redmine::Hook::ViewListener
def view_layouts_base_html_head(context)
return if defined?(EasyExtensions)
def view_layouts_base_html_head(context={})
return unless context[:controller].class.name.match?(/^(CustomWorkflows|Projects)/)
"\n".html_safe + stylesheet_link_tag('custom_workflows.css', plugin: :redmine_custom_workflows)
end
def easy_extensions_javascripts_hook(context={})
context[:template].require_asset('tab_override.js')
end
def easy_extensions_stylesheets_hook(context={})
context[:template].require_asset('custom_workflows.css')
end
end
end

View File

@ -36,8 +36,8 @@ module RedmineCustomWorkflows
def before_save_custom_workflows
@attachment = self
@saved_attributes = attributes.dup
CustomWorkflow.run_shared_code(self)
CustomWorkflow.run_custom_workflows(:attachment, self, :before_save)
CustomWorkflow.run_shared_code self
CustomWorkflow.run_custom_workflows :attachment, self, :before_save
throw :abort if errors.any?
errors.empty? && (@saved_attributes == attributes || valid?)
ensure
@ -45,15 +45,15 @@ module RedmineCustomWorkflows
end
def after_save_custom_workflows
CustomWorkflow.run_custom_workflows(:attachment, self, :after_save)
CustomWorkflow.run_custom_workflows :attachment, self, :after_save
end
def before_destroy_custom_workflows
CustomWorkflow.run_custom_workflows(:attachment, self, :before_destroy)
CustomWorkflow.run_custom_workflows :attachment, self, :before_destroy
end
def after_destroy_custom_workflows
CustomWorkflow.run_custom_workflows(:attachment, self, :after_destroy)
CustomWorkflow.run_custom_workflows :attachment, self, :after_destroy
end
end

View File

@ -32,10 +32,10 @@ module RedmineCustomWorkflows
after_destroy :after_destroy_custom_workflows
def self.users_callback(event, group, user)
group.instance_variable_set(:@group, group)
group.instance_variable_set(:@user, 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)
CustomWorkflow.run_custom_workflows :group_users, group, event
end
[:before_add, :before_remove, :after_add, :after_remove].each do |observable|
send("#{observable}_for_users") << if Rails::VERSION::MAJOR >= 4
@ -50,8 +50,8 @@ module RedmineCustomWorkflows
def before_save_custom_workflows
@group = self
@saved_attributes = attributes.dup
CustomWorkflow.run_shared_code(self)
CustomWorkflow.run_custom_workflows(:group, self, :before_save)
CustomWorkflow.run_shared_code self
CustomWorkflow.run_custom_workflows :group, self, :before_save
throw :abort if errors.any?
errors.empty? && (@saved_attributes == attributes || valid?)
ensure
@ -59,15 +59,15 @@ module RedmineCustomWorkflows
end
def after_save_custom_workflows
CustomWorkflow.run_custom_workflows(:group, self, :after_save)
CustomWorkflow.run_custom_workflows :group, self, :after_save
end
def before_destroy_custom_workflows
CustomWorkflow.run_custom_workflows(:group, self, :before_destroy)
CustomWorkflow.run_custom_workflows :group, self, :before_destroy
end
def after_destroy_custom_workflows
CustomWorkflow.run_custom_workflows(:group, self, :after_destroy)
CustomWorkflow.run_custom_workflows :group, self, :after_destroy
end
end

View File

@ -33,10 +33,10 @@ module RedmineCustomWorkflows
validate :validate_status
def self.attachments_callback(event, issue, attachment)
issue.instance_variable_set(:@issue, issue)
issue.instance_variable_set(:@attachment, attachment)
issue.instance_variable_set :@issue, issue
issue.instance_variable_set :@attachment, attachment
CustomWorkflow.run_shared_code(issue) if event.to_s.starts_with? 'before_'
CustomWorkflow.run_custom_workflows(:issue_attachments, issue, event)
CustomWorkflow.run_custom_workflows :issue_attachments, issue, event
end
[:before_add, :before_remove, :after_add, :after_remove].each do |observable|
@ -57,15 +57,15 @@ module RedmineCustomWorkflows
status_new = IssueStatus.find_by_id(status_id)
errors.add :status, :new_status_invalid,
:old_status => status_was && status_was.name,
:new_status => status_new && status_new.name
old_status: status_was && status_was.name,
new_status: status_new && status_new.name
end
def before_save_custom_workflows
@issue = self
@saved_attributes = attributes.dup
CustomWorkflow.run_shared_code(self)
CustomWorkflow.run_custom_workflows(:issue, self, :before_save)
CustomWorkflow.run_shared_code self
CustomWorkflow.run_custom_workflows :issue, self, :before_save
throw :abort if errors.any?
errors.empty? && (@saved_attributes == attributes || valid?)
ensure
@ -73,15 +73,15 @@ module RedmineCustomWorkflows
end
def after_save_custom_workflows
CustomWorkflow.run_custom_workflows(:issue, self, :after_save)
CustomWorkflow.run_custom_workflows :issue, self, :after_save
end
def before_destroy_custom_workflows
CustomWorkflow.run_custom_workflows(:issue, self, :before_destroy)
CustomWorkflow.run_custom_workflows :issue, self, :before_destroy
end
def after_destroy_custom_workflows
CustomWorkflow.run_custom_workflows(:issue, self, :after_destroy)
CustomWorkflow.run_custom_workflows :issue, self, :after_destroy
end
end

View File

@ -27,7 +27,7 @@ module RedmineCustomWorkflows
def self.included(base)
base.class_eval do
has_and_belongs_to_many :custom_workflows
safe_attributes :custom_workflow_ids, :if =>
safe_attributes :custom_workflow_ids, if:
lambda { |project, user| project.new_record? || user.allowed_to?(:manage_project_workflow, project) }
before_save :before_save_custom_workflows
@ -51,8 +51,8 @@ module RedmineCustomWorkflows
def before_save_custom_workflows
@project = self
@saved_attributes = attributes.dup
CustomWorkflow.run_shared_code(self)
CustomWorkflow.run_custom_workflows(:project, self, :before_save)
CustomWorkflow.run_shared_code self
CustomWorkflow.run_custom_workflows :project, self, :before_save
throw :abort if errors.any?
errors.empty? && (@saved_attributes == attributes || valid?)
ensure
@ -60,15 +60,15 @@ module RedmineCustomWorkflows
end
def after_save_custom_workflows
CustomWorkflow.run_custom_workflows(:project, self, :after_save)
CustomWorkflow.run_custom_workflows :project, self, :after_save
end
def before_destroy_custom_workflows
CustomWorkflow.run_custom_workflows(:project, self, :before_destroy)
CustomWorkflow.run_custom_workflows :project, self, :before_destroy
end
def after_destroy_custom_workflows
CustomWorkflow.run_custom_workflows(:project, self, :after_destroy)
CustomWorkflow.run_custom_workflows :project, self, :after_destroy
end
end

View File

@ -45,15 +45,15 @@ module RedmineCustomWorkflows
end
def after_save_custom_workflows
CustomWorkflow.run_custom_workflows(:time_entry, self, :after_save)
CustomWorkflow.run_custom_workflows :time_entry, self, :after_save
end
def before_destroy_custom_workflows
CustomWorkflow.run_custom_workflows(:time_entry, self, :before_destroy)
CustomWorkflow.run_custom_workflows :time_entry, self, :before_destroy
end
def after_destroy_custom_workflows
CustomWorkflow.run_custom_workflows(:time_entry, self, :after_destroy)
CustomWorkflow.run_custom_workflows :time_entry, self, :after_destroy
end
end

View File

@ -36,8 +36,8 @@ module RedmineCustomWorkflows
def before_save_custom_workflows
@user = self
@saved_attributes = attributes.dup
CustomWorkflow.run_shared_code(self)
CustomWorkflow.run_custom_workflows(:user, self, :before_save)
CustomWorkflow.run_shared_code self
CustomWorkflow.run_custom_workflows :user, self, :before_save
throw :abort if errors.any?
errors.empty? && (@saved_attributes == attributes || valid?)
ensure
@ -45,15 +45,15 @@ module RedmineCustomWorkflows
end
def after_save_custom_workflows
CustomWorkflow.run_custom_workflows(:user, self, :after_save)
CustomWorkflow.run_custom_workflows :user, self, :after_save
end
def before_destroy_custom_workflows
CustomWorkflow.run_custom_workflows(:user, self, :before_destroy)
CustomWorkflow.run_custom_workflows :user, self, :before_destroy
end
def after_destroy_custom_workflows
CustomWorkflow.run_custom_workflows(:user, self, :after_destroy)
CustomWorkflow.run_custom_workflows :user, self, :after_destroy
end
end

View File

@ -36,8 +36,8 @@ module RedmineCustomWorkflows
def before_save_custom_workflows
@version = self
@saved_attributes = attributes.dup
CustomWorkflow.run_shared_code(self)
CustomWorkflow.run_custom_workflows(:version, self, :before_save)
CustomWorkflow.run_shared_code self
CustomWorkflow.run_custom_workflows :version, self, :before_save
throw :abort if errors.any?
errors.empty? && (@saved_attributes == attributes || valid?)
ensure
@ -45,15 +45,15 @@ module RedmineCustomWorkflows
end
def after_save_custom_workflows
CustomWorkflow.run_custom_workflows(:version, self, :after_save)
CustomWorkflow.run_custom_workflows :version, self, :after_save
end
def before_destroy_custom_workflows
CustomWorkflow.run_custom_workflows(:version, self, :before_destroy)
CustomWorkflow.run_custom_workflows :version, self, :before_destroy
end
def after_destroy_custom_workflows
CustomWorkflow.run_custom_workflows(:version, self, :after_destroy)
CustomWorkflow.run_custom_workflows :version, self, :after_destroy
end
end

View File

@ -36,8 +36,8 @@ module RedmineCustomWorkflows
def before_save_custom_workflows
@content = self
@saved_attributes = attributes.dup
CustomWorkflow.run_shared_code(self)
CustomWorkflow.run_custom_workflows(:wiki_content, self, :before_save)
CustomWorkflow.run_shared_code self
CustomWorkflow.run_custom_workflows :wiki_content, self, :before_save
throw :abort if errors.any?
errors.empty? && (@saved_attributes == attributes || valid?)
ensure
@ -45,15 +45,15 @@ module RedmineCustomWorkflows
end
def after_save_custom_workflows
CustomWorkflow.run_custom_workflows(:wiki_content, self, :after_save)
CustomWorkflow.run_custom_workflows :wiki_content, self, :after_save
end
def before_destroy_custom_workflows
CustomWorkflow.run_custom_workflows(:wiki_content, self, :before_destroy)
CustomWorkflow.run_custom_workflows :wiki_content, self, :before_destroy
end
def after_destroy_custom_workflows
CustomWorkflow.run_custom_workflows(:wiki_content, self, :after_destroy)
CustomWorkflow.run_custom_workflows :wiki_content, self, :after_destroy
end
end

View File

@ -27,10 +27,10 @@ module RedmineCustomWorkflows
def self.included(base)
base.class_eval do
def self.attachments_callback(event, page, attachment)
page.instance_variable_set(:@page, page)
page.instance_variable_set(:@attachment, attachment)
page.instance_variable_set :@page, page
page.instance_variable_set :@attachment, attachment
CustomWorkflow.run_shared_code(page) if event.to_s.starts_with? 'before_'
CustomWorkflow.run_custom_workflows(:wiki_page_attachments, page, event)
CustomWorkflow.run_custom_workflows :wiki_page_attachments, page, event
end
[:before_add, :before_remove, :after_add, :after_remove].each do |observable|

View File

@ -28,8 +28,8 @@ clone()
# Exit if the cloning fails
set -e
rm -rf $PATH_TO_REDMINE
git clone -b $REDMINE_GIT_TAG --depth=100 --quiet $REDMINE_GIT_REPO $PATH_TO_REDMINE
rm -rf ${PATH_TO_REDMINE}
git clone -b ${REDMINE_GIT_TAG} --depth=100 --quiet ${REDMINE_GIT_REPO} ${PATH_TO_REDMINE}
}
test()
@ -37,7 +37,7 @@ test()
# Exit if a test fails
set -e
cd $PATH_TO_REDMINE
cd ${PATH_TO_REDMINE}
# Run tests within application
bundle exec rake redmine:plugins:test:units NAME=redmine_custom_workflows RAILS_ENV=test
@ -49,7 +49,7 @@ uninstall()
# Exit if the migration fails
set -e
cd $PATH_TO_REDMINE
cd ${PATH_TO_REDMINE}
# clean up database
bundle exec rake redmine:plugins:migrate NAME=redmine_custom_workflows VERSION=0 RAILS_ENV=test
@ -61,14 +61,14 @@ install()
set -e
# cd to redmine folder
cd $PATH_TO_REDMINE
cd ${PATH_TO_REDMINE}
echo current directory is `pwd`
# Create a link to the Custom Workflows plugin
ln -sf $PATH_TO_CUSTOM_WORKFLOWS plugins/redmine_custom_workflows
ln -sf ${PATH_TO_CUSTOM_WORKFLOWS} plugins/redmine_custom_workflows
# Copy database.yml
cp $WORKSPACE/database.yml config/
cp ${WORKSPACE}/database.yml config/
# Install gems
# Not ideal, but at present Travis-CI will not install with xapian enabled: