Move custis-specific code to/from extension
Move to core: * Mail log * Bugzilla::Checker * MediaWiki link parsing * Filtering HTML part of incoming email * Adding comment to a cloned bug Move to extension: * scrum_cards tablehinted-selects
parent
adf0ce5b0b
commit
22a6a0e369
|
@ -45,6 +45,7 @@ use Bugzilla::Diff;
|
|||
|
||||
use Date::Parse;
|
||||
use Date::Format;
|
||||
use POSIX;
|
||||
|
||||
use constant FORMAT_TRIPLE => "%19s|%-28s|%-28s";
|
||||
use constant FORMAT_3_SIZE => [19,28,28];
|
||||
|
@ -649,6 +650,7 @@ sub sendMail
|
|||
$template->process($tmpl, $vars, \$msg) || ThrowTemplateError($template->error());
|
||||
Bugzilla->template_inner("");
|
||||
|
||||
logMail($vars);
|
||||
MessageToMTA($msg);
|
||||
|
||||
Bugzilla::Hook::process('bugmail-post_send', { tmpl => \$tmpl, vars => $vars });
|
||||
|
@ -656,4 +658,27 @@ sub sendMail
|
|||
return 1;
|
||||
}
|
||||
|
||||
# Log all messages with comment and diff count to data/maillog
|
||||
sub logMail
|
||||
{
|
||||
my ($vars) = @_;
|
||||
my $datadir = bz_locations()->{datadir};
|
||||
my $fd;
|
||||
if (-w "$datadir/maillog" && open $fd, ">>$datadir/maillog")
|
||||
{
|
||||
my $s = [ POSIX::strftime("%Y-%m-%d %H:%M:%S: ", localtime) . ($vars->{isnew} ? "" : "Re: ") . "Bug #$vars->{bugid} mail to $vars->{to}" ];
|
||||
if ($vars->{new_comments} && @{$vars->{new_comments}})
|
||||
{
|
||||
push @$s, scalar(@{$vars->{new_comments}}) . ' comment(s) (#' . (join ',', map { $_->{count} } @{$vars->{new_comments}}) . ')';
|
||||
}
|
||||
if ($vars->{diffarray} && @{$vars->{diffarray}})
|
||||
{
|
||||
push @$s, scalar(grep { $_->{type} eq 'change' } @{$vars->{diffarray}}) . ' diffs';
|
||||
}
|
||||
$s = join "; ", @$s;
|
||||
print $fd $s, "\n";
|
||||
close $fd;
|
||||
}
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
#!/usr/bin/perl
|
||||
# Bug predicate / "checker" object
|
||||
# License: Dual-license GPL 3.0+ or MPL 1.1+
|
||||
# Contributor(s): Vitaliy Filippov <vitalif@mail.ru>
|
||||
|
||||
package Bugzilla::Checker;
|
||||
|
||||
|
@ -14,17 +17,18 @@ use Bugzilla::Util;
|
|||
use constant DB_TABLE => 'checkers';
|
||||
|
||||
use constant {
|
||||
# Да => проверка старого состояния бага ("заморозка")
|
||||
# Нет => проверка нового состояния бага ("проверка новых значений")
|
||||
# Yes => check old state of the bug ("freezer")
|
||||
# No => check new state of the bug ("checker")
|
||||
CF_FREEZE => 0x01,
|
||||
# Да => ошибка, нет => предупреждение
|
||||
# Yes => throw an error, no => give a warning
|
||||
CF_FATAL => 0x02,
|
||||
# Да => проверять при создании бага, нет => не проверять
|
||||
# Yes <=> check new bugs
|
||||
CF_CREATE => 0x04,
|
||||
# Да => проверять при обновлении бага, нет => не проверять
|
||||
# Yes <=> check updates
|
||||
CF_UPDATE => 0x08,
|
||||
# Да => запрещать изменения всех полей, кроме except_fields
|
||||
# Нет => разрешать изменения всех полей, кроме except_fields
|
||||
# Yes => forbid to change everything except except_fields
|
||||
# No => allow to change everything except except_fields
|
||||
# except_fields are empty => CF_DENY added automatically
|
||||
CF_DENY => 0x10,
|
||||
};
|
||||
|
||||
|
@ -32,22 +36,13 @@ our @EXPORT = qw(CF_FREEZE CF_FATAL CF_CREATE CF_UPDATE CF_DENY);
|
|||
|
||||
use constant DB_COLUMNS => (
|
||||
'id',
|
||||
# <Это состояние> задаётся соответствием запросу поиска.
|
||||
'query_id',
|
||||
# Кто создал
|
||||
'user_id',
|
||||
# Флаги
|
||||
'flags',
|
||||
# Сообщение об ошибке в случае некорректности
|
||||
'message',
|
||||
# SQL-код генерировать при каждом изменении бага долго, поэтому кэшируем
|
||||
'sql_code',
|
||||
# Поля-исключения: если CF_DENY, то разрешить только их,
|
||||
# если !(flags & CF_DENY), то запретить только их,
|
||||
# если !(flags & CF_DENY) и их нет, то flags |= CF_DENY
|
||||
'except_fields',
|
||||
# Триггеры - действия над полями багов (требует CF_FREEZE и !CF_FATAL)
|
||||
'triggers',
|
||||
'query_id', # "Bad state" is described by this search query
|
||||
'user_id', # Creator
|
||||
'flags', # Bit field of CF_* flags
|
||||
'message', # Error message text
|
||||
'sql_code', # SQL code for query is cached here
|
||||
'except_fields', # "Exception" fields - see CF_DENY above.
|
||||
'triggers', # Triggers (bug changes) (requires CF_FREEZE & !CF_FATAL)
|
||||
);
|
||||
use constant NAME_FIELD => 'message';
|
||||
use constant ID_FIELD => 'id';
|
||||
|
@ -69,12 +64,10 @@ use constant UPDATE_COLUMNS => (
|
|||
'triggers',
|
||||
);
|
||||
|
||||
# Перепостроение и перекэширование SQL-запроса в базу
|
||||
# от имени суперпользователя (без проверок групп).
|
||||
# На всякий случай из запроса убирается ORDER BY и SELECT ... FROM,
|
||||
# а потом при исполнении приписывается.
|
||||
# Вообще проверка работает дёрганьем SQL-запроса с добавленным условием
|
||||
# на bugs.bug_id=...
|
||||
# The check works by executing this SQL query with added bugs.bug_id=? condition.
|
||||
# Rebuild and save SQL code in the DB, from under the superuser
|
||||
# (without permission checks). ORDER BY and SELECT ... FROM are removed
|
||||
# and then added for more security.
|
||||
sub refresh_sql
|
||||
{
|
||||
my $self = shift;
|
||||
|
@ -97,7 +90,7 @@ sub refresh_sql
|
|||
$self->set_sql_code($sql);
|
||||
}
|
||||
|
||||
# Создание нового предиката - сразу кэшируется SQL-код
|
||||
# Create a predicate, generating SQL code for it
|
||||
sub create
|
||||
{
|
||||
my ($class, $params) = @_;
|
||||
|
@ -115,7 +108,7 @@ sub create
|
|||
return $self;
|
||||
}
|
||||
|
||||
# Обновление - всегда перекэшируется SQL-код
|
||||
# Update a predicate, regenerating SQL code for it
|
||||
sub update
|
||||
{
|
||||
my $self = shift;
|
||||
|
@ -128,20 +121,19 @@ sub update
|
|||
$self->SUPER::update(@_);
|
||||
}
|
||||
|
||||
# Проверяем, что такой поиск существует и доступен пользователю
|
||||
# Check this named query exists and is accessible to the user
|
||||
sub _check_query_id
|
||||
{
|
||||
my ($invocant, $value, $field) = @_;
|
||||
my $q = Bugzilla::Search::Saved->check({ id => $value });
|
||||
# Потенциально мы разрешаем создавать предикаты
|
||||
# на основе расшаренных другими людьми поисков,
|
||||
# но в интерфейсе этого сейчас нет
|
||||
# This code allows to create predicates using searches shared by other users,
|
||||
# but the UI doesn't allow it (yet?).
|
||||
if ($q->user->id != Bugzilla->user->id &&
|
||||
(!$q->shared_with_group || !Bugzilla->user->in_group($q->shared_with_group)))
|
||||
{
|
||||
ThrowUserError('query_access_denied', { query => $q });
|
||||
}
|
||||
# Тоже наша доработка - в сохранённый поиск может быть сохранён просто левый URL
|
||||
# Check if a named query is not a search query, but just an HTTP url
|
||||
if ($q->query =~ /^[a-z][a-z0-9]*:/iso)
|
||||
{
|
||||
ThrowUserError('query_not_savedsearch', { query => $q });
|
||||
|
@ -163,7 +155,7 @@ sub message { $_[0]->{message} }
|
|||
sub sql_code { $_[0]->{sql_code} }
|
||||
sub flags { $_[0]->{flags} }
|
||||
|
||||
# Отдельные флаги
|
||||
# Specific flags from the bitfield
|
||||
sub is_freeze { $_[0]->{flags} & CF_FREEZE }
|
||||
sub is_fatal { ($_[0]->{flags} & CF_FATAL) && !$_[0]->triggers }
|
||||
sub on_create { $_[0]->{flags} & CF_CREATE }
|
||||
|
@ -171,8 +163,7 @@ sub on_update { $_[0]->{flags} & CF_UPDATE }
|
|||
sub deny_all { $_[0]->{flags} & CF_DENY }
|
||||
|
||||
# { field_name => value }
|
||||
# Исключать изменения поля field_name на значение value,
|
||||
# либо на любое значение, если value = undef
|
||||
# Make an exception for change of field_name to 'value', or to any value if value is undef
|
||||
sub except_fields
|
||||
{
|
||||
my $self = shift;
|
||||
|
@ -184,10 +175,9 @@ sub except_fields
|
|||
}
|
||||
|
||||
# { field_name => value }
|
||||
# Изменить значение поля field_name на value. Для полей с множествами значений
|
||||
# field_name также может быть add_<field_name> или remove_<field_name>, что означает
|
||||
# добавить значение или удалить значение соответственно.
|
||||
# FIXME Пока поддерживается только add_cc.
|
||||
# Change field 'field_name' to 'value'. For multivalued fields field_name may also
|
||||
# by 'add_<field_name>' or 'remove_<field_name>', which means add or remove something.
|
||||
# FIXME Now the only function supported is 'add_cc'
|
||||
sub triggers
|
||||
{
|
||||
my $self = shift;
|
||||
|
@ -224,11 +214,11 @@ sub user
|
|||
return $self->{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_flags { $_[0]->set('flags', $_[1]) }
|
||||
sub set_message { $_[0]->set('message', $_[1]) }
|
||||
sub set_sql_code { $_[0]->set('sql_code', $_[1]) }
|
||||
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_flags { $_[0]->set('flags', $_[1]) }
|
||||
sub set_message { $_[0]->set('message', $_[1]) }
|
||||
sub set_sql_code { $_[0]->set('sql_code', $_[1]) }
|
||||
|
||||
sub set_except_fields
|
||||
{
|
|
@ -822,19 +822,6 @@ use constant ABSTRACT_SCHEMA => {
|
|||
],
|
||||
},
|
||||
|
||||
# Additional estimates for SCRUM cards printed from Bugzilla (CustIS Bug 45485)
|
||||
scrum_cards => {
|
||||
FIELDS => [
|
||||
bug_id => {TYPE => 'INT4', NOTNULL => 1},
|
||||
sprint => {TYPE => 'varchar(255)', NOTNULL => 1},
|
||||
type => {TYPE => 'varchar(255)', NOTNULL => 1},
|
||||
estimate => {TYPE => 'varchar(255)', NOTNULL => 1},
|
||||
],
|
||||
INDEXES => [
|
||||
scrum_cards_primary_idx => { FIELDS => ['bug_id', 'sprint', 'type'], TYPE => 'UNIQUE' },
|
||||
],
|
||||
},
|
||||
|
||||
namedqueries_link_in_footer => {
|
||||
FIELDS => [
|
||||
namedquery_id => {TYPE => 'INT4', NOTNULL => 1, REFERENCES => {TABLE => 'namedqueries', COLUMN => 'id', DELETE => 'CASCADE'}},
|
||||
|
|
|
@ -284,6 +284,12 @@ sub quoteUrls {
|
|||
# initialize custom protocols
|
||||
$custom_proto_cached = time;
|
||||
$custom_proto = {};
|
||||
# MediaWiki link integration
|
||||
for (split /\n/, Bugzilla->params->{mediawiki_urls})
|
||||
{
|
||||
my ($wiki, $url) = split /\s+/, trim($_), 2;
|
||||
$custom_proto->{$wiki} = sub { process_wiki_url($url, @_) } if $wiki && $url;
|
||||
}
|
||||
Bugzilla::Hook::process('quote_urls-custom_proto', { custom_proto => $custom_proto });
|
||||
$custom_proto_regex = join '|', keys %$custom_proto;
|
||||
}
|
||||
|
@ -374,6 +380,29 @@ sub quoteUrls {
|
|||
return $text;
|
||||
}
|
||||
|
||||
# MediaWiki page anchor encoding
|
||||
sub process_wiki_anchor
|
||||
{
|
||||
my ($anchor) = (@_);
|
||||
return "" unless $anchor;
|
||||
$anchor =~ tr/ /_/;
|
||||
$anchor = url_quote($anchor);
|
||||
$anchor =~ s/\%3A/:/giso;
|
||||
$anchor =~ tr/%/./;
|
||||
return $anchor;
|
||||
}
|
||||
|
||||
# Convert MediaWiki page titles to URLs
|
||||
sub process_wiki_url
|
||||
{
|
||||
my ($base, $url, $anchor) = @_;
|
||||
$url = trim($url);
|
||||
$url =~ s/\s+/_/gso;
|
||||
# Use url_quote without converting / to %2F
|
||||
$url = url_quote_noslash($url);
|
||||
return $base . $url . '#' . process_wiki_anchor($anchor);
|
||||
}
|
||||
|
||||
# Creates a link to an attachment, including its title.
|
||||
sub get_attachment_link {
|
||||
my ($attachid, $link_text, $comment) = @_;
|
||||
|
|
18
email_in.pl
18
email_in.pl
|
@ -55,7 +55,6 @@ use Bugzilla::Mailer;
|
|||
use Bugzilla::Token;
|
||||
use Bugzilla::User;
|
||||
use Bugzilla::Util;
|
||||
use Bugzilla::Hook;
|
||||
|
||||
#############
|
||||
# Constants #
|
||||
|
@ -79,8 +78,7 @@ sub parse_mail
|
|||
$input_email = Email::MIME->new($mail_text);
|
||||
|
||||
my %fields;
|
||||
Bugzilla::Hook::process('email_in_before_parse', { mail => $input_email,
|
||||
fields => \%fields });
|
||||
Bugzilla::Hook::process('email_in_before_parse', { mail => $input_email, fields => \%fields });
|
||||
# RFC 3834 - Recommendations for Automatic Responses to Electronic Mail
|
||||
# Automatic responses SHOULD NOT be issued in response to any
|
||||
# message which contains an Auto-Submitted header field (see below),
|
||||
|
@ -126,9 +124,10 @@ sub parse_mail
|
|||
debug_print("Body:\n" . $body, 3);
|
||||
|
||||
$body = remove_leading_blank_lines($body);
|
||||
Bugzilla::Hook::process("emailin-filter_body", { body => \$body });
|
||||
my @body_lines = split(/\r?\n/s, $body);
|
||||
|
||||
Bugzilla::Hook::process("emailin-filter_body", { body => \$body });
|
||||
|
||||
my @body_lines = split(/\r?\n/s, $body);
|
||||
my $fields_by_name = { map { (lc($_->description) => $_->name, lc($_->name) => $_->name) } Bugzilla->get_fields({ obsolete => 0 }) };
|
||||
|
||||
# If there are fields specified.
|
||||
|
@ -321,6 +320,13 @@ sub get_body_and_attachments
|
|||
return ($body, $attachments);
|
||||
}
|
||||
|
||||
sub rm_line_feeds
|
||||
{
|
||||
my ($t) = @_;
|
||||
$t =~ s/[\n\r]+/ /giso;
|
||||
return $t;
|
||||
}
|
||||
|
||||
sub get_text_alternative
|
||||
{
|
||||
my ($email) = @_;
|
||||
|
@ -345,6 +351,8 @@ sub get_text_alternative
|
|||
elsif ($ct =~ /^text\/html/i)
|
||||
{
|
||||
$body = $part->body;
|
||||
$body =~ s/<table[^<>]*class=[\"\']?difft[^<>]*>.*?<\/table\s*>//giso;
|
||||
$body =~ s/(<a[^<>]*>.*?<\/a\s*>)/rm_line_feeds($1)/gieso;
|
||||
Bugzilla::Hook::process("emailin-filter_html", { body => \$body });
|
||||
$body = HTML::Strip->new->parse($body);
|
||||
}
|
||||
|
|
|
@ -18,11 +18,6 @@ required_modules('custis', $REQUIRED_MODULES);
|
|||
optional_modules('custis', $OPTIONAL_MODULES);
|
||||
clear_hooks('custis');
|
||||
|
||||
# Email-related hooks
|
||||
set_hook('custis', 'bugmail_pre_template', 'CustisMailHooks::bugmail_pre_template');
|
||||
set_hook('custis', 'emailin_filter_body', 'CustisMailHooks::emailin_filter_body');
|
||||
set_hook('custis', 'emailin_filter_html', 'CustisMailHooks::emailin_filter_html');
|
||||
|
||||
# Hooks allowing to create MySQL Views representing saved searches for users
|
||||
if (!Bugzilla->params->{ext_disable_refresh_views})
|
||||
{
|
||||
|
@ -47,9 +42,9 @@ add_hook('custis', 'install_before_final_checks', 'Checkers::install_before_fi
|
|||
# Other hooks
|
||||
set_hook('custis', 'flag_check_requestee_list', 'CustisMiscHooks::flag_check_requestee_list');
|
||||
set_hook('custis', 'process_bug_after_move', 'CustisMiscHooks::process_bug_after_move');
|
||||
set_hook('custis', 'quote_urls_custom_proto', 'CustisMiscHooks::quote_urls_custom_proto');
|
||||
set_hook('custis', 'enter_bug_cloned_bug', 'CustisMiscHooks::enter_bug_cloned_bug');
|
||||
add_hook('custis', 'bug_end_of_create', 'CustisMiscHooks::bug_end_of_create');
|
||||
set_hook('custis', 'post_bug_cloned_bug', 'CustisMiscHooks::post_bug_cloned_bug');
|
||||
set_hook('custis', 'emailin_filter_body', 'CustisMiscHooks::emailin_filter_body');
|
||||
|
||||
1;
|
||||
__END__
|
||||
|
|
|
@ -1,84 +0,0 @@
|
|||
#!/usr/bin/perl
|
||||
# Various email hooks
|
||||
|
||||
package CustisMailHooks;
|
||||
|
||||
use strict;
|
||||
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::Util;
|
||||
use POSIX qw(strftime);
|
||||
|
||||
# Log all messages with comment and diff count to data/maillog
|
||||
sub bugmail_pre_template
|
||||
{
|
||||
my ($args) = @_;
|
||||
|
||||
my $vars = $args->{vars};
|
||||
my $datadir = bz_locations()->{datadir};
|
||||
my $fd;
|
||||
if (-w "$datadir/maillog" && open $fd, ">>$datadir/maillog")
|
||||
{
|
||||
my $s = [ strftime("%Y-%m-%d %H:%M:%S: ", localtime) . ($vars->{isnew} ? "" : "Re: ") . "Bug #$vars->{bugid} mail to $vars->{to}" ];
|
||||
if ($vars->{new_comments} && @{$vars->{new_comments}})
|
||||
{
|
||||
push @$s, scalar(@{$vars->{new_comments}}) . ' comment(s) (#' . (join ',', map { $_->{count} } @{$vars->{new_comments}}) . ')';
|
||||
}
|
||||
if ($vars->{diffarray} && @{$vars->{diffarray}})
|
||||
{
|
||||
push @$s, scalar(grep { $_->{type} eq 'change' } @{$vars->{diffarray}}) . ' diffs';
|
||||
}
|
||||
$s = join "; ", @$s;
|
||||
print $fd $s, "\n";
|
||||
close $fd;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
##
|
||||
## Handling incoming email:
|
||||
##
|
||||
|
||||
sub emailin_filter_body
|
||||
{
|
||||
my ($args) = @_;
|
||||
|
||||
for (${$args->{body}})
|
||||
{
|
||||
if (/From:\s+bugzilla-daemon(\s*[a-z0-9_\-]+\s*:.*?\n)*\s*Bug\s*\d+<[^>]*>\s*\([^\)]*\)\s*/iso)
|
||||
{
|
||||
my ($pr, $ps) = ($`, $');
|
||||
$ps =~ s/\n+(\r*\n+)+/\n/giso;
|
||||
$_ = $pr . $ps;
|
||||
s!from\s+.*?<http://plantime[^>]*search=([^>]*)>!from $1!giso;
|
||||
s!((Comment|Bug)\s+\#?\d+)<[^<>]*>!$1!giso;
|
||||
s!\n[^\n]*<http://plantime[^>]*search=[^>]*>\s+changed:[ \t\r]*\n.*?$!!iso;
|
||||
s/\s*\n--\s*Configure\s*bugmail<[^>]*>(([ \t\r]*\n[^\n]*)*)//iso;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub emailin_filter_html
|
||||
{
|
||||
my ($args) = @_;
|
||||
|
||||
for (${$args->{body}})
|
||||
{
|
||||
s/<table[^<>]*class=[\"\']?difft[^<>]*>.*?<\/table\s*>//giso;
|
||||
s/<a[^<>]*>.*?<\/a\s*>/_custis_rmlf($&)/gieso;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub _custis_rmlf
|
||||
{
|
||||
my ($t) = @_;
|
||||
$t =~ s/[\n\r]+/ /giso;
|
||||
return $t;
|
||||
}
|
||||
|
||||
1;
|
||||
__END__
|
|
@ -3,8 +3,6 @@
|
|||
# - Expand "group" users in flag requestee
|
||||
# - Remember about nonanswered flag requests
|
||||
# - Automatic settings of cf_extbug
|
||||
# - Add a comment to cloned bug
|
||||
# - Expand MediaWiki urls
|
||||
|
||||
package CustisMiscHooks;
|
||||
|
||||
|
@ -15,7 +13,7 @@ use Bugzilla::Util;
|
|||
use Bugzilla::Constants;
|
||||
use Bugzilla::Error;
|
||||
|
||||
# Expand "group" users in flag requestee
|
||||
# Expand CUSTIS-specific "group" users in flag requestee
|
||||
sub flag_check_requestee_list
|
||||
{
|
||||
my ($args) = @_;
|
||||
|
@ -37,20 +35,20 @@ sub process_bug_after_move
|
|||
{
|
||||
my ($args) = @_;
|
||||
|
||||
my $cgi = Bugzilla->cgi;
|
||||
my $ARGS = Bugzilla->input_params;
|
||||
my $bug_objects = $args->{bug_objects};
|
||||
my $vars = $args->{vars};
|
||||
|
||||
my $single = @$bug_objects == 1;
|
||||
my $clear_on_close =
|
||||
$cgi->param('bug_status') eq 'CLOSED' &&
|
||||
$ARGS->{bug_status} eq 'CLOSED' &&
|
||||
Bugzilla->user->settings->{clear_requests_on_close}->{value} eq 'on';
|
||||
my $verify_flags = $single &&
|
||||
Bugzilla->usage_mode != USAGE_MODE_EMAIL &&
|
||||
Bugzilla->user->wants_request_reminder;
|
||||
my $reset_own_flags = $verify_flags && $cgi->param('comment') !~ /^\s*$/so;
|
||||
my $reset_own_flags = $verify_flags && $ARGS->{comment} !~ /^\s*$/so;
|
||||
|
||||
if (($clear_on_close || $reset_own_flags) && !$cgi->param('force_flags'))
|
||||
if (($clear_on_close || $reset_own_flags) && !$ARGS->{force_flags})
|
||||
{
|
||||
my $flags;
|
||||
my @requery_flags;
|
||||
|
@ -58,18 +56,17 @@ sub process_bug_after_move
|
|||
my $login;
|
||||
# 1) Check flag requests and remind user about resetting his own incoming requests.
|
||||
# 2) When closing bugs, clear all flag requests (CustIS Bug 68430).
|
||||
# Not used in mass update and email modes.
|
||||
for my $bug (@$bug_objects)
|
||||
{
|
||||
if ($single)
|
||||
{
|
||||
for ($cgi->param())
|
||||
for (keys %$ARGS)
|
||||
{
|
||||
if (/^(flag-(\d+))$/)
|
||||
{
|
||||
$flag = Bugzilla::Flag->new({ id => $2 });
|
||||
$flag->{status} = $cgi->param($1);
|
||||
if (($login = trim($cgi->param("requestee-".$flag->{id}))) &&
|
||||
$flag->{status} = $ARGS->{$_};
|
||||
if (($login = trim($ARGS->{"requestee-".$flag->{id}})) &&
|
||||
($login = login_to_id($login)))
|
||||
{
|
||||
$flag->{requestee_id} = $login;
|
||||
|
@ -94,10 +91,11 @@ sub process_bug_after_move
|
|||
if ($verify_flags)
|
||||
{
|
||||
push @requery_flags, $flag;
|
||||
delete $ARGS->{'flag-'.$flag->{id}};
|
||||
}
|
||||
elsif ($single)
|
||||
{
|
||||
$cgi->param('flag-'.$flag->{id} => 'X');
|
||||
$ARGS->{'flag-'.$flag->{id}} = 'X';
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -108,7 +106,6 @@ sub process_bug_after_move
|
|||
if ($verify_flags && @requery_flags)
|
||||
{
|
||||
push @{$vars->{verify_flags}}, @requery_flags;
|
||||
$vars->{field_filter} = '^('.join('|', map { "flag-".$_->id } @{$vars->{verify_flags}}).')$';
|
||||
Bugzilla->template->process("bug/process/verify-flags.html.tmpl", $vars)
|
||||
|| ThrowTemplateError(Bugzilla->template->error());
|
||||
exit;
|
||||
|
@ -134,78 +131,38 @@ sub enter_bug_cloned_bug
|
|||
return 1;
|
||||
}
|
||||
|
||||
# Bug 53590 - add a comment to cloned bug
|
||||
# Bug 69514 - automatic setting of cf_extbug during clone to external product
|
||||
sub bug_end_of_create
|
||||
# Bug 69514 - Automatic setting of cf_extbug during clone to external product
|
||||
sub post_bug_cloned_bug
|
||||
{
|
||||
my ($args) = @_;
|
||||
my $cloned_bug_id = scalar Bugzilla->cgi->param('cloned_bug_id');
|
||||
my $cloned_comment = scalar Bugzilla->cgi->param('cloned_comment');
|
||||
my $bug = $args->{bug};
|
||||
if ($cloned_bug_id)
|
||||
if (($args->{cloned_bug}->product_obj->extproduct || 0) == $args->{bug}->product_id &&
|
||||
!$args->{cloned_bug}->{cf_extbug})
|
||||
{
|
||||
my $cmt = "Bug ".$bug->id." was cloned from ";
|
||||
if ($cloned_comment)
|
||||
{
|
||||
detaint_natural($cloned_comment);
|
||||
$cmt .= 'comment ';
|
||||
$cmt .= $cloned_comment;
|
||||
}
|
||||
else
|
||||
{
|
||||
$cmt .= 'this bug';
|
||||
}
|
||||
detaint_natural($cloned_bug_id);
|
||||
my $cloned_bug = Bugzilla::Bug->check($cloned_bug_id);
|
||||
$cloned_bug->add_comment($cmt);
|
||||
if (($cloned_bug->product_obj->extproduct || 0) == $bug->product_id &&
|
||||
!$cloned_bug->{cf_extbug})
|
||||
{
|
||||
$cloned_bug->{cf_extbug} = $bug->id;
|
||||
}
|
||||
$cloned_bug->update($bug->creation_ts);
|
||||
$args->{cloned_bug}->{cf_extbug} = $args->{bug}->id;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
# MediaWiki link integration
|
||||
sub quote_urls_custom_proto
|
||||
# Filter text body of input messages to remove Outlook link URLs.
|
||||
sub emailin_filter_body
|
||||
{
|
||||
my ($args) = @_;
|
||||
for (split /\n/, Bugzilla->params->{mediawiki_urls})
|
||||
|
||||
for (${$args->{body}})
|
||||
{
|
||||
my ($wiki, $url) = split /\s+/, trim($_), 2;
|
||||
$args->{custom_proto}->{$wiki} = sub { process_wiki_url($url, @_) } if $wiki && $url;
|
||||
if (/From:\s+bugzilla-daemon(\s*[a-z0-9_\-]+\s*:.*?\n)*\s*Bug\s*\d+<[^>]*>\s*\([^\)]*\)\s*/iso)
|
||||
{
|
||||
my ($pr, $ps) = ($`, $');
|
||||
$ps =~ s/\n+(\r*\n+)+/\n/giso;
|
||||
$_ = $pr . $ps;
|
||||
s!from\s+.*?<http://plantime[^>]*search=([^>]*)>!from $1!giso;
|
||||
s!((Comment|Bug)\s+\#?\d+)<[^<>]*>!$1!giso;
|
||||
s!\n[^\n]*<http://plantime[^>]*search=[^>]*>\s+changed:[ \t\r]*\n.*?$!!iso;
|
||||
s/\s*\n--\s*Configure\s*bugmail<[^>]*>(([ \t\r]*\n[^\n]*)*)//iso;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
##
|
||||
## NON-HOOK FUNCTIONS
|
||||
##
|
||||
|
||||
# MediaWiki page anchor encoding
|
||||
sub process_wiki_anchor
|
||||
{
|
||||
my ($anchor) = (@_);
|
||||
return "" unless $anchor;
|
||||
$anchor =~ tr/ /_/;
|
||||
$anchor = url_quote($anchor);
|
||||
$anchor =~ s/\%3A/:/giso;
|
||||
$anchor =~ tr/%/./;
|
||||
return $anchor;
|
||||
}
|
||||
|
||||
# Convert MediaWiki page titles to URLs
|
||||
sub process_wiki_url
|
||||
{
|
||||
my ($base, $url, $anchor) = @_;
|
||||
$url = trim($url);
|
||||
$url =~ s/\s+/_/gso;
|
||||
# Use url_quote without converting / to %2F
|
||||
$url = url_quote_noslash($url);
|
||||
return $base . $url . '#' . process_wiki_anchor($anchor);
|
||||
}
|
||||
|
||||
1;
|
||||
__END__
|
||||
|
|
12
post_bug.cgi
12
post_bug.cgi
|
@ -173,6 +173,18 @@ $bug->set_flags($flags, $new_flags);
|
|||
# Save bug
|
||||
$bug->update;
|
||||
|
||||
if ($ARGS->{cloned_bug_id})
|
||||
{
|
||||
# Add a comment to cloned bug
|
||||
my $cmt = "Bug ".$bug->id." was cloned from ".
|
||||
($ARGS->{cloned_comment} =~ /(\d+)/ ? "comment $1" : 'this bug');
|
||||
detaint_natural($cloned_bug_id);
|
||||
my $cloned_bug = Bugzilla::Bug->check($ARGS->{cloned_bug_id});
|
||||
$cloned_bug->add_comment($cmt);
|
||||
Bugzilla::Hook::process('post_bug_cloned_bug', { bug => $bug, cloned_bug => $cloned_bug });
|
||||
$cloned_bug->update($bug->creation_ts);
|
||||
}
|
||||
|
||||
# Get the bug ID back
|
||||
my $id = $bug->bug_id;
|
||||
|
||||
|
|
|
@ -2,16 +2,16 @@
|
|||
# License: Dual-license MPL 1.1+ or GPL 3.0+
|
||||
# Author(s): Vitaliy Filippov, Stas Fomin %]
|
||||
|
||||
[% cgi = Bugzilla.cgi %]
|
||||
|
||||
[% PROCESS global/header.html.tmpl title='Verify flag requests' %]
|
||||
|
||||
[% ARGS = Bugzilla.input_params %]
|
||||
|
||||
<h3>Please, verify flags:</h3>
|
||||
|
||||
<form action="process_bug.cgi" method="post" enctype="multipart/form-data">
|
||||
|
||||
<input type="hidden" name="force_flags" value="1" />
|
||||
[% PROCESS "global/hidden-fields.html.tmpl" exclude=field_filter %]
|
||||
[% PROCESS "global/hidden-fields.html.tmpl" %]
|
||||
|
||||
<table cellspacing="0" cellpadding="4" style="border-width: 1px 1px 0 1px; border-style: solid; border-color: gray">
|
||||
<tr style="background-color: #e0e0e0">
|
||||
|
@ -29,7 +29,7 @@
|
|||
"[% flag.type.description %]"
|
||||
</td>
|
||||
<td style="border-width: 0 0 1px 0; border-style: solid; border-color: gray">
|
||||
[% cgi.param("requestee-$flag.id" ) %]
|
||||
[% ARGS.${"requestee-$flag.id"}.join(', ') %]
|
||||
</td>
|
||||
<td style="border-width: 0 0 1px 0; border-style: solid; border-color: gray">
|
||||
<select id="flag-[% flag.id %]" name="flag-[% flag.id %]">
|
||||
|
|
Loading…
Reference in New Issue