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

View File

@ -31,11 +31,11 @@ class CustomWorkflow < ActiveRecord::Base
acts_as_list acts_as_list
validates_presence_of :name validates_presence_of :name
validates_uniqueness_of :name, :case_sensitive => false 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 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?} 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| 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)", 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) true, project.id)
@ -43,11 +43,11 @@ class CustomWorkflow < ActiveRecord::Base
def self.import_from_xml(xml) def self.import_from_xml(xml)
attributes = Hash.from_xml(xml).values.first attributes = Hash.from_xml(xml).values.first
attributes.delete('exported_at') attributes.delete 'exported_at'
attributes.delete('plugin_version') attributes.delete 'plugin_version'
attributes.delete('ruby_version') attributes.delete 'ruby_version'
attributes.delete('rails_version') attributes.delete 'rails_version'
CustomWorkflow.new(attributes) CustomWorkflow.new attributes
end end
def self.log_message(str, object) 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="splitcontent">
<div class="splitcontentleft"> <div class="splitcontentleft">
<div class="box tabular"> <div class="box tabular">
<p><%= f.text_field :name, :required => true, :size => 50 %></p> <p><%= f.text_field :name, required: true %></p>
<p><%= f.text_field :author, :size => 50, :label => :field_custom_workflow_author %> <p><%= f.text_field :author, label: :field_custom_workflow_author %>
<em class="info"><%= l(:text_custom_workflow_author) %></em> <em class="info"><%= l(:text_custom_workflow_author) %></em>
</p> </p>
<p><%= f.select :observable, <p><%= f.select :observable,
CustomWorkflow::OBSERVABLES.collect {|o| [l("custom_workflow_observable_#{o}"), o]}, {}, CustomWorkflow::OBSERVABLES.collect { |o| [l("custom_workflow_observable_#{o}"), o] }, {},
:onchange => 'this.form.submit()', onchange: 'this.form.submit()',
:disabled => !@workflow.new_record? %></p> disabled: !@workflow.new_record? %></p>
<p><%= f.text_area :description, cols: 40, rows: 5 , class: 'wiki-edit' %></p> <p><%= f.text_area :description, class: 'wiki-edit' %></p>
<% if @workflow.has_projects_association? %> <% if @workflow.has_projects_association? %>
<p> <p>
<%= f.check_box :is_for_all, onclick: "checkAndDisable('custom_workflow_enabled_projects', this.checked);", <%= f.check_box :is_for_all, onclick: "checkAndDisable('custom_workflow_enabled_projects', this.checked);",
label: :field_enabled_for_all_projects %> label: :field_enabled_for_all_projects %>
</p> </p>
<% end %> <% end %>
<p><%= f.check_box :active, :label => :field_custom_workflow_active %></p> <p><%= f.check_box :active, label: l(:field_active) %></p>
</div> </div>
</div> </div>
<% if @workflow.has_projects_association? %> <% if @workflow.has_projects_association? %>
@ -28,102 +51,94 @@
<%= l(:label_project_plural) %> <%= l(:label_project_plural) %>
</legend> </legend>
<%= render_project_nested_lists(Project.visible.active) do |p| %> <%= render_project_nested_lists(Project.visible.active) do |p| %>
<%= content_tag('label', <%= content_tag 'label',
check_box_tag('custom_workflow[project_ids][]', p.id, check_box_tag('custom_workflow[project_ids][]', p.id,
@workflow.project_ids.include?(p.id) || @workflow.is_for_all?, id: nil, @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 %> <% end %>
<%= hidden_field_tag('custom_workflow[project_ids][]', '', id: nil) %> <%= hidden_field_tag 'custom_workflow[project_ids][]', '', id: nil %>
<% end %> <% end %>
</div> </div>
</div> </div>
<% end %> <% end %>
</div> </div>
<div style="clear: left;"></div> <div id="custom_workflow_scripts" class="box tabular">
<fieldset>
<fieldset class="box"> <legend><%= l(:label_workflow_scripts) %></legend>
<legend><%= l(:label_workflow_scripts) %></legend> <% observable = @workflow.observable.to_sym %>
<% observable = @workflow.observable.to_sym %> <div class="custom-workflow-info">
<p> <em class="info"><%= l("text_custom_workflow_#{observable}_code_note") %></em>
<em class="info"><%= l("text_custom_workflow_#{observable}_code_note") %></em> </div>
</p> <% case observable %>
<% case observable %> <% when :shared %>
<% when :shared %> <%= f.text_area :shared_code, cols: 40, rows: 20, wrap: 'off', class: 'custom_workflow_script' %>
<%= f.text_area :shared_code, :cols => 40, :rows => 20, :wrap => 'off', :class => 'custom_workflow_script' %> <% when *CustomWorkflow::COLLECTION_OBSERVABLES %>
<% when *CustomWorkflow::COLLECTION_OBSERVABLES %> <% collapsed = !(@workflow.before_add.present? || @workflow.after_add.present? || @workflow.errors[:base].present?) %>
<% collapsed = (not (@workflow.before_add.present? or @workflow.after_add.present? or @workflow.errors[:base].present?)) %> <fieldset class="collapsible <%= collapsed ? 'collapsed' : '' %>">
<fieldset class="collapsible <%= collapsed ? 'collapsed' : '' %>"> <legend onclick="toggleFieldset(this);" class="icon icon-collapsed"><%= l(:label_add_workflows) %></legend>
<legend onclick="toggleFieldset(this);"><%= l(:label_add_workflows) %></legend> <div class="<%= collapsed ? 'custom-workflow-hidden' : '' %>">
<div style="<%= collapsed ? 'display: none' : '' %>"> <div class="splitcontent">
<div class="splitcontent"> <div class="splitcontentleft">
<div class="splitcontentleft"> <%= f.text_area :before_add, cols: 40, rows: 20, wrap: 'off', class: 'custom_workflow_script' %>
<%= f.text_area :before_add, :cols => 40, :rows => 20, :wrap => 'off', :class => 'custom_workflow_script' %> </div>
</div> <div class="splitcontentright">
<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>
</div> </div>
</div> </fieldset>
</fieldset> <% collapsed = !(@workflow.before_remove.present? || @workflow.after_remove.present?) %>
<% collapsed = (not (@workflow.before_remove.present? or @workflow.after_remove.present?)) %> <fieldset class="collapsible <%= collapsed ? 'collapsed' : '' %>">
<fieldset class="collapsible <%= collapsed ? 'collapsed' : '' %>"> <legend onclick="toggleFieldset(this);" class="icon icon-collapsed"><%= l(:label_remove_workflows) %></legend>
<legend onclick="toggleFieldset(this);"><%= l(:label_remove_workflows) %></legend> <div class="<%= collapsed ? 'custom-workflow-hidden' : '' %>">
<div style="<%= collapsed ? 'display: none' : '' %>"> <div class="splitcontent">
<div class="splitcontent"> <div class="splitcontentleft">
<div class="splitcontentleft"> <%= f.text_area :before_remove, cols: 40, rows: 20, wrap: 'off', class: 'custom_workflow_script' %>
<%= f.text_area :before_remove, :cols => 40, :rows => 20, :wrap => 'off', :class => 'custom_workflow_script' %> </div>
</div> <div class="splitcontentright">
<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>
</div> </div>
</div> </fieldset>
</fieldset> <% when *CustomWorkflow::SINGLE_OBSERVABLES %>
<% when *CustomWorkflow::SINGLE_OBSERVABLES %> <% collapsed = (not (@workflow.before_save.present? or @workflow.after_save.present? or @workflow.errors[:base].present?)) %>
<% collapsed = (not (@workflow.before_save.present? or @workflow.after_save.present? or @workflow.errors[:base].present?)) %> <fieldset class="collapsible <%= collapsed ? 'collapsed' : '' %>">
<fieldset class="collapsible <%= collapsed ? 'collapsed' : '' %>"> <legend onclick="toggleFieldset(this);" class="icon icon-collapsed"><%= l(:label_save_workflows) %></legend>
<legend onclick="toggleFieldset(this);"><%= l(:label_save_workflows) %></legend> <div class="<%= collapsed ? 'custom-workflow-hidden' : '' %>">
<div style="<%= collapsed ? 'display: none' : '' %>"> <div class="splitcontent">
<div class="splitcontent"> <div class="splitcontentleft">
<div class="splitcontentleft"> <%= f.text_area :before_save, cols: 40, rows: 20, wrap: 'off', class: 'custom_workflow_script' %>
<%= f.text_area :before_save, :cols => 40, :rows => 20, :wrap => 'off', :class => 'custom_workflow_script' %> <% if observable == :issue %>
<% if observable == :issue %> <em class="info"><%= l(:text_custom_workflow_before_save_note) %></em>
<em class="info"><%= l(:text_custom_workflow_before_save_note) %></em> <% end %>
<% end %> </div>
</div> <div class="splitcontentright">
<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 %>
<% if observable == :issue %> <em class="info"><%= l(:text_custom_workflow_after_save_note) %></em>
<em class="info"><%= l(:text_custom_workflow_after_save_note) %></em> <% end %>
<% end %> </div>
</div> </div>
</div> </div>
</div> </fieldset>
</fieldset> <% collapsed = !(@workflow.before_destroy.present? || @workflow.after_destroy.present?) %>
<% collapsed = (not (@workflow.before_destroy.present? or @workflow.after_destroy.present?)) %> <fieldset class="collapsible <%= collapsed ? 'collapsed' : '' %>">
<fieldset class="collapsible <%= collapsed ? 'collapsed' : '' %>"> <legend onclick="toggleFieldset(this);" class="icon icon-collapsed"><%= l(:label_destroy_workflows) %></legend>
<legend onclick="toggleFieldset(this);"><%= l(:label_destroy_workflows) %></legend> <div class="<%= collapsed ? 'custom-workflow-hidden' : '' %>">
<div style="<%= collapsed ? 'display: none' : '' %>"> <div class="splitcontent">
<div class="splitcontent"> <div class="splitcontentleft">
<div class="splitcontentleft"> <%= f.text_area :before_destroy, cols: 40, rows: 20, wrap: 'off', class: 'custom_workflow_script' %>
<%= f.text_area :before_destroy, :cols => 40, :rows => 20, :wrap => 'off', :class => 'custom_workflow_script' %> </div>
</div> <div class="splitcontentright">
<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>
</div> </div>
</div> </fieldset>
</fieldset> <% end %>
<% end %> </fieldset>
</fieldset> </div>
<script>
$('.custom_workflow_script').taboverride(2, true);
</script>
<%= wikitoolbar_for :custom_workflow_description %> <%= 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> <h2><%= link_to l(:label_custom_workflow_plural), custom_workflows_path %> &#187; <%= @workflow %></h2>
<%= error_messages_for 'workflow' %> <%= error_messages_for 'workflow' %>
<%= labelled_form_for @workflow do |f| %> <%= labelled_form_for @workflow do |f| %>
<%= render :partial => 'form', :locals => {:f => f} %> <%= render partial: 'form', locals: { f: f } %>
<%= submit_tag l(:button_save) %> <%= submit_tag l(:button_save) %>
<% end %> <% 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,85 +1,94 @@
<% 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"> <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> </div>
<h2><%=l(:label_custom_workflow_plural)%></h2> <h2><%= l(:label_custom_workflow_plural) %></h2>
<div class="autoscroll"> <div class="autoscroll">
<% if @workflows.any? %> <% if @workflows.any? %>
<table class="custom-workflows list"> <table class="custom-workflows list" data-hascontextmenu="true">
<thead> <thead>
<tr> <tr>
<th><%= l(:field_name) %></th> <th><%= l(:field_name) %></th>
<th><%= l(:field_description) %></th> <th><%= l(:field_observable) %></th>
<th><%= l(:field_observable) %></th> <th><%= l(:field_author) %></th>
<th><%= l(:field_author) %></th> <th></th>
<th><%= l(:label_project_plural) %></th> </tr>
<th><%= l(:button_sort) %></th> </thead>
<th></th> <tbody>
</tr> <% @workflows.each do |workflow| %>
</thead> <tr class="<%= cycle('odd', 'even') %> <%= 'disabled' unless workflow.active? %>">
<tbody> <td class="name"><%= link_to workflow.name, edit_custom_workflow_path(workflow) %></td>
<% @workflows.each do |workflow| %> <td class="observable"><%= l("custom_workflow_observable_#{workflow.observable}") %></td>
<tr class="<%= cycle("odd", "even") %> <%= 'disabled' unless workflow.active? %>"> <td class="author"><%= mail_to workflow.author if workflow.author.present? %></td>
<td class="name"><%= link_to(workflow.name, edit_custom_workflow_path(workflow)) %></td> <td class="buttons">
<td class="description"><%= textilizable(workflow.description) %></td> <%= reorder_handle workflow, url: url_for(action: 'reorder', id: workflow) %>
<td class="observable"><%= l("custom_workflow_observable_#{workflow.observable}") %></td> <% if workflow.active? %>
<td class="author"><%= mail_to workflow.author if workflow.author.present? %></td> <%= link_to l(:button_custom_workflow_deactivate), custom_workflow_status_path(workflow, active: false),
<td> class: 'icon icon-lock', method: :post %>
<% if not workflow.has_projects_association? %> <% else %>
- <%= link_to l(:button_activate), custom_workflow_status_path(workflow, active: true),
<% elsif workflow.is_for_all? %> class: 'icon icon-unlock', method: :post %>
<%= l(:field_enabled_for_all_projects) %> <% end %>
<% elsif workflow.projects.empty? %> <%= link_to l(:label_custom_workflow_export), export_custom_workflow_path(workflow),
<%= l(:text_no_enabled_projects) %> class: 'icon icon-download', method: :get %>
<% else %> <%= link_to l(:button_delete), workflow, class: 'icon icon-del', data: { confirm: l(:text_are_you_sure) },
<%= workflow.projects.map(&:name).join(", ") %> confirm: l(:text_are_you_sure), method: :delete %>
<% end %> </td>
</td> </tr>
<td align="center" nowrap> <% end %>
<%= reorder_handle(workflow, url: url_for(action: 'reorder', id: workflow) ) %> </tbody>
</td> </table>
<td class="buttons">
<div class="icon"></div>
<% if workflow.active? %>
<%= 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) %>
<% 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) %>
</td>
</tr>
<% end %>
</tbody>
</table>
<% else %> <% else %>
<p class="nodata"><%= l(:label_no_data) %></p> <p class="nodata"><%= l(:label_no_data) %></p>
<% end %> <% end %>
</div> </div>
<div id="import-dialog" class="custom-workflow-hidden">
<div id="import-dialog" style="display: none">
<h3 class="title"><%= l(:label_custom_workflow_import) %></h3> <h3 class="title"><%= l(:label_custom_workflow_import) %></h3>
<%= form_tag import_custom_workflow_path, :multipart => true do %> <%= form_tag import_custom_workflow_path, :multipart => true do %>
<p> <p>
<%= l(:field_custom_workflow_file) %>: <%= l(:field_custom_workflow_file) %>:
</p> </p>
<p> <p>
<%= file_field_tag 'file', :accept => 'application/xml' %> <%= file_field_tag 'file', accept: 'application/xml' %>
</p> </p>
<p class="buttons"> <p class="buttons">
<%= submit_tag l(:button_import), :name => nil, :onclick => "hideModal(this);" %> <%= 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_cancel), name: nil, onclick: 'hideModal(this);', type: 'button' %>
</p> </p>
<% end %> <% end %>
</div> </div>
<%= javascript_tag do %> <%= javascript_tag do %>
$(function() { $("table.custom-workflows tbody").positionedItems(); }); $(function() {
$("table.custom-workflows tbody").positionedItems();
});
<% end %> <% 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> <h2><%= link_to l(:label_custom_workflow_plural), custom_workflows_path %> &#187; <%= l(:label_custom_workflow_new) %></h2>
<%= error_messages_for 'workflow' %> <%= error_messages_for 'workflow' %>
<%= labelled_form_for @workflow do |f| %> <%= labelled_form_for @workflow do |f| %>
<%= render :partial => 'form', :locals => {:f => f} %> <%= render partial: 'form', locals: { f: f } %>
<%= submit_tag l(:button_create) %> <%= submit_tag l(:button_create) %>
<% end %> <% 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 %> <%= form_for @project do %>
<%= hidden_field_tag :tab, 'custom_workflow' %> <%= hidden_field_tag :tab, 'custom_workflow' %>
<%= hidden_field_tag 'project[custom_workflow_ids][]', '' %> <%= hidden_field_tag 'project[custom_workflow_ids][]', '' %>
@ -18,7 +41,7 @@
<% end %> <% end %>
</label> </label>
</dt> </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 %> <% end %>
</dl> </dl>
<% else %> <% 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; 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 { table.list.custom-workflows tr.disabled {
color: silver; color: silver;
} }
#custom_workflow_description, #custom_workflow_name {
width: 98%;
}
.custom_workflow_script {
width: 98%;
font-size: 11px;
}
#custom_workflow_enabled_projects ul.root { #custom_workflow_enabled_projects ul.root {
max-height: 400px; max-height: 400px;
overflow-y: auto; overflow-y: auto;
} }
/* Icons */ .custom-workflow-hidden {
.icon-import { display: none;
background-image: url(../images/export.png);
} }
.icon-export { .custom-workflow-info {
background-image: url(../images/import.png); 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_plural: Uživatelské procesy
label_custom_workflow_new: Vytvořit uživatelský proces label_custom_workflow_new: Vytvořit uživatelský proces
label_workflow_scripts: Skript procesu label_workflow_scripts: Skript procesu
label_enabled_projects: Povoleno pro projekt(y)
label_custom_workflow_export: Export label_custom_workflow_export: Export
label_custom_workflow_import: Importovat proces label_custom_workflow_import: Importovat proces
label_save_workflows: Uložení sledovaných objektů 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_add_workflows: Přidání sledovaných objektů do sezanmu
label_remove_workflows: Odstranění sledovaných objektů za seznamu label_remove_workflows: Odstranění sledovaných objektů za seznamu
button_import: Import
button_custom_workflow_activate: Aktivovat
button_custom_workflow_deactivate: Deaktivovat button_custom_workflow_deactivate: Deaktivovat
field_after_save: Skript spuštěný po uložení sledovaného objektu 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_before_remove: Skript spuštěný před odstraněním sledovaného objektu ze seznamu
field_shared_code: Sdílený kód field_shared_code: Sdílený kód
field_observable: Sledovaný objekt field_observable: Sledovaný objekt
field_is_enabled: Povoleno
field_enabled_for_all_projects: Povoleno pro všechny projekty field_enabled_for_all_projects: Povoleno pro všechny projekty
field_custom_workflow_author: Autorův e-mail field_custom_workflow_author: Autorův e-mail
field_custom_workflow_file: Select the XML file previously exported process field_custom_workflow_file: Select the XML file previously exported process
field_custom_workflow_active: Aktivní
field_custom_workflow: field_custom_workflow:
script: Skript script: Skript
@ -70,8 +65,8 @@ cs:
scripts_absent: Musí být definován alespoň jeden skript scripts_absent: Musí být definován alespoň jeden skript
text_select_project_custom_workflows: Vyberte uživatelský skript projektu 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 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'`." 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 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í 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í. rekurzivnímu volání.

View File

@ -20,87 +20,101 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
en: en:
project_module_custom_workflows_module: "Custom workflows" project_module_custom_workflows_module: Custom workflows
permission_manage_project_workflow: "Manage project custom workflows" permission_manage_project_workflow: Manage project custom workflows
label_custom_workflow: "Custom workflows" label_custom_workflow: Custom workflow
label_custom_workflow_plural: "Custom workflows" label_custom_workflow_plural: Custom workflows
label_custom_workflow_new: "Create a custom workflow" label_custom_workflow_new: Create a custom workflow
label_workflow_scripts: "Workflow scripts" label_workflow_scripts: Workflow scripts
label_enabled_projects: "Enabled for project(s)" label_custom_workflow_export: Export
label_custom_workflow_export: "Export" label_custom_workflow_import: Import workflow
label_custom_workflow_import: "Import workflow" label_save_workflows: Saving observable objects
label_save_workflows: "Saving observable objects" label_destroy_workflows: Destroying observable objects
label_destroy_workflows: "Destroying observable objects" label_add_workflows: Adding observable objects to collection
label_add_workflows: "Adding observable objects to collection" label_remove_workflows: Removing observable objects from collection
label_remove_workflows: "Removing observable objects from collection"
button_import: "Import" button_custom_workflow_deactivate: Deactivate
button_custom_workflow_activate: "Activate"
button_custom_workflow_deactivate: "Deactivate"
field_after_save: "Workflow script executable after saving observable object" field_after_save: Workflow script executable after saving observable object
field_before_save: "Workflow script executable before saving observable object" field_before_save: Workflow script executable before saving observable object
field_after_destroy: "Workflow script executable after destroying observable object" field_after_destroy: Workflow script executable after destroying observable object
field_before_destroy: "Workflow script executable before 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_after_add: Workflow script executable after adding observable object to collection
field_before_add: "Workflow script executable before 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_after_remove: Workflow script executable after removing observable object from collection
field_before_remove: "Workflow script executable before removing observable object from collection" field_before_remove: Workflow script executable before removing observable object from collection
field_shared_code: "Shared code" field_shared_code: Shared code
field_observable: "Observable object" field_observable: Observable object
field_is_enabled: "Enabled" field_enabled_for_all_projects: Enabled for all projects
field_enabled_for_all_projects: "Enabled for all projects" field_custom_workflow_author: Author's e-mail
field_custom_workflow_author: "Author's e-mail" field_custom_workflow_file: Select the XML file previously exported process
field_custom_workflow_file: "Select the XML file previously exported process"
field_custom_workflow_active: "Active"
field_custom_workflow: field_custom_workflow:
script: "Workflow script" script: Workflow script
notice_successful_import: "Custom workflow has successfully imported" notice_successful_import: Custom workflow has successfully imported
notice_successful_status_change: "Status has successfully changed" notice_successful_status_change: Status has successfully changed
error_failed_import: "Error importing custom workflow (unknown format? please see log)" error_failed_import: Error importing custom workflow (unknown format? please see log)
activerecord: activerecord:
errors: errors:
messages: messages:
invalid_script: "contains error: %{error}" 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" 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_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_before_save_note: You can change properties of the issues here. Do not create or update related
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. issues in this script. To finish with error, use `raise WorkflowError, 'Message to user'`.
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_after_save_note: You can update or create related issues here. Note that this script will be
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 also executed for the newly created issues. So make appropriate checks to prevent infinite recursion.
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_issue_code_note: Scripts are executed in the context of Issue object like ordinary
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. before_save and after_save callbacks. So use methods and properties of the issue directly (or through `self`).
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. Instance variables (@variable) are also allowed and may be used if needed.
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_shared_code_note: This code will run before any other workflow and may contain shared code,
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") e.g. functions and classes needed by other workflows
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_user_code_note: Scripts are executed in the context of User object when user object changes
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") (destroys). Use methods and properties of the user 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_group_code_note: Scripts are executed in the context of Group object when group object changes
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") (destroys). Use methods and properties of the group 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_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_no_enabled_projects: No projects
text_custom_workflow_author: Will be included in exported XML text_custom_workflow_author: Will be included in exported XML
text_custom_workflow_disabled: disabled by admin text_custom_workflow_disabled: disabled by admin
text_custom_workflow_is_for_all: enabled for all projects text_custom_workflow_is_for_all: enabled for all projects
custom_workflow_observable_shared: "<shared code>" custom_workflow_observable_shared: <shared code>
custom_workflow_observable_issue: "Issue" custom_workflow_observable_issue: Issue
custom_workflow_observable_issue_attachments: "Issue Attachments" custom_workflow_observable_issue_attachments: Issue Attachments
custom_workflow_observable_group: "Group" custom_workflow_observable_group: Group
custom_workflow_observable_user: "User" custom_workflow_observable_user: User
custom_workflow_observable_attachment: "Attachment" custom_workflow_observable_attachment: Attachment
custom_workflow_observable_project: "Project" custom_workflow_observable_project: Project
custom_workflow_observable_project_attachments: "Project Attachments / Files" custom_workflow_observable_project_attachments: Project Attachments / Files
custom_workflow_observable_wiki_content: "Wiki Content" custom_workflow_observable_wiki_content: Wiki Content
custom_workflow_observable_wiki_page_attachments: "Wiki Page Attachments" custom_workflow_observable_wiki_page_attachments: Wiki Page Attachments
custom_workflow_observable_group_users: "Group Users" custom_workflow_observable_group_users: Group Users
custom_workflow_observable_time_entry: "Time Entry" custom_workflow_observable_time_entry: Time Entry
custom_workflow_observable_version: "Version" custom_workflow_observable_version: Version

View File

@ -20,63 +20,101 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
pt-BR: pt-BR:
project_module_custom_workflows_module: "Fluxo de Trabalho Personalizado" project_module_custom_workflows_module: Fluxo de Trabalho Personalizado
permission_manage_project_workflow: "Gerenciar fluxos de trabalho personalizados de projeto" permission_manage_project_workflow: Gerenciar fluxos de trabalho personalizados de projeto
label_custom_workflow: "Fluxo de Trabalho personalizado" label_custom_workflow: Fluxo de Trabalho personalizado
label_custom_workflow_plural: "Fluxos de Trabalho personalizados" label_custom_workflow_plural: Fluxos de Trabalho personalizados
label_custom_workflow_new: "Criar um fluxo de trabalho personalizado" label_custom_workflow_new: Criar um fluxo de trabalho personalizado
label_workflow_scripts: "Scripts de fluxo de trabalho" 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_export: "Exportar" label_custom_workflow_import: Importar fluxo de trabalho
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_deactivate: Desativar
button_custom_workflow_activate: "Ativar"
button_custom_workflow_deactivate: "Desativar"
field_after_save: "Workflow script executable after saving observable object" field_after_save: Workflow script executable after saving observable object
field_before_save: "Workflow script executable before 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_after_destroy: Workflow script executable after destroying observable object
field_before_add: "Workflow script executable before adding observable object to collection" field_before_destroy: Workflow script executable before destroying observable object
field_after_remove: "Workflow script executable after removing observable object from collection" field_after_add: Workflow script executable after adding observable object to collection
field_before_remove: "Workflow script executable before removing observable object from collection" field_before_add: Workflow script executable before adding observable object to collection
field_shared_code: "Shared code" field_after_remove: Workflow script executable after removing observable object from collection
field_observable: "Observable object" field_before_remove: Workflow script executable before removing observable object from collection
field_is_enabled: "Ativado" field_shared_code: Shared code
field_enabled_for_all_projects: "Ativado para todos os projetos" field_observable: Observable object
field_custom_workflow_author: "E-mail do autor" field_enabled_for_all_projects: Ativado para todos os projetos
field_custom_workflow_file: "Selecione o arquivo XML exportado anteriormente" field_custom_workflow_author: E-mail do autor
field_custom_workflow_active: "Ativo" field_custom_workflow_file: Selecione o arquivo XML exportado anteriormente
field_custom_workflow: 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_import: Fluxo de Trabalho Personalizado foi importado com sucesso
notice_successful_status_change: "Situação foi alterada 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)" error_failed_import: Erro ao importar o fluxo de trabalho customizado (Formato desconhecido? Por favor verifique o log)
activerecord: activerecord:
errors: errors:
messages: messages:
invalid_script: "contém erro: %{error}" 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" 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_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_before_save_note: Você pode alterar as propriedades das tarefas aqui. Não crie ou atualize
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. tarefas relacionadas neste script. Para terminar com o erro, utilize a `raise WorkflowError, 'Mensagem para o usuário'`.
text_custom_workflow_issue_code_note: Ambos os scripts são executados no contexto da tarefa pelas chamadas before_save com retornos para a chamada after_save. Portanto, use métodos e propriedades da tarefa diretamente (ou através de "self"). Variáveis de instância (@variable) também são permitidos e podem ser utilizados, se necessário. text_custom_workflow_after_save_note: Você pode atualizar ou criar tarefas relacionadas aqui. Note que esse script
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. também será executado para as tarefas criadas recentemente. Então, faça as verificações adequadas para evitar que
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") entre em recursividade infinita.
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_issue_code_note: Ambos os scripts são executados no contexto da tarefa pelas chamadas before_save
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. 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_no_enabled_projects: Não há projeto
text_custom_workflow_author: Será incluído no XML exportado text_custom_workflow_author: Será incluído no XML exportado
text_custom_workflow_disabled: desabilitado por admin text_custom_workflow_disabled: desabilitado por admin
text_custom_workflow_is_for_all: Ativado para todos os projetos text_custom_workflow_is_for_all: Ativado para todos os projetos
custom_workflow_observable_shared: "<shared code>" custom_workflow_observable_shared: <shared code>
custom_workflow_observable_issue: "Issue" custom_workflow_observable_issue: Issue
custom_workflow_observable_group: "Group" custom_workflow_observable_issue_attachments: Issue Attachments
custom_workflow_observable_user: "User" custom_workflow_observable_group: Group
custom_workflow_observable_group_users: "Group Users" 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. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
ru: ru:
project_module_custom_workflows_module: "Пользовательские рабочие процессы" project_module_custom_workflows_module: Пользовательские рабочие процессы
permission_manage_project_workflow: "Управление пользовательскими рабочими процессами в проекте" permission_manage_project_workflow: Управление пользовательскими рабочими процессами в проекте
label_custom_workflow: "Пользовательский рабочий процесс" label_custom_workflow: Пользовательский рабочий процесс
label_custom_workflow_plural: "Пользовательские рабочие процессы" label_custom_workflow_plural: Пользовательские рабочие процессы
label_custom_workflow_new: "Новый процесс" label_custom_workflow_new: Новый процесс
label_workflow_scripts: "Сценарии" label_workflow_scripts: Сценарии
label_enabled_projects: "Разрешен в проектах" label_custom_workflow_export: Экспорт
label_custom_workflow_export: "Экспорт" label_custom_workflow_import: Импорт процесса
label_custom_workflow_import: "Импорт процесса" label_save_workflows: Сохранение наблюдаемых объектов
label_save_workflows: "Сохранение наблюдаемых объектов" label_destroy_workflows: Уничтожение наблюдаемых объектов
label_destroy_workflows: "Уничтожение наблюдаемых объектов" label_add_workflows: Добавление наблюдаемых объектов в коллекцию
label_add_workflows: "Добавление наблюдаемых объектов в коллекцию" label_remove_workflows: Удаление наблюдаемых объектов из коллекции
label_remove_workflows: "Удаление наблюдаемых объектов из коллекции"
button_import: "Импортировать" button_custom_workflow_deactivate: Деактивировать
button_custom_workflow_activate: "Активировать"
button_custom_workflow_deactivate: "Деактивировать"
field_after_save: "Сценарий выполняемый после сохранения наблюдаемого объекта" field_after_save: Сценарий выполняемый после сохранения наблюдаемого объекта
field_before_save: "Сценарий выполняемый перед сохранением наблюдаемого объекта" field_before_save: Сценарий выполняемый перед сохранением наблюдаемого объекта
field_after_destroy: "Сценарий выполняемый после уничтожения наблюдаемого объекта" field_after_destroy: Сценарий выполняемый после уничтожения наблюдаемого объекта
field_before_destroy: "Сценарий выполняемый перед уничтожением наблюдаемого объекта" field_before_destroy: Сценарий выполняемый перед уничтожением наблюдаемого объекта
field_after_add: "Сценарий выполняемый после добавления наблюдаемого объекта в коллекцию" field_after_add: Сценарий выполняемый после добавления наблюдаемого объекта в коллекцию
field_before_add: "Сценарий выполняемый перед добавлением наблюдаемого объекта в коллекцию" field_before_add: Сценарий выполняемый перед добавлением наблюдаемого объекта в коллекцию
field_after_remove: "Сценарий выполняемый после удаления наблюдаемого объекта из коллекции" field_after_remove: Сценарий выполняемый после удаления наблюдаемого объекта из коллекции
field_before_remove: "Сценарий выполняемый перед удалением наблюдаемого объекта из коллекции" field_before_remove: Сценарий выполняемый перед удалением наблюдаемого объекта из коллекции
field_shared_code: "Общий код" field_shared_code: Общий код
field_observable: "Наблюдаемый объект" field_observable: Наблюдаемый объект
field_is_enabled: "Разрешено" field_enabled_for_all_projects: Разрешен для всех проектов
field_enabled_for_all_projects: "Разрешен для всех проектов" field_custom_workflow_author: E-Mail адрес автора
field_custom_workflow_author: "E-Mail адрес автора" field_custom_workflow_file: Выберите XML файл ранее экспортированного процесса
field_custom_workflow_file: "Выберите XML файл ранее экспортированного процесса"
field_custom_workflow_active: "Активен"
field_custom_workflow: field_custom_workflow:
script: "Сценарий" script: Сценарий
notice_successful_import: "Рабочий процесс успешно импортирован" notice_successful_import: Рабочий процесс успешно импортирован
notice_successful_status_change: "Статус успешно изменен" notice_successful_status_change: Статус успешно изменен
error_failed_import: "Ошибка импорта рабочего процесса (неверный формат? смотри журнал)" error_failed_import: Ошибка импорта рабочего процесса (неверный формат? смотри журнал)
activerecord: activerecord:
errors: errors:
messages: messages:
invalid_script: "содержит ошибку: %{error}" invalid_script: "содержит ошибку: %{error}"
custom_workflow_error: "Ошибка в сценарии рабочего процесса (обратитесь к администратору)" custom_workflow_error: Ошибка в сценарии рабочего процесса (Обратитесь к администратору)
new_status_invalid: "- переход от '%{old_status}' к '%{new_status}' невозможен" new_status_invalid: "- переход от '%{old_status}' к '%{new_status}' невозможен"
scripts_absent: "Хотя бы один скрипт должен быть определен" scripts_absent: Хотя бы один скрипт должен быть определен
text_select_project_custom_workflows: Выберите процессы для данного проекта text_select_project_custom_workflows: Выберите процессы для данного проекта
text_custom_workflow_before_save_note: Здесь вы можете изменять свойства задачи. Не создавайте и не обновляйте связанные задачи в этом сценарии. Чтобы завершить сценарий с произвольной ошибкой, используйте raise WorkflowError, "Message to user". text_custom_workflow_before_save_note: Здесь вы можете изменять свойства задачи. Не создавайте и не обновляйте
text_custom_workflow_after_save_note: Вы можете обновлять и создавать задачи (в том числе и связанные задачи) здесь. Обратите внимание, что данный сценарий будет также выполняться и для вновь создаваемых задач. Поэтому используйте дополнительные проверки, чтобы избежать бесконечной рекурсии. связанные задачи в этом сценарии. Чтобы завершить сценарий с произвольной ошибкой, используйте `raise WorkflowError, 'Message to user'`.
text_custom_workflow_issue_code_note: Эти сценарии исполняются в контексте задачи, как и обычные обратные вызовы before_save и after_save. Поэтому используйте методы и свойства задачи (Issue) напрямую или через ключевое слово self. text_custom_workflow_after_save_note: Вы можете обновлять и создавать задачи (в том числе и связанные задачи) здесь.
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_issue_code_note: Эти сценарии исполняются в контексте задачи, как и обычные обратные вызовы
text_custom_workflow_group_users_code_note: Эти сценарии выполняются когда пользователь добавляется в группу/удаляется из группы. Используйте переменные @user и @group для доступа к соответствующим объектам из Ваших сценариев. before_save и after_save. Поэтому используйте методы и свойства задачи (Issue) напрямую или через ключевое слово
text_custom_workflow_attachment_code_note: Эти сценарии исполняются в контексте объекта вложения когда объект вложения изменяется (удаляется). Используйте методы и свойства объекта вложения (Attachment) напрямую или через ключевое слово self. Обратите внимание на то, что данные сценарии выполняются при сохранении (удалении) вложения любого типа (задача, документ, страница Wiki и т.д.), поэтому следует дополнительно проверять в коде поле container_type либо в качестве наблюдаемого объекта выбрать конкретный тип вложения. self.
text_custom_workflow_issue_attachments_code_note: Эти сценарии выполняются когда вложение прикладывается к задаче/удаляется из задачи. Используйте переменные @issue и @attachment для доступа к соответствующим объектам из Ваших сценариев. text_custom_workflow_shared_code_note: Этот код будет исполняться перед любым другим процессом и может содержать общий
text_custom_workflow_project_code_note: Эти сценарии исполняются в контексте объекта проекта когда объект проекта изменяется (удаляется). Используйте методы и свойства объекта группы (Project) напрямую или через ключевое слово self. код, например, функции и классы, необходимые для работы других процессов.
text_custom_workflow_project_attachments_code_note: Эти сценарии выполняются когда файл загружается в проект/удаляется из проекта. Используйте переменные @project и @attachment для доступа к соответствующим объектам из Ваших сценариев. text_custom_workflow_user_code_note: Эти сценарии исполняются в контексте объекта пользователя когда объект
text_custom_workflow_wiki_content_code_note: Эти сценарии исполняются в контексте объекта Wiki содержания когда объект Wiki содержания изменяется (удаляется). Используйте методы и свойства объекта содержания Wiki (WikiContent) напрямую или через ключевое слово self. пользователя изменяется (удаляется). Используйте методы и свойства объекта пользователя (User) напрямую или через ключевое слово `self`.
text_custom_workflow_wiki_page_attachments_code_note: Эти сценарии выполняются когда файл загружается на Wiki страницу/удаляется с Wiki страницы. Используйте переменные @page и @attachment для доступа к соответствующим объектам из Ваших сценариев. text_custom_workflow_group_code_note: Эти сценарии исполняются в контексте объекта группы когда объект группы
text_custom_workflow_time_entry_code_note: Эти сценарии исполняются в контексте объекта затраченного времени когда объект изменяется (удаляется). Используйте методы и свойства объекта затраченного времени (TimeEntry) напрямую или через ключевое слово self. изменяется (удаляется). Используйте методы и свойства объекта группы (Group) напрямую или через ключевое слово `self`.
text_custom_workflow_version_code_note: Эти сценарии исполняются в контексте объекта версии когда объект изменяется (удаляется). Используйте методы и свойства объекта версии (Version) напрямую или через ключевое слово 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_no_enabled_projects: Нет проектов
text_custom_workflow_author: Будет использован в XML файле при экспорте text_custom_workflow_author: Будет использован в XML файле при экспорте
text_custom_workflow_disabled: отключен администратором text_custom_workflow_disabled: отключен администратором
text_custom_workflow_is_for_all: разрешен для всех проектов text_custom_workflow_is_for_all: разрешен для всех проектов
custom_workflow_observable_shared: "<общий код>" custom_workflow_observable_shared: <общий код>
custom_workflow_observable_issue: "Задача" custom_workflow_observable_issue: Задача
custom_workflow_observable_issue_attachments: "Вложения задач" custom_workflow_observable_issue_attachments: Вложения задач
custom_workflow_observable_group: "Группа" custom_workflow_observable_group: Группа
custom_workflow_observable_user: "Пользователь" custom_workflow_observable_user: Пользователь
custom_workflow_observable_attachment: "Вложение" custom_workflow_observable_attachment: Вложение
custom_workflow_observable_project: "Проект" custom_workflow_observable_project: Проект
custom_workflow_observable_project_attachments: "Вложения проекта / Файлы" custom_workflow_observable_project_attachments: Вложения проекта / Файлы
custom_workflow_observable_wiki_content: "Содержание Wiki" custom_workflow_observable_wiki_content: Содержание Wiki
custom_workflow_observable_wiki_page_attachments: "Вложения страниц Wiki" custom_workflow_observable_wiki_page_attachments: Вложения страниц Wiki
custom_workflow_observable_group_users: "Пользователи группы" custom_workflow_observable_group_users: Пользователи группы
custom_workflow_observable_time_entry: "Затраченное время" custom_workflow_observable_time_entry: Затраченное время
custom_workflow_observable_version: "Версия" custom_workflow_observable_version: Версия

View File

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

View File

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

View File

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

View File

@ -22,7 +22,7 @@
class CreateCustomWorkflowsProjects < ActiveRecord::Migration[4.2] class CreateCustomWorkflowsProjects < ActiveRecord::Migration[4.2]
def change def change
create_table :custom_workflows_projects, :force => true, :id => false do |t| create_table :custom_workflows_projects, force: true, id: false do |t|
t.references :project t.references :project
t.references :custom_workflow t.references :custom_workflow
end end

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -22,11 +22,11 @@
class AddAdditionalScriptFieldsToCustomWorkflows < ActiveRecord::Migration[4.2] class AddAdditionalScriptFieldsToCustomWorkflows < ActiveRecord::Migration[4.2]
def change def change
add_column :custom_workflows, :shared_code, :text, :null => true add_column :custom_workflows, :shared_code, :text, null: true
add_column :custom_workflows, :before_add, :text, :null => true add_column :custom_workflows, :before_add, :text, null: true
add_column :custom_workflows, :after_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, :before_remove, :text, null: true
add_column :custom_workflows, :after_remove, :text, :null => true add_column :custom_workflows, :after_remove, :text, null: true
end end
end end

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -45,15 +45,15 @@ module RedmineCustomWorkflows
end end
def after_save_custom_workflows def after_save_custom_workflows
CustomWorkflow.run_custom_workflows(:time_entry, self, :after_save) CustomWorkflow.run_custom_workflows :time_entry, self, :after_save
end end
def before_destroy_custom_workflows def before_destroy_custom_workflows
CustomWorkflow.run_custom_workflows(:time_entry, self, :before_destroy) CustomWorkflow.run_custom_workflows :time_entry, self, :before_destroy
end end
def after_destroy_custom_workflows 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
end end

View File

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

View File

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

View File

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

View File

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

View File

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