Bug 70998

git-svn-id: svn://svn.office.custis.ru/3rdparty/bugzilla.org/trunk@1006 6955db30-a419-402b-8a0d-67ecbb4d7f56
master
vfilippov 2010-10-25 13:37:16 +00:00
parent 0bd5aa03f0
commit 66c12629de
6 changed files with 145 additions and 81 deletions

View File

@ -19,8 +19,6 @@ my $cgi = Bugzilla->cgi;
my $params = $cgi->Vars; my $params = $cgi->Vars;
my $vars = {}; my $vars = {};
my @REAL_UPDATE_COLUMNS = qw(is_freeze is_fatal message);
$user->in_group('bz_editcheckers') $user->in_group('bz_editcheckers')
|| ThrowUserError('auth_failure', {group => 'bz_editcheckers', || ThrowUserError('auth_failure', {group => 'bz_editcheckers',
action => 'modify', action => 'modify',
@ -33,42 +31,52 @@ if ($params->{save})
if ($params->{edit}) if ($params->{edit})
{ {
check_token_data($params->{token}, 'editcheckers'); check_token_data($params->{token}, 'editcheckers');
# Заполняем поля-исключения
my $except = {};
for (keys %$params)
{
if (/^except_field_(\d+)$/so)
{
$except->{$params->{$_}} =
$params->{"except_field_$1_value"} || undef;
}
}
$except = undef if !%$except;
if (!$params->{deny_all} && !$except)
{
$params->{deny_all} = 1;
}
my $flags =
($params->{is_freeze} ? 1 : 0) * CF_FREEZE |
($params->{is_fatal} ? 1 : 0) * CF_FATAL |
($params->{on_update} ? 1 : 0) * CF_UPDATE |
($params->{on_create} ? 1 : 0) * CF_CREATE |
($params->{deny_all} ? 1 : 0) * CF_DENY;
# Ошибка, если CF_CREATE & (есть except_fields).
if (($flags & CF_CREATE) && $except)
{
ThrowUserError('chk_create_except');
}
# Создаём/обновляем
my $ch; my $ch;
if ($params->{create}) if ($params->{create})
{ {
$ch = Bugzilla::Checker->create({ $ch = Bugzilla::Checker->create({
(map { ($_ => $params->{$_}) } 'query_id', @REAL_UPDATE_COLUMNS), query_id => $params->{query_id},
user_id => $user->id, user_id => $user->id,
message => $params->{query_id},
flags => $flags,
except_fields => $except,
}); });
} }
else else
{ {
$ch = Bugzilla::Checker->check({ id => $id }); $ch = Bugzilla::Checker->check({ id => $id });
my $f; $ch->set_message($params->{message});
for (@REAL_UPDATE_COLUMNS) $ch->set_flags($flags);
{
$f = "set_$_";
$ch->$f($params->{$_});
}
}
# поля-исключения: если deny_all=1, то разрешить только их,
# если deny_all=0, то запретить только их,
# если deny_all=0 и их нет, то deny_all=1
my $except = { deny_all => $params->{deny_all}, except_fields => {} };
for (keys %$params)
{
if (/^except_field_(\d+)$/so)
{
$except->{except_fields}->{$params->{$_}} =
$params->{"except_field_$1_value"} || undef;
}
}
if (!%{$except->{except_fields}})
{
$except = undef;
}
$ch->set_except_fields($except); $ch->set_except_fields($except);
$ch->update; $ch->update;
}
delete_token($params->{token}); delete_token($params->{token});
} }
elsif ($params->{delete}) elsif ($params->{delete})
@ -103,7 +111,7 @@ else
} }
else else
{ {
$vars->{checker} = { deny_all => 1 }; $vars->{checker} = { is_fatal => 1, deny_all => 1, on_update => 1 };
} }
} }

View File

