redmine_avm 0.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/app/controllers/issue_status_assigns_controller.rb +23 -0
- data/app/helpers/issue_status_assigns_helper.rb +11 -0
- data/app/models/issue_status_assign.rb +9 -0
- data/app/views/settings/_avm.html.erb +49 -0
- data/config/initializers/000_dependencies.rb +6 -0
- data/config/initializers/events_manager.rb +17 -0
- data/config/initializers/redmine_plugins_helper.rb +22 -0
- data/config/locales/en.yml +10 -0
- data/config/locales/pt-BR.yml +10 -0
- data/config/routes.rb +5 -0
- data/db/migrate/20160516164053_create_issue_status_assigns.rb +10 -0
- data/db/migrate/20180711164719_rename_plugin_avm_to_redmine_avm.rb +40 -0
- data/init.rb +21 -0
- data/lib/avm/issue/assign.rb +49 -0
- data/lib/avm/issue/dependencies_section_check.rb +58 -0
- data/lib/avm/issue/motivation_check.rb +33 -0
- data/lib/avm/issue/unblock.rb +45 -0
- data/lib/avm/issue/undefine.rb +47 -0
- data/lib/avm/listeners/issue_auto_assign.rb +25 -0
- data/lib/avm/listeners/issue_auto_unblock.rb +36 -0
- data/lib/avm/listeners/issue_auto_unblock/issue_delete.rb +21 -0
- data/lib/avm/listeners/issue_auto_unblock/issue_relation_delete.rb +22 -0
- data/lib/avm/listeners/issue_auto_unblock/issue_update.rb +45 -0
- data/lib/avm/listeners/issue_auto_undefine.rb +37 -0
- data/lib/avm/listeners/issue_auto_undefine/issue_create.rb +23 -0
- data/lib/avm/listeners/issue_auto_undefine/issue_relation_create.rb +23 -0
- data/lib/avm/listeners/issue_auto_undefine/issue_update.rb +52 -0
- data/lib/avm/listeners/issue_dependencies_section_check.rb +29 -0
- data/lib/avm/listeners/issue_motivation_check.rb +29 -0
- data/lib/avm/patches/issue_patch.rb +74 -0
- data/lib/avm/patches/test_case_patch.rb +26 -0
- data/lib/avm/settings.rb +64 -0
- data/lib/redmine_avm.rb +5 -0
- data/lib/redmine_avm/version.rb +8 -0
- data/lib/tasks/issue.rake +54 -0
- data/lib/tasks/redmine_avm.rake +13 -0
- metadata +98 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 491c7e0c46f51ace5676cfbb1025bb731ab26ad3ec4814763cf66a6ec06d9c3b
|
4
|
+
data.tar.gz: b051350b016dcf1055111222f7b2c83807ecf9a4c35ddc553623333665f0b73d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: acf3a7957aad165d003a34c0333b62fb3c71c4a6d59cbbd4ea6e7bcc8693223adc3d9869f17a67a6c9e1d70d8decb77c9c92545795dbe04369ef54572598405a
|
7
|
+
data.tar.gz: cbbf26130460b0dccbe194cee629177eab52f696089e0b3ae3445f3eef2ed203a50989de2114665a59837e79fb08b051f937fb6141586da3d3bea6defe2a63b1
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class IssueStatusAssignsController < ApplicationController
|
4
|
+
before_action :require_admin, :active_scaffold_set_dynamic_options
|
5
|
+
layout 'admin'
|
6
|
+
|
7
|
+
active_scaffold :issue_status_assign do |conf|
|
8
|
+
conf.columns[:issue_status].form_ui = :select
|
9
|
+
conf.columns[:issue_field].form_ui = :select
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def active_scaffold_set_dynamic_options
|
15
|
+
active_scaffold_config.columns[:issue_field].options =
|
16
|
+
{ options: issue_field_options }
|
17
|
+
end
|
18
|
+
|
19
|
+
def issue_field_options
|
20
|
+
CustomField.where(type: 'IssueCustomField', field_format: 'user')
|
21
|
+
.map { |cf| [cf.id, cf.to_s] }
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class IssueStatusAssign < ActiveRecord::Base
|
4
|
+
belongs_to :issue_status, dependent: :destroy
|
5
|
+
belongs_to :issue_field, class_name: 'CustomField', dependent: :destroy
|
6
|
+
|
7
|
+
validates :issue_status, presence: true, uniqueness: true
|
8
|
+
validates :issue_field, presence: true
|
9
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
<p>
|
2
|
+
<label>Usuário administrador</label>
|
3
|
+
<select name="settings[admin_user_id]">
|
4
|
+
<%= options_for_select(User.all.order('firstname','lastname').collect {|u| [ u.to_s, u.id ] }, settings['admin_user_id'] ) %>
|
5
|
+
</select>
|
6
|
+
</p>
|
7
|
+
<p>
|
8
|
+
<label>Situação de tarefa indefinda.</label>
|
9
|
+
<select name="settings[issue_status_undefined_id]">
|
10
|
+
<%= options_for_select([['-','']] + IssueStatus.all.collect {|p| [ p.name, p.id ] }, settings['issue_status_undefined_id'] ) %>
|
11
|
+
</select>
|
12
|
+
</p>
|
13
|
+
<p>
|
14
|
+
<label>Situação de tarefa bloqueada.</label>
|
15
|
+
<select name="settings[issue_status_blocked_id]">
|
16
|
+
<%= options_for_select([['-','']] + IssueStatus.all.collect {|p| [ p.name, p.id ] }, settings['issue_status_blocked_id'] ) %>
|
17
|
+
</select>
|
18
|
+
</p>
|
19
|
+
<p>
|
20
|
+
<label>Situação de tarefa desbloqueada.</label>
|
21
|
+
<select name="settings[issue_status_unblocked_id]">
|
22
|
+
<%= options_for_select([['-','']] + IssueStatus.all.collect {|p| [ p.name, p.id ] }, settings['issue_status_unblocked_id'] ) %>
|
23
|
+
</select>
|
24
|
+
</p>
|
25
|
+
<p>
|
26
|
+
<label>Título da seção de dependências.</label>
|
27
|
+
<%= text_field_tag 'settings[dependencies_section_title]',
|
28
|
+
settings['dependencies_section_title'] %>
|
29
|
+
</p>
|
30
|
+
<p>
|
31
|
+
<label>Mensagem para seção de dependências não encontrada.</label>
|
32
|
+
<%= text_area_tag 'settings[no_dependencies_section_message]',
|
33
|
+
settings['no_dependencies_section_message'] %>
|
34
|
+
</p>
|
35
|
+
<p>
|
36
|
+
<label>Mensagem para dependências ausentes na seção de dependências.</label>
|
37
|
+
<%= text_area_tag 'settings[dependencies_section_missing_dependencies_message]',
|
38
|
+
settings['dependencies_section_missing_dependencies_message'] %>
|
39
|
+
</p>
|
40
|
+
<p>
|
41
|
+
<label>Título da seção de motivação.</label>
|
42
|
+
<%= text_field_tag 'settings[motivation_section_title]',
|
43
|
+
settings['motivation_section_title'] %>
|
44
|
+
</p>
|
45
|
+
<p>
|
46
|
+
<label>Mensagem indefinição por imotivação.</label>
|
47
|
+
<%= text_area_tag 'settings[unmotivated_message]',
|
48
|
+
settings['unmotivated_message'] %>
|
49
|
+
</p>
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
EventsManager.add_listener(Issue, :create, 'Avm::Listeners::IssueAutoUndefine')
|
4
|
+
EventsManager.add_listener(Issue, :update, 'Avm::Listeners::IssueAutoUndefine')
|
5
|
+
EventsManager.add_listener(IssueRelation, :create, 'Avm::Listeners::IssueAutoUndefine')
|
6
|
+
EventsManager.add_listener(IssueRelation, :delete, 'Avm::Listeners::IssueAutoUnblock')
|
7
|
+
EventsManager.add_listener(Issue, :delete, 'Avm::Listeners::IssueAutoUnblock')
|
8
|
+
EventsManager.add_listener(Issue, :update, 'Avm::Listeners::IssueAutoUnblock')
|
9
|
+
EventsManager.add_listener(Issue, :create, 'Avm::Listeners::IssueAutoAssign')
|
10
|
+
EventsManager.add_listener(Issue, :update, 'Avm::Listeners::IssueAutoAssign')
|
11
|
+
EventsManager.add_listener(Issue, :update,
|
12
|
+
'Avm::Listeners::IssueDependenciesSectionCheck')
|
13
|
+
EventsManager.add_listener(IssueRelation, :create,
|
14
|
+
'Avm::Listeners::IssueDependenciesSectionCheck')
|
15
|
+
[[Issue, :create], [Issue, :update], [IssueRelation, :create]].each do |v|
|
16
|
+
EventsManager.add_listener(v.first, v.last, 'Avm::Listeners::IssueMotivationCheck')
|
17
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
default_value = {
|
4
|
+
dependencies_section_title: 'Dependencies',
|
5
|
+
no_dependencies_section_message: <<MESSAGE.strip_heredoc,
|
6
|
+
Dependencies section not found.
|
7
|
+
Customize this message in plugin AVM's configuration.
|
8
|
+
MESSAGE
|
9
|
+
dependencies_section_missing_dependencies_message: <<MESSAGE.strip_heredoc,
|
10
|
+
Missing dependencies found dependencies section: %{ids}.
|
11
|
+
Customize this message in plugin AVM's configuration.
|
12
|
+
MESSAGE
|
13
|
+
motivation_section_title: 'Motivation',
|
14
|
+
unmotivated_message: 'This issue is not motivated.'
|
15
|
+
}
|
16
|
+
|
17
|
+
if ActiveRecord::Base.connection.table_exists? ::User.table_name
|
18
|
+
default_admin = ::User.where(id: 1, admin: true).first
|
19
|
+
default_value[:admin_user_id] = default_admin.id if default_admin
|
20
|
+
end
|
21
|
+
|
22
|
+
::RedminePluginsHelper::Settings.default(:redmine_avm, default_value)
|
@@ -0,0 +1,10 @@
|
|
1
|
+
en:
|
2
|
+
issue_assign_message: |
|
3
|
+
Assigned to *%{user}* because:
|
4
|
+
|
5
|
+
# The issue's status is *%{issue_status}*;
|
6
|
+
# For this status the expected role is *%{issue_field}*;
|
7
|
+
# *%{user}* is the issue's *%{issue_field}*.
|
8
|
+
issue_unblock_message: 'This issue was unblocked because has no open dependencies.'
|
9
|
+
issue_undefine_message: 'This issue was undefined because it has undefined dependencies: %{undefined_dependencies_ids}.'
|
10
|
+
label_issue_status_assigns: Issue status assigns
|
@@ -0,0 +1,10 @@
|
|
1
|
+
pt-BR:
|
2
|
+
issue_assign_message: |
|
3
|
+
Atribuído para *%{user}* porque:
|
4
|
+
|
5
|
+
# A tarefa está na situação *%{issue_status}*;
|
6
|
+
# Nessa situação o responsável esperado é o *%{issue_field}*;
|
7
|
+
# *%{user}* é o *%{issue_field}* da tarefa.
|
8
|
+
issue_unblock_message: 'Esta tarefa foi desbloqueada porque não possui dependências abertas.'
|
9
|
+
issue_undefine_message: 'Esta tarefa foi indefinida porque possui dependências indefinidas: %{undefined_dependencies_ids}.'
|
10
|
+
label_issue_status_assigns: Atribuição por situações de tarefa
|
data/config/routes.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class RenamePluginAvmToRedmineAvm < ActiveRecord::Migration
|
4
|
+
class Settings < ActiveRecord::Base
|
5
|
+
end
|
6
|
+
|
7
|
+
def up
|
8
|
+
rename_plugin('avm', 'redmine_avm')
|
9
|
+
end
|
10
|
+
|
11
|
+
def down
|
12
|
+
rename_plugin('redmine_avm', 'avm')
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def rename_plugin(from, to)
|
18
|
+
old_record = Setting.find_by(name: from)
|
19
|
+
return if old_record.blank?
|
20
|
+
|
21
|
+
new_record = Setting.find_by(name: to)
|
22
|
+
if new_record.present?
|
23
|
+
new_value = plugin_setting_value(old_record).value.merge(plugin_setting_value(new_record))
|
24
|
+
new_record.update!(value: new_value)
|
25
|
+
old_record.destroy!
|
26
|
+
else
|
27
|
+
old_record.update!(name: to)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def plugin_setting_value(record)
|
32
|
+
if record.value.blank?
|
33
|
+
{}.with_indifferent_access
|
34
|
+
elsif record.value.is_a?(Hash)
|
35
|
+
record.value
|
36
|
+
else
|
37
|
+
raise "#{record} value is not Hash or blank"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/init.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'redmine'
|
4
|
+
|
5
|
+
require 'avm/patches/issue_patch'
|
6
|
+
require 'avm/patches/test_case_patch'
|
7
|
+
|
8
|
+
Redmine::Plugin.register :redmine_avm do
|
9
|
+
name 'Redmine\'s AVM plugin'
|
10
|
+
author ::RedmineNonprojectModules::AUTHOR
|
11
|
+
description ::RedmineAvm::SUMMARY
|
12
|
+
version ::RedmineNonprojectModules::VERSION
|
13
|
+
url ::RedmineNonprojectModules::HOMEPAGE
|
14
|
+
|
15
|
+
settings(partial: 'settings/avm')
|
16
|
+
|
17
|
+
Redmine::MenuManager.map :admin_menu do |menu|
|
18
|
+
menu.push :issue_status_assigns, { controller: 'issue_status_assigns', action: 'index' },
|
19
|
+
caption: :label_issue_status_assigns
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Avm
|
4
|
+
module Issue
|
5
|
+
class Assign
|
6
|
+
attr_reader :issue
|
7
|
+
|
8
|
+
def initialize(issue)
|
9
|
+
@issue = issue
|
10
|
+
end
|
11
|
+
|
12
|
+
def run
|
13
|
+
return unless issue_status_assign && issue_has_assign_field?
|
14
|
+
return unless issue.assigned_to || issue_status_user
|
15
|
+
return if issue.assigned_to == issue_status_user
|
16
|
+
|
17
|
+
assign
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def issue_status_assign
|
23
|
+
@issue_status_assign ||= IssueStatusAssign.where(issue_status: issue.status).first
|
24
|
+
end
|
25
|
+
|
26
|
+
def issue_has_assign_field?
|
27
|
+
issue.available_custom_fields.include?(issue_status_assign.issue_field)
|
28
|
+
end
|
29
|
+
|
30
|
+
def issue_status_user
|
31
|
+
id = issue.custom_field_value(issue_status_assign.issue_field)
|
32
|
+
id.present? ? User.find(id) : nil
|
33
|
+
end
|
34
|
+
|
35
|
+
def assign
|
36
|
+
user = issue_status_user
|
37
|
+
Rails.logger.debug("Assigning #{user} to #{issue}")
|
38
|
+
user_label = user ? user.to_s : 'NENHUM'
|
39
|
+
@issue.init_journal(
|
40
|
+
Avm::Settings.admin_user,
|
41
|
+
I18n.translate(:issue_assign_message, user: user_label, issue_status: issue.status,
|
42
|
+
issue_field: issue_status_assign.issue_field.name)
|
43
|
+
)
|
44
|
+
@issue.assigned_to = user
|
45
|
+
@issue.save!
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Avm
|
4
|
+
module Issue
|
5
|
+
class DependenciesSectionCheck
|
6
|
+
def initialize(issue)
|
7
|
+
@issue = issue
|
8
|
+
end
|
9
|
+
|
10
|
+
def run
|
11
|
+
return if @issue.closed?
|
12
|
+
return if @issue.dependencies.empty?
|
13
|
+
|
14
|
+
unless @issue.dependencies_section
|
15
|
+
Rails.logger.info("\##{@issue.id}: no section found")
|
16
|
+
undefine_no_dependencies_secion
|
17
|
+
return
|
18
|
+
end
|
19
|
+
return if all_dependencies_in_dependencies_section?
|
20
|
+
|
21
|
+
Rails.logger.info("\##{@issue.id}: missing dependencies in dependency section")
|
22
|
+
undefine_no_all_dependencies
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def all_dependencies_in_dependencies_section?
|
28
|
+
sec_deps = @issue.dependencies_section_dependencies
|
29
|
+
@issue.dependencies.all? { |d| sec_deps.include?(d.id) }
|
30
|
+
end
|
31
|
+
|
32
|
+
def undefine_no_dependencies_secion
|
33
|
+
@issue.init_journal(
|
34
|
+
Avm::Settings.admin_user,
|
35
|
+
Avm::Settings.no_dependencies_section_message
|
36
|
+
)
|
37
|
+
@issue.status = Avm::Settings.issue_status_undefined
|
38
|
+
@issue.save!
|
39
|
+
end
|
40
|
+
|
41
|
+
def undefine_no_all_dependencies
|
42
|
+
@issue.init_journal(
|
43
|
+
Avm::Settings.admin_user,
|
44
|
+
format(Avm::Settings.dependencies_section_missing_dependencies_message,
|
45
|
+
ids: no_dependencies_section_ids_string)
|
46
|
+
)
|
47
|
+
@issue.status = Avm::Settings.issue_status_undefined
|
48
|
+
@issue.save!
|
49
|
+
end
|
50
|
+
|
51
|
+
def no_dependencies_section_ids_string
|
52
|
+
sec_deps = @issue.dependencies_section_dependencies
|
53
|
+
deps = @issue.dependencies.map(&:id)
|
54
|
+
(deps - sec_deps).map { |id| "##{id}" }.join(', ')
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Avm
|
4
|
+
module Issue
|
5
|
+
class MotivationCheck
|
6
|
+
def initialize(issue)
|
7
|
+
@issue = issue
|
8
|
+
end
|
9
|
+
|
10
|
+
def run
|
11
|
+
return if motivated?
|
12
|
+
|
13
|
+
Rails.logger.info("\##{@issue.id}: unmotivated")
|
14
|
+
undefine
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def motivated?
|
20
|
+
@issue.closed? || @issue.motivated?
|
21
|
+
end
|
22
|
+
|
23
|
+
def undefine
|
24
|
+
@issue.init_journal(
|
25
|
+
Avm::Settings.admin_user,
|
26
|
+
Avm::Settings.unmotivated_message
|
27
|
+
)
|
28
|
+
@issue.status = Avm::Settings.issue_status_undefined
|
29
|
+
@issue.save!
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Avm
|
4
|
+
module Issue
|
5
|
+
class Unblock
|
6
|
+
def initialize(issue)
|
7
|
+
@issue = issue
|
8
|
+
end
|
9
|
+
|
10
|
+
def run
|
11
|
+
msg = unblock?
|
12
|
+
if msg
|
13
|
+
Rails.logger.debug(msg)
|
14
|
+
else
|
15
|
+
Rails.logger.debug("##{@issue.id} has no open dependencies. Unblocking...")
|
16
|
+
unblock
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def unblock?
|
23
|
+
return "##{@issue.id} is not blocked" unless status_blocked?
|
24
|
+
return "##{@issue.id} has open dependencies" if open_dependencies?
|
25
|
+
end
|
26
|
+
|
27
|
+
def status_blocked?
|
28
|
+
@issue.status == Avm::Settings.issue_status_blocked
|
29
|
+
end
|
30
|
+
|
31
|
+
def open_dependencies?
|
32
|
+
@issue.dependencies.any? { |d| !d.closed? }
|
33
|
+
end
|
34
|
+
|
35
|
+
def unblock
|
36
|
+
@issue.init_journal(
|
37
|
+
Avm::Settings.admin_user,
|
38
|
+
I18n.translate(:issue_unblock_message)
|
39
|
+
)
|
40
|
+
@issue.status = Avm::Settings.issue_status_unblocked
|
41
|
+
@issue.save!
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Avm
|
4
|
+
module Issue
|
5
|
+
class Undefine
|
6
|
+
def initialize(issue)
|
7
|
+
@issue = issue
|
8
|
+
end
|
9
|
+
|
10
|
+
def run
|
11
|
+
msg = undefine?
|
12
|
+
if msg
|
13
|
+
Rails.logger.debug(msg)
|
14
|
+
else
|
15
|
+
Rails.logger.debug("##{@issue.id} has undefined dependencies. Undefining...")
|
16
|
+
undefine
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def undefine?
|
23
|
+
return "##{@issue.id} already undefined" if @issue.undefined?
|
24
|
+
return "##{@issue.id} already closed" if @issue.closed?
|
25
|
+
return "##{@issue.id} has no undefined dependencies" unless @issue.dependencies.any?(
|
26
|
+
&:undefined?
|
27
|
+
)
|
28
|
+
end
|
29
|
+
|
30
|
+
def undefine
|
31
|
+
@issue.init_journal(
|
32
|
+
Avm::Settings.admin_user,
|
33
|
+
I18n.translate(
|
34
|
+
:issue_undefine_message,
|
35
|
+
undefined_dependencies_ids: undefined_dependency_ids_string
|
36
|
+
)
|
37
|
+
)
|
38
|
+
@issue.status = Avm::Settings.issue_status_undefined
|
39
|
+
@issue.save!
|
40
|
+
end
|
41
|
+
|
42
|
+
def undefined_dependency_ids_string
|
43
|
+
@issue.dependencies.select(&:undefined?).map { |i| "##{i.id}" }.join(', ')
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Avm
|
4
|
+
module Listeners
|
5
|
+
class IssueAutoAssign
|
6
|
+
attr_reader :event
|
7
|
+
|
8
|
+
def initialize(event)
|
9
|
+
@event = event
|
10
|
+
end
|
11
|
+
|
12
|
+
def run
|
13
|
+
issue = issue_to_check
|
14
|
+
Avm::Issue::Assign.new(issue).run if issue
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def issue_to_check
|
20
|
+
return event.data if event.issue_create?
|
21
|
+
return event.data.issue if event.issue_update?
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Avm
|
4
|
+
module Listeners
|
5
|
+
class IssueAutoUnblock
|
6
|
+
include IssueDelete
|
7
|
+
include IssueRelationDelete
|
8
|
+
include IssueUpdate
|
9
|
+
|
10
|
+
attr_reader :event
|
11
|
+
|
12
|
+
def initialize(event)
|
13
|
+
@event = event
|
14
|
+
end
|
15
|
+
|
16
|
+
def run
|
17
|
+
return if check_conditions
|
18
|
+
|
19
|
+
Rails.logger.debug("Unblock condition not found for #{@event}")
|
20
|
+
end
|
21
|
+
|
22
|
+
def check_conditions
|
23
|
+
%w[issue_relation_deleted issue_deleted issue_updated].any? do |m|
|
24
|
+
issues = send(m)
|
25
|
+
next unless issues
|
26
|
+
|
27
|
+
issues.each do |issue|
|
28
|
+
Rails.logger.debug("#{m}: #{issue}")
|
29
|
+
Avm::Issue::Unblock.new(issue).run
|
30
|
+
end
|
31
|
+
true
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Avm
|
4
|
+
module Listeners
|
5
|
+
class IssueAutoUnblock
|
6
|
+
module IssueDelete
|
7
|
+
def issue_deleted
|
8
|
+
return unless event.entity == ::Issue && event.action == :delete
|
9
|
+
|
10
|
+
::Issue.where(status: Avm::Settings.issue_status_blocked)
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def issue
|
16
|
+
event.data
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Avm
|
4
|
+
module Listeners
|
5
|
+
class IssueAutoUnblock
|
6
|
+
module IssueRelationDelete
|
7
|
+
def issue_relation_deleted
|
8
|
+
return unless event.entity == IssueRelation && event.action == :delete
|
9
|
+
return unless relation.relation_type == IssueRelation::TYPE_BLOCKS
|
10
|
+
|
11
|
+
[relation.issue_to]
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def relation
|
17
|
+
event.data
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Avm
|
4
|
+
module Listeners
|
5
|
+
class IssueAutoUnblock
|
6
|
+
module IssueUpdate
|
7
|
+
def issue_updated
|
8
|
+
return unless issue_update_event?
|
9
|
+
return [issue] if status_changed_to_blocked?
|
10
|
+
return issue.r_dependencies if status_changed_to_closed?
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def issue_update_event?
|
16
|
+
event.entity == ::Issue && event.action == :update
|
17
|
+
end
|
18
|
+
|
19
|
+
def journal
|
20
|
+
event.data
|
21
|
+
end
|
22
|
+
|
23
|
+
def issue
|
24
|
+
journal.issue
|
25
|
+
end
|
26
|
+
|
27
|
+
def status_changed_to_blocked?
|
28
|
+
new_integer_value('status_id') == Avm::Settings.issue_status_blocked.id
|
29
|
+
end
|
30
|
+
|
31
|
+
def new_integer_value(attr)
|
32
|
+
d = journal.detail_for_attribute(attr)
|
33
|
+
return nil unless d
|
34
|
+
|
35
|
+
d.value.to_i
|
36
|
+
end
|
37
|
+
|
38
|
+
def status_changed_to_closed?
|
39
|
+
sid = new_integer_value('status_id')
|
40
|
+
sid ? IssueStatus.find(sid).is_closed : false
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Avm
|
4
|
+
module Listeners
|
5
|
+
class IssueAutoUndefine
|
6
|
+
include IssueCreate
|
7
|
+
include IssueUpdate
|
8
|
+
include IssueRelationCreate
|
9
|
+
|
10
|
+
attr_reader :event
|
11
|
+
|
12
|
+
def initialize(event)
|
13
|
+
@event = event
|
14
|
+
end
|
15
|
+
|
16
|
+
def run
|
17
|
+
return if check_conditions
|
18
|
+
|
19
|
+
Rails.logger.debug("Undefine condition not found for #{@event}")
|
20
|
+
end
|
21
|
+
|
22
|
+
def check_conditions
|
23
|
+
%w[issue_created_undefined issue_updated_undefined
|
24
|
+
issue_relation_created_undefined].any? do |m|
|
25
|
+
issues = send(m)
|
26
|
+
next unless issues
|
27
|
+
|
28
|
+
issues.each do |issue|
|
29
|
+
Rails.logger.debug("#{m}: #{issue}")
|
30
|
+
Avm::Issue::Undefine.new(issue).run
|
31
|
+
end
|
32
|
+
true
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Avm
|
4
|
+
module Listeners
|
5
|
+
class IssueAutoUndefine
|
6
|
+
module IssueCreate
|
7
|
+
def issue_created_undefined
|
8
|
+
return unless event.entity == Issue && event.action == :create
|
9
|
+
return unless issue.status == Avm::Settings.issue_status_undefined
|
10
|
+
return unless issue.parent
|
11
|
+
|
12
|
+
[issue.parent]
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def issue
|
18
|
+
event.data
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Avm
|
4
|
+
module Listeners
|
5
|
+
class IssueAutoUndefine
|
6
|
+
module IssueRelationCreate
|
7
|
+
def issue_relation_created_undefined
|
8
|
+
return unless @event.entity == IssueRelation && @event.action == :create
|
9
|
+
return unless relation.relation_type == IssueRelation::TYPE_BLOCKS
|
10
|
+
return unless relation.issue_from.status == Avm::Settings.issue_status_undefined
|
11
|
+
|
12
|
+
[relation.issue_to]
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def relation
|
18
|
+
event.data
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Avm
|
4
|
+
module Listeners
|
5
|
+
class IssueAutoUndefine
|
6
|
+
module IssueUpdate
|
7
|
+
def issue_updated_undefined
|
8
|
+
return unless issue_update_event?
|
9
|
+
return journal.issue.r_dependencies if status_changed_to_undefined?
|
10
|
+
return [journal.issue] if status_changed_from_undefined?
|
11
|
+
return [journal.issue.parent] if parent_changed_and_undefined?
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def issue_update_event?
|
17
|
+
@event.entity == Issue && @event.action == :update
|
18
|
+
end
|
19
|
+
|
20
|
+
def journal
|
21
|
+
@event.data
|
22
|
+
end
|
23
|
+
|
24
|
+
def status_changed_to_undefined?
|
25
|
+
new_integer_value('status_id') == Avm::Settings.issue_status_undefined.id
|
26
|
+
end
|
27
|
+
|
28
|
+
def status_changed_from_undefined?
|
29
|
+
old_integer_value('status_id') == Avm::Settings.issue_status_undefined.id
|
30
|
+
end
|
31
|
+
|
32
|
+
def parent_changed_and_undefined?
|
33
|
+
new_integer_value('parent_id') && journal.issue.undefined?
|
34
|
+
end
|
35
|
+
|
36
|
+
def new_integer_value(attr)
|
37
|
+
d = journal.detail_for_attribute(attr)
|
38
|
+
return nil unless d
|
39
|
+
|
40
|
+
d.value.to_i
|
41
|
+
end
|
42
|
+
|
43
|
+
def old_integer_value(attr)
|
44
|
+
d = journal.detail_for_attribute(attr)
|
45
|
+
return nil unless d
|
46
|
+
|
47
|
+
d.old_value.to_i
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Avm
|
4
|
+
module Listeners
|
5
|
+
class IssueDependenciesSectionCheck
|
6
|
+
attr_reader :event
|
7
|
+
|
8
|
+
def initialize(event)
|
9
|
+
@event = event
|
10
|
+
end
|
11
|
+
|
12
|
+
def run
|
13
|
+
issue = issue_to_check
|
14
|
+
return unless issue
|
15
|
+
return if issue.status == Avm::Settings.issue_status_undefined
|
16
|
+
|
17
|
+
Avm::Issue::DependenciesSectionCheck.new(issue).run if issue
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def issue_to_check
|
23
|
+
return event.data.issue if event.issue_update?
|
24
|
+
return event.data.issue_to if event.issue_relation_create? &&
|
25
|
+
event.data.relation_type == IssueRelation::TYPE_BLOCKS
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Avm
|
4
|
+
module Listeners
|
5
|
+
class IssueMotivationCheck
|
6
|
+
attr_reader :event
|
7
|
+
|
8
|
+
def initialize(event)
|
9
|
+
@event = event
|
10
|
+
end
|
11
|
+
|
12
|
+
def run
|
13
|
+
issue = issue_to_check
|
14
|
+
return unless issue
|
15
|
+
return if issue.status == Avm::Settings.issue_status_undefined
|
16
|
+
|
17
|
+
Avm::Issue::MotivationCheck.new(issue).run if issue
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def issue_to_check
|
23
|
+
return event.data.issue if event.issue_update?
|
24
|
+
return event.data.issue_to if event.issue_relation_create? &&
|
25
|
+
event.data.relation_type == IssueRelation::TYPE_BLOCKS
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Avm
|
4
|
+
module Patches
|
5
|
+
module IssuePatch
|
6
|
+
def self.included(base)
|
7
|
+
base.send(:include, InstanceMethods)
|
8
|
+
base.send(:include, MotivationMethods)
|
9
|
+
end
|
10
|
+
|
11
|
+
module InstanceMethods
|
12
|
+
def undefined?
|
13
|
+
status == ::Avm::Settings.issue_status_undefined
|
14
|
+
end
|
15
|
+
|
16
|
+
def description_section(section_title)
|
17
|
+
['h\1\.', '----', '$'].each do |e|
|
18
|
+
m = /h([0-9])\.\s*#{Regexp.escape(section_title)}(.+)#{e}/m
|
19
|
+
.match(description)
|
20
|
+
return m[2].strip + "\r\n" if m
|
21
|
+
end
|
22
|
+
nil
|
23
|
+
end
|
24
|
+
|
25
|
+
def dependencies
|
26
|
+
children.all + relations_to.select do |r|
|
27
|
+
r.relation_type == ::IssueRelation::TYPE_BLOCKS
|
28
|
+
end.map(&:issue_from)
|
29
|
+
end
|
30
|
+
|
31
|
+
def r_dependencies
|
32
|
+
rd = []
|
33
|
+
rd << parent if parent
|
34
|
+
rd + relations_from.select do |r|
|
35
|
+
r.relation_type == ::IssueRelation::TYPE_BLOCKS
|
36
|
+
end.map(&:issue_to)
|
37
|
+
end
|
38
|
+
|
39
|
+
def dependencies_section
|
40
|
+
description_section(::Avm::Settings.dependencies_section_title)
|
41
|
+
end
|
42
|
+
|
43
|
+
def dependencies_section_dependencies
|
44
|
+
s = dependencies_section
|
45
|
+
return [] unless s
|
46
|
+
|
47
|
+
s.scan(/\#([0-9]+)/).map { |x| x[0].to_i }
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
module MotivationMethods
|
52
|
+
def motivated?
|
53
|
+
motivated_by_self? || motivated_by_relations?
|
54
|
+
end
|
55
|
+
|
56
|
+
def motivated_by_self?
|
57
|
+
!undefined? && motivation_section.present?
|
58
|
+
end
|
59
|
+
|
60
|
+
def motivated_by_relations?
|
61
|
+
!undefined? && r_dependencies.any?(&:motivated?)
|
62
|
+
end
|
63
|
+
|
64
|
+
def motivation_section
|
65
|
+
description_section(::Avm::Settings.motivation_section_title)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
unless ::Issue.included_modules.include? ::Avm::Patches::IssuePatch
|
73
|
+
::Issue.send(:include, ::Avm::Patches::IssuePatch)
|
74
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Avm
|
4
|
+
module Patches
|
5
|
+
module TestCasePatch
|
6
|
+
def self.included(base)
|
7
|
+
base.setup do
|
8
|
+
s = ::Setting.plugin_redmine_avm.dup || {}
|
9
|
+
s['issue_status_undefined_id'] = 1
|
10
|
+
s['issue_status_blocked_id'] = 4
|
11
|
+
s['issue_status_unblocked_id'] = 2
|
12
|
+
s['admin_user_id'] = 1
|
13
|
+
::Setting.plugin_redmine_avm = s
|
14
|
+
::ListenerOption.listener_enable('Avm::Listeners::IssueMotivationCheck', false)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
if Rails.env.test?
|
22
|
+
require Rails.root.join('test', 'test_helper.rb')
|
23
|
+
unless ::ActiveSupport::TestCase.included_modules.include? Avm::Patches::TestCasePatch
|
24
|
+
::ActiveSupport::TestCase.send(:include, Avm::Patches::TestCasePatch)
|
25
|
+
end
|
26
|
+
end
|
data/lib/avm/settings.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Avm
|
4
|
+
class Settings
|
5
|
+
class << self
|
6
|
+
def issue_status_undefined(raise_if_empty = true)
|
7
|
+
issue_status('issue_status_undefined_id', 'indefida', raise_if_empty)
|
8
|
+
end
|
9
|
+
|
10
|
+
def issue_status_blocked(raise_if_empty = true)
|
11
|
+
issue_status('issue_status_blocked_id', 'bloqueada', raise_if_empty)
|
12
|
+
end
|
13
|
+
|
14
|
+
def issue_status_unblocked(raise_if_empty = true)
|
15
|
+
issue_status('issue_status_unblocked_id', 'desbloqueada', raise_if_empty)
|
16
|
+
end
|
17
|
+
|
18
|
+
def admin_user(raise_if_empty = true)
|
19
|
+
if Setting.plugin_redmine_avm['admin_user_id'].present?
|
20
|
+
User.find(Setting.plugin_redmine_avm['admin_user_id'])
|
21
|
+
elsif raise_if_empty
|
22
|
+
raise 'Usuário administrador não configurado. Acesse /settings/plugin/avm.'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def dependencies_section_title
|
27
|
+
required_text(__method__)
|
28
|
+
end
|
29
|
+
|
30
|
+
def no_dependencies_section_message
|
31
|
+
required_text(__method__)
|
32
|
+
end
|
33
|
+
|
34
|
+
def dependencies_section_missing_dependencies_message
|
35
|
+
required_text(__method__)
|
36
|
+
end
|
37
|
+
|
38
|
+
def motivation_section_title
|
39
|
+
required_text(__method__)
|
40
|
+
end
|
41
|
+
|
42
|
+
def unmotivated_message
|
43
|
+
required_text(__method__)
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def issue_status(key, message, raise_if_empty)
|
49
|
+
if Setting.plugin_redmine_avm[key].present?
|
50
|
+
IssueStatus.find(Setting.plugin_redmine_avm[key])
|
51
|
+
elsif raise_if_empty
|
52
|
+
raise "Situação de tarefa #{message} não configurada. Acesse /settings/plugin/avm."
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def required_text(key)
|
57
|
+
v = Setting.plugin_redmine_avm[key]
|
58
|
+
return v if v.present?
|
59
|
+
|
60
|
+
raise "Setting.plugin_redmine_avm[#{key}] is empty"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
data/lib/redmine_avm.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
namespace :avm do
|
4
|
+
namespace :issue do
|
5
|
+
desc 'Verifica auto-indefinição de uma tarefa'
|
6
|
+
task :undefine, [:issue_id] => :environment do |_t, args|
|
7
|
+
Avm::Issue::Undefine.new(Issue.find(args.issue_id)).run
|
8
|
+
end
|
9
|
+
|
10
|
+
desc 'Verifica auto-indefinição de todas as tarefas indefinidas'
|
11
|
+
task undefine_all: :environment do |_t, _args|
|
12
|
+
Issue.where.not(status: Avm::Settings.issue_status_undefined).each do |i|
|
13
|
+
Avm::Issue::Undefine.new(i).run
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
desc 'Verifica auto-desbloqueio de uma tarefa'
|
18
|
+
task unblock: :environment do |_t, _args|
|
19
|
+
Issue.where(status: Avm::Settings.issue_status_blocked).each do |i|
|
20
|
+
Avm::Issue::Unblock.new(i).run
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
desc 'Verifica auto-desbloqueio de todas as tarefas'
|
25
|
+
task unblock_all: :environment do |_t, _args|
|
26
|
+
Issue.where(status: Avm::Settings.issue_status_blocked).each do |i|
|
27
|
+
Avm::Issue::Unblock.new(i).run
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
desc 'Verifica auto-atribuição de todas as tarefas'
|
32
|
+
task assign_all: :environment do |_t, _args|
|
33
|
+
Issue.all.each do |i|
|
34
|
+
Avm::Issue::Assign.new(i).run
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
desc 'Verifica a seção de dependências de todas as tarefas'
|
39
|
+
task dependencies_section_check_all: :environment do |_t, _args|
|
40
|
+
Issue.where(closed_on: nil)
|
41
|
+
.where.not(status: Avm::Settings.issue_status_undefined).each do |i|
|
42
|
+
Avm::Issue::DependenciesSectionCheck.new(i).run
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
desc 'Verifica a ausência de motivação em todas as tarefas'
|
47
|
+
task motivation_check_all: :environment do |_t, _args|
|
48
|
+
Issue.where(closed_on: nil)
|
49
|
+
.where.not(status: Avm::Settings.issue_status_undefined).each do |i|
|
50
|
+
Avm::Issue::MotivationCheck.new(i).run
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
namespace :redmine_avm do
|
4
|
+
Rake::TestTask.new(test: 'db:test:prepare') do |t|
|
5
|
+
plugin_root = ::File.dirname(::File.dirname(__dir__))
|
6
|
+
|
7
|
+
t.description = 'Run plugin redmine_avm\'s tests.'
|
8
|
+
t.libs << 'test'
|
9
|
+
t.test_files = FileList["#{plugin_root}/test/**/*_test.rb"]
|
10
|
+
t.verbose = false
|
11
|
+
t.warning = false
|
12
|
+
end
|
13
|
+
end
|
metadata
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: redmine_avm
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.3.3
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Eduardo Henrique Bogoni
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2019-08-29 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: aranha
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.10'
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 0.10.1
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - "~>"
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0.10'
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 0.10.1
|
33
|
+
description:
|
34
|
+
email:
|
35
|
+
executables: []
|
36
|
+
extensions: []
|
37
|
+
extra_rdoc_files: []
|
38
|
+
files:
|
39
|
+
- app/controllers/issue_status_assigns_controller.rb
|
40
|
+
- app/helpers/issue_status_assigns_helper.rb
|
41
|
+
- app/models/issue_status_assign.rb
|
42
|
+
- app/views/settings/_avm.html.erb
|
43
|
+
- config/initializers/000_dependencies.rb
|
44
|
+
- config/initializers/events_manager.rb
|
45
|
+
- config/initializers/redmine_plugins_helper.rb
|
46
|
+
- config/locales/en.yml
|
47
|
+
- config/locales/pt-BR.yml
|
48
|
+
- config/routes.rb
|
49
|
+
- db/migrate/20160516164053_create_issue_status_assigns.rb
|
50
|
+
- db/migrate/20180711164719_rename_plugin_avm_to_redmine_avm.rb
|
51
|
+
- init.rb
|
52
|
+
- lib/avm/issue/assign.rb
|
53
|
+
- lib/avm/issue/dependencies_section_check.rb
|
54
|
+
- lib/avm/issue/motivation_check.rb
|
55
|
+
- lib/avm/issue/unblock.rb
|
56
|
+
- lib/avm/issue/undefine.rb
|
57
|
+
- lib/avm/listeners/issue_auto_assign.rb
|
58
|
+
- lib/avm/listeners/issue_auto_unblock.rb
|
59
|
+
- lib/avm/listeners/issue_auto_unblock/issue_delete.rb
|
60
|
+
- lib/avm/listeners/issue_auto_unblock/issue_relation_delete.rb
|
61
|
+
- lib/avm/listeners/issue_auto_unblock/issue_update.rb
|
62
|
+
- lib/avm/listeners/issue_auto_undefine.rb
|
63
|
+
- lib/avm/listeners/issue_auto_undefine/issue_create.rb
|
64
|
+
- lib/avm/listeners/issue_auto_undefine/issue_relation_create.rb
|
65
|
+
- lib/avm/listeners/issue_auto_undefine/issue_update.rb
|
66
|
+
- lib/avm/listeners/issue_dependencies_section_check.rb
|
67
|
+
- lib/avm/listeners/issue_motivation_check.rb
|
68
|
+
- lib/avm/patches/issue_patch.rb
|
69
|
+
- lib/avm/patches/test_case_patch.rb
|
70
|
+
- lib/avm/settings.rb
|
71
|
+
- lib/redmine_avm.rb
|
72
|
+
- lib/redmine_avm/version.rb
|
73
|
+
- lib/tasks/issue.rake
|
74
|
+
- lib/tasks/redmine_avm.rake
|
75
|
+
homepage: https://github.com/esquilo-azul/redmine_avm
|
76
|
+
licenses: []
|
77
|
+
metadata: {}
|
78
|
+
post_install_message:
|
79
|
+
rdoc_options: []
|
80
|
+
require_paths:
|
81
|
+
- lib
|
82
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
83
|
+
requirements:
|
84
|
+
- - ">="
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
version: '0'
|
87
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
88
|
+
requirements:
|
89
|
+
- - ">="
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
version: '0'
|
92
|
+
requirements: []
|
93
|
+
rubyforge_project:
|
94
|
+
rubygems_version: 2.7.7
|
95
|
+
signing_key:
|
96
|
+
specification_version: 4
|
97
|
+
summary: AVM support for Redmine.
|
98
|
+
test_files: []
|