Bug 108088 - Bug change triggers
git-svn-id: svn://svn.office.custis.ru/3rdparty/bugzilla.org/trunk@1624 6955db30-a419-402b-8a0d-67ecbb4d7f56master
parent
db49e899bf
commit
c1dfdb5a8f
|
@ -52,6 +52,15 @@ if ($params->{save})
|
|||
($params->{on_update} ? 1 : 0) * CF_UPDATE |
|
||||
($params->{on_create} ? 1 : 0) * CF_CREATE |
|
||||
($params->{deny_all} ? 1 : 0) * CF_DENY;
|
||||
# Триггеры
|
||||
my $triggers;
|
||||
for (keys %$params)
|
||||
{
|
||||
if ($params->{$_} !~ /^\s*$/so && /^triggers_(.*)$/so)
|
||||
{
|
||||
$triggers->{$1} = $params->{$_};
|
||||
}
|
||||
}
|
||||
# Создаём/обновляем
|
||||
my $ch;
|
||||
if ($params->{create})
|
||||
|
@ -62,6 +71,7 @@ if ($params->{save})
|
|||
message => $params->{message},
|
||||
flags => $flags,
|
||||
except_fields => $except,
|
||||
triggers => $triggers,
|
||||
});
|
||||
}
|
||||
else
|
||||
|
@ -71,6 +81,7 @@ if ($params->{save})
|
|||
$ch->set_message($params->{message});
|
||||
$ch->set_flags($flags);
|
||||
$ch->set_except_fields($except);
|
||||
$ch->set_triggers($triggers);
|
||||
$ch->update;
|
||||
}
|
||||
delete_token($params->{token});
|
||||
|
|
|
@ -13,11 +13,18 @@ use Bugzilla::Error;
|
|||
use constant DB_TABLE => 'checkers';
|
||||
|
||||
use constant {
|
||||
CF_FREEZE => 0x01, # да => заморозка, нет => проверка значений
|
||||
CF_FATAL => 0x02, # да => ошибка, нет => предупреждение
|
||||
CF_CREATE => 0x04, # да => проверять при создании бага, нет => не проверять
|
||||
CF_UPDATE => 0x08, # да => проверять при обновлении бага, нет => не проверять
|
||||
CF_DENY => 0x10, # да => запрещать изменения всех полей, кроме..., нет => разрешать изменения всех полей, кроме...
|
||||
# Да => проверка старого состояния бага ("заморозка")
|
||||
# Нет => проверка нового состояния бага ("проверка новых значений")
|
||||
CF_FREEZE => 0x01,
|
||||
# Да => ошибка, нет => предупреждение
|
||||
CF_FATAL => 0x02,
|
||||
# Да => проверять при создании бага, нет => не проверять
|
||||
CF_CREATE => 0x04,
|
||||
# Да => проверять при обновлении бага, нет => не проверять
|
||||
CF_UPDATE => 0x08,
|
||||
# Да => запрещать изменения всех полей, кроме except_fields
|
||||
# Нет => разрешать изменения всех полей, кроме except_fields
|
||||
CF_DENY => 0x10,
|
||||
};
|
||||
|
||||
our @EXPORT = qw(CF_FREEZE CF_FATAL CF_CREATE CF_UPDATE CF_DENY);
|
||||
|
@ -38,6 +45,8 @@ use constant DB_COLUMNS => (
|
|||
# если !(flags & CF_DENY), то запретить только их,
|
||||
# если !(flags & CF_DENY) и их нет, то flags |= CF_DENY
|
||||
'except_fields',
|
||||
# Триггеры - действия над полями багов (требует CF_FREEZE и !CF_FATAL)
|
||||
'triggers',
|
||||
);
|
||||
use constant NAME_FIELD => 'message';
|
||||
use constant ID_FIELD => 'id';
|
||||
|
@ -56,6 +65,7 @@ use constant UPDATE_COLUMNS => (
|
|||
'message',
|
||||
'sql_code',
|
||||
'except_fields',
|
||||
'triggers',
|
||||
);
|
||||
|
||||
# Перепостроение и перекэширование SQL-запроса в базу
|
||||
|
@ -95,6 +105,10 @@ sub create
|
|||
{
|
||||
$params->{except_fields} = encode_json($params->{except_fields});
|
||||
}
|
||||
if ($params->{triggers})
|
||||
{
|
||||
$params->{triggers} = encode_json($params->{triggers});
|
||||
}
|
||||
my $self = Bugzilla::Object::create($class, $params);
|
||||
$self->update;
|
||||
$self->query->set_shared_with_group(Bugzilla::Group->check({ name => 'bz_editcheckers' }));
|
||||
|
@ -107,6 +121,10 @@ sub update
|
|||
my $self = shift;
|
||||
$self->refresh_sql;
|
||||
$self->query->set_shared_with_group(Bugzilla::Group->check({ name => 'bz_editcheckers' }));
|
||||
if ($self->triggers)
|
||||
{
|
||||
$self->{flags} |= CF_FREEZE;
|
||||
}
|
||||
$self->SUPER::update(@_);
|
||||
}
|
||||
|
||||
|
@ -147,14 +165,14 @@ sub flags { $_[0]->{flags} }
|
|||
|
||||
# Отдельные флаги
|
||||
sub is_freeze { $_[0]->{flags} & CF_FREEZE }
|
||||
sub is_fatal { $_[0]->{flags} & CF_FATAL }
|
||||
sub is_fatal { ($_[0]->{flags} & CF_FATAL) && !$_[0]->triggers }
|
||||
sub on_create { $_[0]->{flags} & CF_CREATE }
|
||||
sub on_update { $_[0]->{flags} & CF_UPDATE }
|
||||
sub deny_all { $_[0]->{flags} & CF_DENY }
|
||||
|
||||
# { field_name => value }
|
||||
# when value is undef, then the change of field with name=field_name to any value is an exception
|
||||
# when value is not undef, then only the change to value=value is an exception
|
||||
# Исключать изменения поля field_name на значение value,
|
||||
# либо на любое значение, если value = undef
|
||||
sub except_fields
|
||||
{
|
||||
my $self = shift;
|
||||
|
@ -165,6 +183,21 @@ sub except_fields
|
|||
return $self->{except_fields_obj};
|
||||
}
|
||||
|
||||
# { field_name => value }
|
||||
# Изменить значение поля field_name на value. Для полей с множествами значений
|
||||
# field_name также может быть add_<field_name> или remove_<field_name>, что означает
|
||||
# добавить значение или удалить значение соответственно.
|
||||
# FIXME Пока поддерживается только add_cc.
|
||||
sub triggers
|
||||
{
|
||||
my $self = shift;
|
||||
if (!exists $self->{triggers_obj})
|
||||
{
|
||||
$self->{triggers_obj} = $self->{triggers} ? decode_json($self->{triggers}) : undef;
|
||||
}
|
||||
return $self->{triggers_obj};
|
||||
}
|
||||
|
||||
sub name
|
||||
{
|
||||
my $self = shift;
|
||||
|
@ -204,5 +237,12 @@ sub set_except_fields
|
|||
delete $self->{except_fields_obj};
|
||||
}
|
||||
|
||||
sub set_triggers
|
||||
{
|
||||
my ($self, $value) = @_;
|
||||
$self->set('triggers', $value ? encode_json($value) : undef);
|
||||
delete $self->{triggers_obj};
|
||||
}
|
||||
|
||||
1;
|
||||
__END__
|
||||
|
|
|
@ -75,7 +75,21 @@ sub alert
|
|||
{
|
||||
my ($bug, $is_new) = @_;
|
||||
my (@fatal, @warn);
|
||||
map { $_->is_fatal ? push(@fatal, $_) : push(@warn, $_) } @{$bug->{failed_checkers} || []};
|
||||
for (@{$bug->{failed_checkers} || []})
|
||||
{
|
||||
if ($_->triggers)
|
||||
{
|
||||
# Триггеры нас вообще не расстраивают
|
||||
}
|
||||
elsif ($_->is_fatal)
|
||||
{
|
||||
push(@fatal, $_);
|
||||
}
|
||||
else
|
||||
{
|
||||
push(@warn, $_);
|
||||
}
|
||||
}
|
||||
my $force = 1 && Bugzilla->cgi->param('force_checkers');
|
||||
if (!@fatal && (!@warn || $force))
|
||||
{
|
||||
|
@ -110,15 +124,15 @@ sub show_checker_errors
|
|||
{
|
||||
my ($bugs) = @_;
|
||||
$bugs ||= Bugzilla->request_cache->{failed_checkers};
|
||||
return if !$bugs || !grep { @{$_->{failed_checkers} || []} } @$bugs;
|
||||
return if !$bugs || !grep { grep { !$_->triggers } @{$_->{failed_checkers} || []} } @$bugs;
|
||||
if (Bugzilla->error_mode != ERROR_MODE_WEBPAGE)
|
||||
{
|
||||
my $info = [
|
||||
map { {
|
||||
bug_id => $_->bug_id,
|
||||
errors => [ map { $_->message } @{$_->{failed_checkers}} ]
|
||||
errors => [ map { $_->message } grep { !$_->triggers } @{$_->{failed_checkers}} ]
|
||||
} }
|
||||
grep { $_->{failed_checkers} } @$bugs
|
||||
grep { @{$_->{failed_checkers} || []} } @$bugs
|
||||
];
|
||||
ThrowUserError('checks_failed', { bugs => $info });
|
||||
}
|
||||
|
@ -138,7 +152,7 @@ sub freeze_failed_checkers
|
|||
$failedbugs && @$failedbugs || return undef;
|
||||
return [
|
||||
map { [ $_->bug_id, [ map { $_->id } @{$_->{failed_checkers}} ] ] }
|
||||
grep { $_->{failed_checkers} } @$failedbugs
|
||||
grep { @{$_->{failed_checkers} || []} } @$failedbugs
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -160,10 +174,16 @@ sub unfreeze_failed_checkers
|
|||
sub filter_failed_checkers
|
||||
{
|
||||
my ($checkers, $changes, $bug) = @_;
|
||||
# фильтруем подошедшие проверки по изменённым полям
|
||||
# Фильтруем подошедшие проверки по изменённым полям
|
||||
my @rc;
|
||||
for (@$checkers)
|
||||
{
|
||||
if ($_->triggers)
|
||||
{
|
||||
# Не трогаем триггеры
|
||||
push @rc, $_;
|
||||
next;
|
||||
}
|
||||
my $e = $_->except_fields;
|
||||
my $ok = 1;
|
||||
if ($_->deny_all)
|
||||
|
@ -220,14 +240,41 @@ sub filter_failed_checkers
|
|||
@$checkers = @rc;
|
||||
}
|
||||
|
||||
# Запустить триггеры для бага $bug из $bug->{failed_checkers}
|
||||
sub run_triggers
|
||||
{
|
||||
my ($bug) = @_;
|
||||
my $modified = 0;
|
||||
for (my $i = $#{$bug->{failed_checkers}}; $i >= 0; $i--)
|
||||
{
|
||||
my $checker = $bug->{failed_checkers}->[$i];
|
||||
if ($checker->triggers)
|
||||
{
|
||||
if ($checker->triggers->{add_cc})
|
||||
{
|
||||
# FIXME Пока поддерживается только добавление CC, но несложно докрутить произвольные изменения
|
||||
for (split /[\s,]+/, $checker->triggers->{add_cc})
|
||||
{
|
||||
$bug->add_cc($_);
|
||||
$modified = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
# FIXME Нужно показывать информацию о применённом триггере (сейчас оно работает втихаря)
|
||||
splice @{$bug->{failed_checkers}}, $i, 1;
|
||||
}
|
||||
return $modified;
|
||||
}
|
||||
|
||||
# hooks:
|
||||
|
||||
sub bug_pre_update
|
||||
{
|
||||
my ($args) = @_;
|
||||
my $bug = $args->{bug};
|
||||
# запускаем проверки, работающие ДО внесения изменений (заморозка багов)
|
||||
# запускаем проверки, работающие ДО внесения изменений - заморозку багов и триггеры
|
||||
$bug->{failed_checkers} = check($bug->bug_id, CF_FREEZE | CF_UPDATE, CF_FREEZE | CF_UPDATE);
|
||||
run_triggers($bug);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -274,6 +321,11 @@ sub bug_end_of_create
|
|||
{
|
||||
alert($bug, 1);
|
||||
}
|
||||
# А ещё при создании бага триггеры срабатывают отдельным запросом
|
||||
if (run_triggers($bug))
|
||||
{
|
||||
$bug->update;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -122,6 +122,7 @@ sub db_schema_abstract_schema
|
|||
push @{$schema->{fielddefs}->{FIELDS}}, add_to_deps => {TYPE => 'INT2', NOTNULL => 1, DEFAULT => 0};
|
||||
|
||||
# Bug 68921 - Предикаты корректности из запросов поиска
|
||||
# Bug 108088 - Триггеры (пока поддерживается только 1 триггер: добавление CC)
|
||||
$schema->{checkers} = {
|
||||
FIELDS => [
|
||||
id => {TYPE => 'MEDIUMSERIAL', NOTNULL => 1, PRIMARYKEY => 1},
|
||||
|
@ -131,6 +132,7 @@ sub db_schema_abstract_schema
|
|||
message => {TYPE => 'LONGTEXT', NOTNULL => 1},
|
||||
sql_code => {TYPE => 'LONGTEXT'},
|
||||
except_fields => {TYPE => 'LONGBLOB'},
|
||||
triggers => {TYPE => 'LONGBLOB'},
|
||||
],
|
||||
INDEXES => [
|
||||
checkers_query_id_idx => { FIELDS => ['query_id'] },
|
||||
|
@ -406,6 +408,9 @@ sub install_update_fielddefs
|
|||
$dbh->bz_alter_column('checkers', message => {TYPE => 'LONGTEXT', NOTNULL => 1});
|
||||
}
|
||||
|
||||
# Bug 108088 - Триггеры (пока поддерживается только 1 триггер: добавление CC)
|
||||
$dbh->bz_add_column('checkers', triggers => {TYPE => 'LONGBLOB'});
|
||||
|
||||
# Устанавливаем значение buglist в правильное
|
||||
my @yes = map { $_->{name} } grep { $_->{buglist} } Bugzilla::Field::DEFAULT_FIELDS;
|
||||
my @no = map { $_->{name} } grep { !$_->{buglist} } Bugzilla::Field::DEFAULT_FIELDS;
|
||||
|
|
|
@ -24,11 +24,22 @@
|
|||
[% FOR c = checkers %]
|
||||
<tr>
|
||||
<td><a href="?mode=edit&id=[% c.id %]">[% c.name | html %]</a></td>
|
||||
<td style="background-color: [% c.is_fatal ? '#fdd' : '#ffd' %]; text-align: center" title="[% c.is_fatal
|
||||
? 'Жёсткий запрет: При нарушении правила изменения блокируются и выдаётся ошибка'
|
||||
: 'Мягкий запрет: При нарушении правила выдаётся предупреждение, но изменение не блокируется' %]">
|
||||
[% c.is_fatal ? "Жёсткий" : "Мягкий" %]
|
||||
[% IF c.triggers %]
|
||||
<td style="background-color: #ddf; text-align: center"
|
||||
title="Вообще не запрет, а триггер: при попадании под условия запроса в баг вносятся изменения" />
|
||||
Триггер
|
||||
</td>
|
||||
[% ELSIF c.is_fatal %]
|
||||
<td style="background-color: #fdd; text-align: center"
|
||||
title="Жёсткий запрет: При нарушении правила изменения блокируются и выдаётся ошибка" />
|
||||
Жёсткий
|
||||
</td>
|
||||
[% ELSE %]
|
||||
<td style="background-color: #ffd; text-align: center"
|
||||
title="Мягкий запрет: При нарушении правила выдаётся предупреждение, но изменение не блокируется">
|
||||
Мягкий
|
||||
</td>
|
||||
[% END %]
|
||||
<td style="background-color: [% c.is_freeze ? '#ddf' : '#fdd' %]; text-align: center" title="[% c.is_freeze
|
||||
? 'Заморозка (защита от изменения)'
|
||||
: 'Проверка корректности новых значений' %]">
|
||||
|
@ -56,14 +67,15 @@
|
|||
[% ELSE %]
|
||||
<h3>Редактирование предиката [% checker.name | html %]</h3>
|
||||
[% END %]
|
||||
<form action="?save=1&edit=1" method="POST">
|
||||
<form action="?save=1&edit=1" method="POST" onsubmit="return check_trigger()">
|
||||
<input type="hidden" name="token" value="[% token | html %]" />
|
||||
[% IF create %]
|
||||
<input type="hidden" name="create" value="1" />
|
||||
[% ELSE %]
|
||||
<input type="hidden" name="id" value="[% checker.id %]" />
|
||||
[% END %]
|
||||
<table>
|
||||
[% IF create %]
|
||||
<input type="hidden" name="create" value="1" />
|
||||
[% ELSE %]
|
||||
<input type="hidden" name="id" value="[% checker.id %]" />
|
||||
[% END %]
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Сохранённый запрос:</th>
|
||||
<td><select name="query_id">
|
||||
|
@ -77,16 +89,31 @@
|
|||
</select></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Сообщение об ошибке:</th>
|
||||
<td colspan="2" style="padding-left: 100px">
|
||||
<input type="radio" id="is_checker" name="is_trigger" value="0" onchange="switch_trigger()" [% " checked='checked'" IF !checker.triggers %] />
|
||||
<label for="is_checker">Проверка</label>
|
||||
<input type="radio" id="is_trigger" name="is_trigger" value="1" onchange="switch_trigger()" [% " checked='checked'" IF checker.triggers %] />
|
||||
<label for="is_trigger">Триггер</label>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Описание проверки:</th>
|
||||
<td><textarea name="message" rows="8" cols="80">[% checker.message | html %]</textarea></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Параметры:</th>
|
||||
<th>Момент проверки:</th>
|
||||
<td>
|
||||
<input type="checkbox" name="on_create" id="on_create" [% " checked='checked'" IF checker.on_create %] onclick="this.blur()" onblur="showhide_allowdeny()" />
|
||||
<label for="on_create" id="label_for_on_create">Проверять создание новых багов (требуется «Запрещать изменения всех полей»)</label><br />
|
||||
<input type="checkbox" name="on_update" id="on_update" [% " checked='checked'" IF checker.on_update %] onclick="this.blur()" onblur="showhide_allowdeny()" />
|
||||
<label for="on_update">Проверять изменения багов</label><br />
|
||||
<label for="on_update">Проверять изменения багов</label>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tbody id="tbody_checker" [% " style='display: none'" IF checker.triggers %]>
|
||||
<tr>
|
||||
<th>Параметры:</th>
|
||||
<td>
|
||||
<input type="checkbox" name="is_freeze" id="is_freeze" [% " checked='checked'" IF checker.is_freeze %] />
|
||||
<label for="is_freeze" id="label_for_is_freeze">Заморозка багов (защита от изменений, только для режима обновления)</label><br />
|
||||
<input type="checkbox" name="is_fatal" id="is_fatal" [% " checked='checked'" IF checker.is_fatal %] />
|
||||
|
@ -115,7 +142,16 @@
|
|||
запрет ввода трудозатрат задним числом. Флажок "Запрещать изменения всех полей"
|
||||
должен быть <b>сброшен</b>.
|
||||
</td></tr>
|
||||
</tbody>
|
||||
<tbody id="tbody_trigger" [% " style='display: none'" IF !checker.triggers %]>
|
||||
<tr>
|
||||
<th>Добавить CC:</th>
|
||||
<td><input type="text" id="triggers_add_cc" name="triggers_add_cc" value="[% checker.triggers.add_cc | html %]" /></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tbody>
|
||||
<tr><td></td><td><input type="submit" value=" Сохранить " /></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</form>
|
||||
<div id="one_field_copy" style="display:none">
|
||||
|
@ -149,6 +185,25 @@ function add_field(fld, val)
|
|||
}
|
||||
except_field_index++;
|
||||
}
|
||||
function switch_trigger()
|
||||
{
|
||||
var f = document.getElementById('is_trigger').checked;
|
||||
document.getElementById('tbody_checker').style.display = f ? 'none' : '';
|
||||
document.getElementById('tbody_trigger').style.display = f ? '' : 'none';
|
||||
}
|
||||
function check_trigger()
|
||||
{
|
||||
var f = document.getElementById('is_trigger').checked;
|
||||
var cc = document.getElementById('triggers_add_cc');
|
||||
if (!f)
|
||||
cc.value = '';
|
||||
else if (!cc.value)
|
||||
{
|
||||
alert('Задайте действие триггера!');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
[%# Загружаем текущее состояние дел %]
|
||||
showhide_allowdeny();
|
||||
[% IF checker.except_fields %]
|
||||
|
|
Loading…
Reference in New Issue