@ -3,27 +3,37 @@
package Bugzilla::Checker; package Bugzilla::Checker;
use strict; use strict;
use base 'Bugzilla::Object'; use base qw(Bugzilla::Object Exporter);
use JSON; use JSON;
use Bugzilla::Search::Saved; use Bugzilla::Search::Saved;
use constant DB_TABLE => 'checkers'; use constant DB_TABLE => 'checkers';
use constant {
CF_FREEZE => 0x01, # да => заморозка, нет => проверка значений
CF_FATAL => 0x02, # да => ошибка, нет => предупреждение
CF_CREATE => 0x04, # да => проверять при создании бага, нет => не проверять
CF_UPDATE => 0x08, # да => проверять при обновлении бага, нет => не проверять
CF_DENY => 0x10, # да => запрещать изменения всех полей, кроме..., нет => разрешать изменения всех полей, кроме...
};
our @EXPORT = qw(CF_FREEZE CF_FATAL CF_CREATE CF_UPDATE CF_DENY);
use constant DB_COLUMNS => ( use constant DB_COLUMNS => (
# <Это состояние> задаётся соответствием запросу поиска. # <Это состояние> задаётся соответствием запросу поиска.
'query_id', 'query_id',
# Кто создал
'user_id', 'user_id',
# Два типа: # Флаги
# Check = запрещается переход багов в это состояние из любого другого 'flags',
# ("проверка корректности изменений") # Сообщение об ошибке в случае некорректности
# Freeze = запрещается переход багов из этого состояния в любое другое
# ("заморозка бага")
'is_freeze',
'is_fatal',
'message', 'message',
# SQL-код генерировать при каждом изменении бага долго, поэтому кэшируем # SQL-код генерировать при каждом изменении бага долго, поэтому кэшируем
'sql_code', 'sql_code',
# Поля-исключения: если CF_DENY, то разрешить только их,
# если !(flags & CF_DENY), то запретить только их,
# если !(flags & CF_DENY) и их нет, то flags |= CF_DENY
'except_fields', 'except_fields',
); );
use constant NAME_FIELD => 'message'; use constant NAME_FIELD => 'message';
@ -33,14 +43,12 @@ use constant LIST_ORDER => NAME_FIELD;
use constant REQUIRED_CREATE_FIELDS => qw(query_id message); use constant REQUIRED_CREATE_FIELDS => qw(query_id message);
use constant VALIDATORS => { use constant VALIDATORS => {
query_id => \&check_query_id, query_id => \&_check_query_id,
is_fatal => \&Bugzilla::Object::check_boolean, flags => \&_check_flags,
is_freeze => \&Bugzilla::Object::check_boolean,
}; };
use constant UPDATE_COLUMNS => ( use constant UPDATE_COLUMNS => (
'is_freeze', 'flags',
'is_fatal',
'message', 'message',
'sql_code', 'sql_code',
'except_fields', 'except_fields',
@ -77,7 +85,11 @@ sub refresh_sql
sub create sub create
{ {
my ($class, $params) = @_; my ($class, $params) = @_;
Bugzilla::Object::create(@_); if ($params->{except_fields})
{
$params->{except_fields} = encode_json($params->{except_fields});
}
Bugzilla::Object::create($class, $params);
my $self = $class->new($params->{query_id}); my $self = $class->new($params->{query_id});
$self->update if $self; $self->update if $self;
return $self; return $self;
@ -92,7 +104,7 @@ sub update
} }
# Проверяем, что такой поиск существует и доступен пользователю # Проверяем, что такой поиск существует и доступен пользователю
sub check_query_id sub _check_query_id
{ {
my ($invocant, $value, $field) = @_; my ($invocant, $value, $field) = @_;
my $q = Bugzilla::Search::Saved->check({ id => $value }); my $q = Bugzilla::Search::Saved->check({ id => $value });
@ -112,14 +124,37 @@ sub check_query_id
return $q->id; return $q->id;
} }
# FIXME проверка должна быть здесь, но Bugzilla::Object не позволяет это реализовать:
# Ошибка, если CF_CREATE & (есть except_fields).
sub _check_flags
{
my ($invocant, $value, $field) = @_;
$value = int($value);
if ($value & (CF_FREEZE|CF_CREATE))
{
ThrowUserError('chk_freeze_create');
}
elsif (($value & CF_CREATE) && !($value & CF_DENY))
{
ThrowUserError('chk_create_allow');
}
return $value;
}
sub query_id { $_[0]->{query_id} } sub query_id { $_[0]->{query_id} }
sub user_id { $_[0]->{user_id} } sub user_id { $_[0]->{user_id} }
sub is_freeze { $_[0]->{is_freeze} }
sub is_fatal { $_[0]->{is_fatal} }
sub message { $_[0]->{message} } sub message { $_[0]->{message} }
sub sql_code { $_[0]->{sql_code} } sub sql_code { $_[0]->{sql_code} }
sub flags { $_[0]->{flags} }
# { deny_all => 1|0, except_fields => { field_name => value } } # Отдельные флаги
sub is_freeze { $_[0]->{flags} & CF_FREEZE }
sub is_fatal { $_[0]->{flags} & CF_FATAL }
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 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 # when value is not undef, then only the change to value=value is an exception
sub except_fields sub except_fields
@ -132,12 +167,6 @@ sub except_fields
return $self->{except_fields_obj}; return $self->{except_fields_obj};
} }
sub deny_all
{
my $self = shift;
return $self->except_fields ? $self->except_fields->{deny_all} : 1;
}
sub name sub name
{ {
my $self = shift; my $self = shift;
@ -166,8 +195,7 @@ sub user
sub set_query_id { $_[0]->set('query_id', Bugzilla::Search::Saved->check({ id => $_[1] })->id) } sub set_query_id { $_[0]->set('query_id', Bugzilla::Search::Saved->check({ id => $_[1] })->id) }
sub set_user_id { $_[0]->set('user_id', Bugzilla::User->check({ userid => $_[1] })->id) } sub set_user_id { $_[0]->set('user_id', Bugzilla::User->check({ userid => $_[1] })->id) }
sub set_is_freeze { $_[0]->set('is_freeze', $_[1]) } sub set_flags { $_[0]->set('flags', $_[1]) }
sub set_is_fatal { $_[0]->set('is_fatal', $_[1]) }
sub set_message { $_[0]->set('message', $_[1]) } sub set_message { $_[0]->set('message', $_[1]) }
sub set_sql_code { $_[0]->set('sql_code', $_[1]) } sub set_sql_code { $_[0]->set('sql_code', $_[1]) }

View File

@ -33,9 +33,16 @@ sub all
return $c->{checkers}; return $c->{checkers};
} }
# Запустить набор проверок по одному багу
# $mask - маска из флагов, которые нужно проверять для фильтрации проверок
# $flags - требуемые значения этих флагов
# Т.е. check(..., CF_UPDATE, CF_FREEZE | CF_UPDATE) выберет проверки
# с флагом CF_UPDATE, но без флага CF_FREEZE
sub check sub check
{ {
my ($bug_id, $is_freeze) = @_; my ($bug_id, $flags, $mask) = @_;
$mask ||= 0;
$flags ||= 0;
$bug_id = $bug_id->bug_id if ref $bug_id; $bug_id = $bug_id->bug_id if ref $bug_id;
$bug_id = int($bug_id) || return; $bug_id = int($bug_id) || return;
my $all = all(); my $all = all();
@ -43,7 +50,7 @@ sub check
my ($s, $i); my ($s, $i);
for (values %$all) for (values %$all)
{ {
if (!($is_freeze xor $_->is_freeze)) if (($_->flags & $mask) == $flags)
{ {
$s = $_->sql_code; $s = $_->sql_code;
$i = $_->id; $i = $_->id;
@ -100,7 +107,7 @@ sub filter_failed_checkers
my @rc; my @rc;
for (@$checkers) for (@$checkers)
{ {
my $e = $_->except_fields->{except_fields}; my $e = $_->except_fields;
my $ok = 1; my $ok = 1;
if ($_->deny_all) if ($_->deny_all)
{ {
@ -140,7 +147,7 @@ sub bug_pre_update
my ($args) = @_; my ($args) = @_;
my $bug = $args->{bug}; my $bug = $args->{bug};
# запускаем проверки, работающие ДО внесения изменений (заморозка багов) # запускаем проверки, работающие ДО внесения изменений (заморозка багов)
$bug->{failed_checkers} = check($bug->bug_id, 'freeze'); $bug->{failed_checkers} = check($bug->bug_id, CF_FREEZE | CF_UPDATE, CF_FREEZE | CF_UPDATE);
return 1; return 1;
} }
@ -154,7 +161,7 @@ sub bug_end_of_update
? [ '', scalar @{$args->{bug}->{added_comments}} ] : undef; ? [ '', scalar @{$args->{bug}->{added_comments}} ] : undef;
# запускаем проверки, работающие ПОСЛЕ внесения изменений # запускаем проверки, работающие ПОСЛЕ внесения изменений
push @{$bug->{failed_checkers}}, @{ check($bug->bug_id) }; push @{$bug->{failed_checkers}}, @{ check($bug->bug_id, CF_UPDATE, CF_FREEZE | CF_UPDATE) };
if (@{$bug->{failed_checkers}}) if (@{$bug->{failed_checkers}})
{ {
@ -175,18 +182,8 @@ sub post_bug_post_create
{ {
my ($args) = @_; my ($args) = @_;
my $bug = $args->{bug}; my $bug = $args->{bug};
$bug->{failed_checkers} = check($bug->bug_id); # При создании бага сия радость по изменениям не фильтруеццо!
if (@{$bug->{failed_checkers}}) $bug->{failed_checkers} = check($bug->bug_id, CF_CREATE, CF_CREATE);
{
# при создании бага не хочется дёргать всякие
# процедуры получения значений полей, поэтому
# работаем по упрощённому варианту
my $changes = {
map { $_->name => [ '', $bug->{$_->name} ] }
grep { $bug->{$_->name} }
Bugzilla->get_fields };
filter_failed_checkers($bug->{failed_checkers}, $changes);
}
if (@{$bug->{failed_checkers}}) if (@{$bug->{failed_checkers}})
{ {
alert($bug); alert($bug);

View File

@ -311,6 +311,28 @@ sub install_update_db
$dbh->bz_add_column('fielddefs', clone_bug => {TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 1}); $dbh->bz_add_column('fielddefs', clone_bug => {TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 1});
} }
# Временное, можно удалить
if ($dbh->bz_column_info('checkers', 'is_freeze'))
{
use Bugzilla::Checker;
use JSON;
$dbh->bz_add_column('checkers', flags => {TYPE => 'INT2', NOTNULL => 1, DEFAULT => 1});
my $rows = $dbh->selectall_arrayref('SELECT * FROM checkers', {Slice=>{}});
for (@$rows)
{
$_->{except_fields} = $_->{except_fields} ? decode_json($_->{except_fields}) : {};
$dbh->do('UPDATE checkers SET flags=?, except_fields=? WHERE query_id=?', undef,
($_->{is_freeze} ? 1 : 0) * CF_FREEZE |
($_->{is_fatal} ? 1 : 0) * CF_FATAL |
($_->{except_fields}->{deny_all} ? 1 : 0) * CF_DENY |
CF_CREATE | CF_UPDATE,
$_->{except_fields}->{except_fields} ? encode_json($_->{except_fields}->{except_fields}) : undef,
$_->{query_id});
}
$dbh->bz_drop_column('checkers', 'is_freeze');
$dbh->bz_drop_column('checkers', 'is_fatal');
}
return 1; return 1;
} }

View File

@ -53,8 +53,12 @@
<tr> <tr>
<th>Параметры:</th> <th>Параметры:</th>
<td> <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">Проверять создание новых багов</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 />
<input type="checkbox" name="is_freeze" id="is_freeze" [% " checked='checked'" IF checker.is_freeze %] /> <input type="checkbox" name="is_freeze" id="is_freeze" [% " checked='checked'" IF checker.is_freeze %] />
<label for="is_freeze">Режим заморозки багов (если нет, то режим проверки новых состояний)</label><br /> <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 %] /> <input type="checkbox" name="is_fatal" id="is_fatal" [% " checked='checked'" IF checker.is_fatal %] />
<label for="is_fatal">Запрещать изменения? (если нет, только даётся предупреждение)</label><br /> <label for="is_fatal">Запрещать изменения? (если нет, только даётся предупреждение)</label><br />
</td> </td>
@ -62,8 +66,8 @@
<tr> <tr>
<th style="background: #FFE0E0">Запрещать:</th> <th style="background: #FFE0E0">Запрещать:</th>
<td style="background: #FFE0E0"> <td style="background: #FFE0E0">
<input type="checkbox" name="deny_all" id="deny_all" onclick="this.blur(); return true;" onblur="showhide_allowdeny(this.checked)" [% " checked='checked'" IF checker.deny_all %] /> <input type="checkbox" name="deny_all" id="deny_all" onclick="this.blur(); return true;" onblur="showhide_allowdeny()" [% " checked='checked'" IF checker.deny_all %] />
<label for="deny_all">Запрещать изменения всех полей</label> &nbsp; <label for="deny_all">Запрещать изменения всех полей</label>
</td> </td>
</tr> </tr>
<tr id="except_fields_tr" style="background: #FFE0E0"> <tr id="except_fields_tr" style="background: #FFE0E0">
@ -87,10 +91,16 @@
<script language="JavaScript"> <script language="JavaScript">
var fieldids = { '':'' [% FOR f = my_fielddefs %],"[% f.name | js %]": [% loop.count %][% END %] }; var fieldids = { '':'' [% FOR f = my_fielddefs %],"[% f.name | js %]": [% loop.count %][% END %] };
var except_field_index = 0; var except_field_index = 0;
function showhide_allowdeny(chk) function showhide_allowdeny()
{ {
var chk = document.getElementById('deny_all').checked;
document.getElementById('except_fields_title').innerHTML = chk ? 'Но разрешать:' : ''; document.getElementById('except_fields_title').innerHTML = chk ? 'Но разрешать:' : '';
document.getElementById('except_fields_tr').style.backgroundColor = chk ? '#E0FFE0' : '#FFE0E0'; document.getElementById('except_fields_tr').style.backgroundColor = chk ? '#E0FFE0' : '#FFE0E0';
chk = document.getElementById('on_update').checked && !document.getElementById('on_create').checked;
if (!chk)
document.getElementById('is_freeze').checked = false;
document.getElementById('label_for_is_freeze').style.color = chk ? '' : 'gray';
document.getElementById('is_freeze').disabled = !chk;
} }
function add_field(fld, val) function add_field(fld, val)
{ {
@ -106,10 +116,10 @@ function add_field(fld, val)
except_field_index++; except_field_index++;
} }
[%# Загружаем текущее состояние дел %] [%# Загружаем текущее состояние дел %]
showhide_allowdeny(document.getElementById('deny_all').checked); showhide_allowdeny();
[% IF checker.except_fields.except_fields %] [% IF checker.except_fields %]
[% FOR f = checker.except_fields.except_fields.keys %] [% FOR f = checker.except_fields.keys %]
add_field("[% f | js %]", "[% checker.except_fields.except_fields.$f | js %]"); add_field("[% f | js %]", "[% checker.except_fields.$f | js %]");
[% END %] [% END %]
[% ELSE %] [% ELSE %]
add_field(); add_field();

View File

@ -142,7 +142,6 @@ sub reload
if ($@) if ($@)
{ {
warn $@; warn $@;
$INC{$key} ||= $file;
} }
$STATS->{$file} = $mtime; $STATS->{$file} = $mtime;
} }