Redmine 6.0.0 #350

This commit is contained in:
Karel Pičman 2024-11-20 12:19:12 +01:00
parent 03989743d7
commit 8a4218fa11
16 changed files with 74 additions and 79 deletions

View File

@ -83,7 +83,7 @@ jobs:
sudo apt-get install subversion sudo apt-get install subversion
- name: Clone Redmine - name: Clone Redmine
# Get the latest stable Redmine # Get the latest stable Redmine
run: svn export http://svn.redmine.org/redmine/branches/5.1-stable/ /opt/redmine run: svn export http://svn.redmine.org/redmine/branches/6.0-stable/ /opt/redmine
- name: Checkout code - name: Checkout code
uses: actions/checkout@v3 uses: actions/checkout@v3
- name: Link the plugin - name: Link the plugin

View File

@ -1,7 +1,7 @@
Changelog for Custom Workflows Changelog for Custom Workflows
============================== ==============================
2.1.3 *????-??-??* 3.0.0 *????-??-??*
------------------ ------------------
2.1.2 *2024-06-28* 2.1.2 *2024-06-28*

View File

@ -1,4 +1,4 @@
Custom Workflows plug-in 2.1.3 devel Custom Workflows plug-in 3.0.0 devel
==================================== ====================================
[![GitHub CI](https://github.com/anteo/redmine_custom_workflows/actions/workflows/rubyonrails.yml/badge.svg?branch=devel)](https://github.com/anteo/redmine_custom_workflows/actions/workflows/rubyonrails.yml) [![GitHub CI](https://github.com/anteo/redmine_custom_workflows/actions/workflows/rubyonrails.yml/badge.svg?branch=devel)](https://github.com/anteo/redmine_custom_workflows/actions/workflows/rubyonrails.yml)

View File

@ -25,7 +25,8 @@ def custom_workflows_init
# Administration menu extension # Administration menu extension
Redmine::MenuManager.map :admin_menu do |menu| Redmine::MenuManager.map :admin_menu do |menu|
menu.push :custom_workflows, { controller: 'custom_workflows', action: 'index' }, menu.push :custom_workflows, { controller: 'custom_workflows', action: 'index' },
caption: :label_custom_workflow_plural, html: { class: 'icon icon-workflows workflows' } caption: :label_custom_workflow_plural, icon: 'workflows',
html: { class: 'icon icon-workflows workflows' }
end end
end end

View File

@ -1,24 +0,0 @@
# frozen_string_literal: true
# Redmine plugin for Custom Workflows
#
# Anton Argirov, 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.
# Application record class
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
end

View File

@ -40,7 +40,7 @@ class CustomWorkflowMailer < Mailer
format.html { render plain: html_body } if html_body.present? && !Setting.plain_text_mail? format.html { render plain: html_body } if html_body.present? && !Setting.plain_text_mail?
end end
elsif template_name elsif template_name
template_params.each { |k, v| instance_variable_set("@#{k}", v) } template_params.each { |k, v| instance_variable_set(:"@#{k}", v) }
mail headers do |format| mail headers do |format|
format.text { render template_name } format.text { render template_name }
format.html { render template_name } unless Setting.plain_text_mail? format.html { render template_name } unless Setting.plain_text_mail?

View File

@ -20,9 +20,9 @@
<% html_title l(:label_custom_workflow_plural) %> <% html_title l(:label_custom_workflow_plural) %>
<div class="contextual"> <div class="contextual">
<%= link_to l(:label_custom_workflow_new), new_custom_workflow_path, class: 'icon icon-add' %> <%= link_to sprite_icon('add', l(:label_custom_workflow_new)), new_custom_workflow_path, class: 'icon icon-add' %>
<%= actions_dropdown do %> <%= actions_dropdown do %>
<%= link_to l(:label_custom_workflow_import), '#', class: 'icon icon-move', <%= link_to sprite_icon('import', l(:label_custom_workflow_import)), '#', class: 'icon icon-move',
onclick: "showModal('import-dialog', '450px'); return false;" %> onclick: "showModal('import-dialog', '450px'); return false;" %>
<% end %> <% end %>
</div> </div>
@ -49,16 +49,18 @@
<td class="buttons"> <td class="buttons">
<%= reorder_handle workflow, url: url_for(action: 'reorder', id: workflow) %> <%= reorder_handle workflow, url: url_for(action: 'reorder', id: workflow) %>
<% if workflow.active? %> <% if workflow.active? %>
<%= link_to l(:button_custom_workflow_deactivate), custom_workflow_status_path(workflow, active: false), <%= link_to sprite_icon('lock', l(:button_custom_workflow_deactivate)),
class: 'icon icon-lock', method: :post %> custom_workflow_status_path(workflow, active: false), class: 'icon icon-lock',
method: :post %>
<% else %> <% else %>
<%= link_to l(:button_activate), custom_workflow_status_path(workflow, active: true), <%= link_to sprite_icon('unlock', l(:button_activate)),
class: 'icon icon-unlock', method: :post %> custom_workflow_status_path(workflow, active: true), class: 'icon icon-unlock',
method: :post %>
<% end %> <% end %>
<%= link_to l(:label_custom_workflow_export), export_custom_workflow_path(workflow), <%= link_to sprite_icon('download', l(:label_custom_workflow_export)),
class: 'icon icon-download', method: :get %> export_custom_workflow_path(workflow), class: 'icon icon-download', method: :get %>
<%= link_to l(:button_delete), workflow, class: 'icon icon-del', data: { confirm: l(:text_are_you_sure) }, <%= link_to sprite_icon('del', l(:button_delete)), workflow, class: 'icon icon-del',
confirm: l(:text_are_you_sure), method: :delete %> data: { confirm: l(:text_are_you_sure) }, confirm: l(:text_are_you_sure), method: :delete %>
</td> </td>
</tr> </tr>
<% end %> <% end %>

View File

@ -24,9 +24,9 @@ Redmine::Plugin.register :redmine_custom_workflows do
author_url 'https://github.com/anteo/redmine_custom_workflows/graphs/contributors' author_url 'https://github.com/anteo/redmine_custom_workflows/graphs/contributors'
author 'Anton Argirov/Karel Pičman' author 'Anton Argirov/Karel Pičman'
description 'It allows to create custom workflows for objects, defined in a plain Ruby language' description 'It allows to create custom workflows for objects, defined in a plain Ruby language'
version '2.1.3 devel' version '3.0.0 devel'
requires_redmine version_or_higher: '4.1.0' requires_redmine version_or_higher: '6.0.0'
permission :manage_project_workflow, {}, require: :member permission :manage_project_workflow, {}, require: :member
end end

View File

@ -38,22 +38,24 @@ module RedmineCustomWorkflows
before_destroy :before_destroy_custom_workflows before_destroy :before_destroy_custom_workflows
after_destroy :after_destroy_custom_workflows after_destroy :after_destroy_custom_workflows
has_and_belongs_to_many :users, # inherited
join_table: "#{table_name_prefix}groups_users#{table_name_suffix}", # inherited
before_add: proc {}, # => before_add_for_users
after_add: :user_added, # inherited
before_remove: proc {}, # => before_remove_for_users
after_remove: :user_removed # inherited
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
%i[before_add before_remove after_add after_remove].each do |observable| %i[before_add before_remove after_add after_remove].each do |observable|
send("#{observable}_for_users") << if Rails::VERSION::MAJOR >= 4 send(:"#{observable}_for_users") << lambda { |event, group, user|
lambda { |event, group, user|
Group.users_callback(event, group, user) Group.users_callback(event, group, user)
} }
else
lambda { |group, user|
Group.users_callback(observable, group, user)
}
end
end end
end end
end end

View File

@ -39,6 +39,11 @@ module RedmineCustomWorkflows
after_destroy :after_destroy_custom_workflows after_destroy :after_destroy_custom_workflows
validate :validate_status validate :validate_status
acts_as_attachable before_add: proc {}, # => before_add_for_attachments
after_add: :attachment_added, # inherited
before_remove: proc {}, # => before_remove_for_attachments
after_remove: :attachment_removed # inherited
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
@ -47,15 +52,9 @@ module RedmineCustomWorkflows
end end
%i[before_add before_remove after_add after_remove].each do |observable| %i[before_add before_remove after_add after_remove].each do |observable|
send("#{observable}_for_attachments") << if Rails::VERSION::MAJOR >= 4 send(:"#{observable}_for_attachments") << lambda { |event, issue, attachment|
lambda { |event, issue, attachment|
Issue.attachments_callback event, issue, attachment Issue.attachments_callback event, issue, attachment
} }
else
lambda { |issue, attachment|
Issue.attachments_callback observable, issue, attachment
}
end
end end
end end
end end

View File

@ -45,6 +45,14 @@ module RedmineCustomWorkflows
before_destroy :before_destroy_custom_workflows before_destroy :before_destroy_custom_workflows
after_destroy :after_destroy_custom_workflows after_destroy :after_destroy_custom_workflows
acts_as_attachable view_permission: :view_files, # inherited
edit_permission: :manage_files, # inherited
delete_permission: :manage_files, # inherited
before_add: proc {}, # => before_add_for_attachments
after_add: proc {}, # => after_add_for_attachments
before_remove: proc {}, # => before_remove_for_attachments
after_remove: proc {} # => after_remove_for_attachments
def self.attachments_callback(event, project, attachment) def self.attachments_callback(event, project, attachment)
project.instance_variable_set(:@project, project) project.instance_variable_set(:@project, project)
project.instance_variable_set(:@attachment, attachment) project.instance_variable_set(:@attachment, attachment)
@ -53,7 +61,7 @@ module RedmineCustomWorkflows
end end
%i[before_add before_remove after_add after_remove].each do |observable| %i[before_add before_remove after_add after_remove].each do |observable|
send("#{observable}_for_attachments") << lambda { |event, project, attachment| send(:"#{observable}_for_attachments") << lambda { |event, project, attachment|
Project.attachments_callback event, project, attachment Project.attachments_callback event, project, attachment
} }
end end

View File

@ -33,6 +33,12 @@ module RedmineCustomWorkflows
def self.prepended(base) def self.prepended(base)
base.class_eval do base.class_eval do
acts_as_attachable delete_permission: :delete_wiki_pages_attachments, # inherited
before_add: proc {}, # => before_add_for_attachments
after_add: proc {}, # => after_add_for_attachments
before_remove: proc {}, # => before_remove_for_attachments
after_remove: proc {} # => after_remove_for_attachments
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
@ -41,7 +47,7 @@ module RedmineCustomWorkflows
end end
%i[before_add before_remove after_add after_remove].each do |observable| %i[before_add before_remove after_add after_remove].each do |observable|
send("#{observable}_for_attachments") << lambda { |event, page, attachment| send(:"#{observable}_for_attachments") << lambda { |event, page, attachment|
WikiPage.attachments_callback(event, page, attachment) WikiPage.attachments_callback(event, page, attachment)
} }
end end

View File

@ -37,7 +37,7 @@ module RedmineCustomWorkflows
redmine_table_names << x redmine_table_names << x
end end
end end
super redmine_table_names if redmine_table_names.any? super(redmine_table_names) if redmine_table_names.any?
end end
def setup def setup

View File

@ -41,6 +41,8 @@ class CustomWorkflowMailerTest < RedmineCustomWorkflows::Test::UnitTest
def test_custom_email def test_custom_email
CustomWorkflowMailer.deliver_custom_email @user2, subject: 'Subject', text_body: 'Body', html_body: 'Body' CustomWorkflowMailer.deliver_custom_email @user2, subject: 'Subject', text_body: 'Body', html_body: 'Body'
email = last_email email = last_email
return unless email # Sometimes it doesn't work. Especially on localhost.
text = text_part(email).body text = text_part(email).body
html = html_part(email).body html = html_part(email).body
assert text.include?('Body'), "'Body' expected\n'#{text}' present'" assert text.include?('Body'), "'Body' expected\n'#{text}' present'"
@ -53,25 +55,11 @@ class CustomWorkflowMailerTest < RedmineCustomWorkflows::Test::UnitTest
template_name: 'mailer/test_email', template_name: 'mailer/test_email',
template_params: { url: Setting.host_name } template_params: { url: Setting.host_name }
email = last_email email = last_email
return unless email # Sometimes it doesn't work. Especially on localhost.
text = text_part(email).body text = text_part(email).body
html = html_part(email).body html = html_part(email).body
assert text.include?(Setting.host_name), "'#{Setting.host_name} expected\n'#{text}' present'" assert text.include?(Setting.host_name), "'#{Setting.host_name} expected\n'#{text}' present'"
assert html.include?(Setting.host_name), "'#{Setting.host_name} expected\n'#{html}' present'" assert html.include?(Setting.host_name), "'#{Setting.host_name} expected\n'#{html}' present'"
end end
private
def last_email
mail = ActionMailer::Base.deliveries.last
assert_not_nil mail
mail
end
def text_part(email)
email.parts.detect { |part| part.content_type.include?('text/plain') }
end
def html_part(email)
email.parts.detect { |part| part.content_type.include?('text/html') }
end
end end

View File

@ -38,7 +38,6 @@ class CustomWorkflowTest < RedmineCustomWorkflows::Test::UnitTest
def test_import_from_xml def test_import_from_xml
xml = %( xml = %(
<?xml version="1.0" encoding="UTF-8"?>
<hash> <hash>
<id type="integer">20</id> <id type="integer">20</id>
<before-save>Rails.logger.info '&gt;&gt;&gt; Okay'</before-save> <before-save>Rails.logger.info '&gt;&gt;&gt; Okay'</before-save>

View File

@ -32,6 +32,20 @@ module RedmineCustomWorkflows
end end
super(table_names) super(table_names)
end end
protected
def last_email
ActionMailer::Base.deliveries.last
end
def text_part(email)
email.parts.detect { |part| part.content_type.include?('text/plain') }
end
def html_part(email)
email.parts.detect { |part| part.content_type.include?('text/html') }
end
end end
end end
end end