redmine_avm 0.3.3
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|