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 $vars = {};
my @REAL_UPDATE_COLUMNS = qw(is_freeze is_fatal message);
$user->in_group('bz_editcheckers')
|| ThrowUserError('auth_failure', {group => 'bz_editcheckers',
action => 'modify',
@ -33,42 +31,52 @@ if ($params->{save})
if ($params->{edit})
{
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;
if ($params->{create})
{
$ch = Bugzilla::Checker->create({
(map { ($_ => $params->{$_}) } 'query_id', @REAL_UPDATE_COLUMNS),
user_id => $user->id,
query_id => $params->{query_id},
user_id => $user->id,
message => $params->{query_id},
flags => $flags,
except_fields => $except,
});
}
else
{
$ch = Bugzilla::Checker->check({ id => $id });
my $f;
for (@REAL_UPDATE_COLUMNS)
{
$f = "set_$_";
$ch->$f($params->{$_});
}
$ch->set_message($params->{message});
$ch->set_flags($flags);
$ch->set_except_fields($except);
$ch->update;
}
# поля-исключения: если 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->update;
delete_token($params->{token});
}
elsif ($params->{delete})
@ -103,7 +111,7 @@ 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;
use strict;
use base 'Bugzilla::Object';
use base qw(Bugzilla::Object Exporter);
use JSON;
use Bugzilla::Search::Saved;
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 => (
# <Это состояние> задаётся соответствием запросу поиска.
'query_id',
# Кто создал
'user_id',
# Два типа:
# Check = запрещается переход багов в это состояние из любого другого
# ("проверка корректности изменений")
# Freeze = запрещается переход багов из этого состояния в любое другое
# ("заморозка бага")
'is_freeze',
'is_fatal',
# Флаги
'flags',
# Сообщение об ошибке в случае некорректности
'message',
# SQL-код генерировать при каждом изменении бага долго, поэтому кэшируем
'sql_code',
# Поля-исключения: если CF_DENY, то разрешить только их,
# если !(flags & CF_DENY), то запретить только их,
# если !(flags & CF_DENY) и их нет, то flags |= CF_DENY
'except_fields',
);
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 VALIDATORS => {
query_id => \&check_query_id,
is_fatal => \&Bugzilla::Object::check_boolean,
is_freeze => \&Bugzilla::Object::check_boolean,
query_id => \&_check_query_id,
flags => \&_check_flags,
};
use constant UPDATE_COLUMNS => (
'is_freeze',
'is_fatal',
'flags',
'message',
'sql_code',
'except_fields',
@ -77,7 +85,11 @@ sub refresh_sql
sub create
{
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});
$self->update if $self;
return $self;
@ -92,7 +104,7 @@ sub update
}
# Проверяем, что такой поиск существует и доступен пользователю
sub check_query_id
sub _check_query_id
{
my ($invocant, $value, $field) = @_;
my $q = Bugzilla::Search::Saved->check({ id => $value });
@ -112,14 +124,37 @@ sub check_query_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 user_id { $_[0]->{user_id} }
sub is_freeze { $_[0]->{is_freeze} }
sub is_fatal { $_[0]->{is_fatal} }
sub message { $_[0]->{message} }
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 not undef, then only the change to value=value is an exception
sub except_fields
@ -132,12 +167,6 @@ sub except_fields
return $self->{except_fields_obj};
}
sub deny_all
{
my $self = shift;
return $self->except_fields ? $self->except_fields->{deny_all} : 1;
}
sub name
{
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_user_id { $_[0]->set('user_id', Bugzilla::User->check({ userid => $_[1] })->id) }
sub set_is_freeze { $_[0]->set('is_freeze', $_[1]) }
sub set_is_fatal { $_[0]->set('is_fatal', $_[1]) }
sub set_flags { $_[0]->set('flags', $_[1]) }
sub set_message { $_[0]->set('message', $_[1]) }
sub set_sql_code { $_[0]->set('sql_code', $_[1]) }

View File

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

View File

@ -53,8 +53,12 @@
<tr>
<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">Проверять создание новых багов</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 %] />
<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 %] />
<label for="is_fatal">Запрещать изменения? (если нет, только даётся предупреждение)</label><br />
</td>
@ -62,8 +66,8 @@
<tr>
<th style="background: #FFE0E0">Запрещать:</th>
<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 %] />
<label for="deny_all">Запрещать изменения всех полей</label> &nbsp;
<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>
</td>
</tr>
<tr id="except_fields_tr" style="background: #FFE0E0">
@ -87,10 +91,16 @@
<script language="JavaScript">
var fieldids = { '':'' [% FOR f = my_fielddefs %],"[% f.name | js %]": [% loop.count %][% END %] };
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_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)
{
@ -106,10 +116,10 @@ function add_field(fld, val)
except_field_index++;
}
[%# Загружаем текущее состояние дел %]
showhide_allowdeny(document.getElementById('deny_all').checked);
[% IF checker.except_fields.except_fields %]
[% FOR f = checker.except_fields.except_fields.keys %]
add_field("[% f | js %]", "[% checker.except_fields.except_fields.$f | js %]");
showhide_allowdeny();
[% IF checker.except_fields %]
[% FOR f = checker.except_fields.keys %]
add_field("[% f | js %]", "[% checker.except_fields.$f | js %]");
[% END %]
[% ELSE %]
add_field();

View File

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