From 694df9c1c6ec244c416c2d8e85657651b6a28dab Mon Sep 17 00:00:00 2001 From: Dylan William Hardison Date: Mon, 9 Apr 2018 22:51:08 -0400 Subject: [PATCH 001/119] Bug 1450679 - Replace custom Sentry integration with Logging --- Bugzilla.pm | 6 +- Bugzilla/Config/Advanced.pm | 6 - Bugzilla/Constants.pm | 2 - Bugzilla/Error.pm | 111 +++--- Bugzilla/Install/Filesystem.pm | 7 - Bugzilla/Logging.pm | 8 +- Bugzilla/Sentry.pm | 374 ------------------ Log/Log4perl/Layout/Mozilla.pm | 5 +- conf/log4perl-test.conf | 2 + scripts/nagios_blocker_checker.pl | 4 +- sentry.pl | 94 ----- .../default/admin/params/advanced.html.tmpl | 5 - .../en/default/global/code-error.html.tmpl | 10 - 13 files changed, 66 insertions(+), 568 deletions(-) delete mode 100644 Bugzilla/Sentry.pm delete mode 100755 sentry.pl diff --git a/Bugzilla.pm b/Bugzilla.pm index 1188102d3d..a8b164b573 100644 --- a/Bugzilla.pm +++ b/Bugzilla.pm @@ -100,7 +100,7 @@ sub init_page { } if (i_am_cgi()) { - Log::Log4perl::MDC->put(remote_ip => remote_ip()); + Bugzilla::Logging->fields->{remote_ip} = remote_ip(); } if (${^TAINT}) { @@ -386,8 +386,8 @@ sub login { my $authenticated_user = $authorizer->login($type); - if (i_am_cgi()) { - Log::Log4perl::MDC->put(user_id => $authenticated_user->id); + if (i_am_cgi() && $authenticated_user->id) { + Bugzilla::Logging->fields->{user_id} = $authenticated_user->id; } # At this point, we now know if a real person is logged in. diff --git a/Bugzilla/Config/Advanced.pm b/Bugzilla/Config/Advanced.pm index 2eec11dbe1..9316d8e48c 100644 --- a/Bugzilla/Config/Advanced.pm +++ b/Bugzilla/Config/Advanced.pm @@ -37,12 +37,6 @@ use constant get_param_list => ( default => 0 }, - { - name => 'sentry_uri', - type => 't', - default => '', - }, - { name => 'metrics_enabled', type => 'b', diff --git a/Bugzilla/Constants.pm b/Bugzilla/Constants.pm index 6e3a127362..80d9c4e0c2 100644 --- a/Bugzilla/Constants.pm +++ b/Bugzilla/Constants.pm @@ -694,8 +694,6 @@ sub _bz_locations { 'webdotdir' => "$datadir/webdot", 'extensionsdir' => "$libpath/extensions", 'assetsdir' => "$datadir/assets", - # error_reports store error/warnings destined for sentry - 'error_reports' => "$libpath/error_reports", 'confdir' => $confdir, }; } diff --git a/Bugzilla/Error.pm b/Bugzilla/Error.pm index d675718489..ef57303e98 100644 --- a/Bugzilla/Error.pm +++ b/Bugzilla/Error.pm @@ -13,9 +13,10 @@ use warnings; use base qw(Exporter); -@Bugzilla::Error::EXPORT = qw(ThrowCodeError ThrowTemplateError ThrowUserError ThrowErrorPage); +## no critic (Modules::ProhibitAutomaticExportation) +our @EXPORT = qw( ThrowCodeError ThrowTemplateError ThrowUserError ThrowErrorPage); +## use critic -use Bugzilla::Sentry; use Bugzilla::Constants; use Bugzilla::WebService::Constants; use Bugzilla::Util; @@ -37,7 +38,7 @@ sub _in_eval { } sub _throw_error { - my ($name, $error, $vars) = @_; + my ($name, $error, $vars, $logfunc) = @_; $vars ||= {}; $vars->{error} = $error; @@ -47,38 +48,6 @@ sub _throw_error { my $dbh = eval { Bugzilla->dbh }; $dbh->bz_rollback_transaction() if ($dbh && $dbh->bz_in_transaction() && !_in_eval()); - my $datadir = bz_locations()->{'datadir'}; - # If a writable $datadir/errorlog exists, log error details there. - if (-w "$datadir/errorlog") { - require Data::Dumper; - my $mesg = ""; - for (1..75) { $mesg .= "-"; }; - $mesg .= "\n[$$] " . time2str("%D %H:%M:%S ", time()); - $mesg .= "$name $error "; - $mesg .= remote_ip(); - $mesg .= Bugzilla->user->login; - $mesg .= (' actually ' . Bugzilla->sudoer->login) if Bugzilla->sudoer; - $mesg .= "\n"; - my %params = Bugzilla->cgi->Vars; - $Data::Dumper::Useqq = 1; - for my $param (sort keys %params) { - my $val = $params{$param}; - # obscure passwords - $val = "*****" if $param =~ /password/i; - # limit line length - $val =~ s/^(.{512}).*$/$1\[CHOP\]/; - $mesg .= "[$$] " . Data::Dumper->Dump([$val],["param($param)"]); - } - for my $var (sort keys %ENV) { - my $val = $ENV{$var}; - $val = "*****" if $val =~ /password|http_pass/i; - $mesg .= "[$$] " . Data::Dumper->Dump([$val],["env($var)"]); - } - open(ERRORLOGFID, ">>", "$datadir/errorlog"); - print ERRORLOGFID "$mesg\n"; - close ERRORLOGFID; - } - my $template = Bugzilla->template; my $message; @@ -97,34 +66,24 @@ sub _throw_error { message => \$message }); if ($Bugzilla::Template::is_processing) { - $name =~ /^global\/(user|code)-error/; - my $type = $1 // 'unknown'; + my ($type) = $name =~ /^global\/(user|code)-error/; + $type //= 'unknown'; die Template::Exception->new("bugzilla.$type.$error", $vars); } if (Bugzilla->error_mode == ERROR_MODE_WEBPAGE) { - if (sentry_should_notify($vars->{error})) { - $vars->{maintainers_notified} = 1; - $vars->{processed} = {}; - } else { - $vars->{maintainers_notified} = 0; - } - my $cgi = Bugzilla->cgi; $cgi->close_standby_message('text/html', 'inline', 'error', 'html'); $template->process($name, $vars) || ThrowTemplateError($template->error()); print $cgi->multipart_final() if $cgi->{_multipart_in_progress}; - - if ($vars->{maintainers_notified}) { - sentry_handle_error($vars->{error}, $vars->{processed}->{error_message}); - } + $logfunc->("webpage error: $error"); } elsif (Bugzilla->error_mode == ERROR_MODE_TEST) { die Dumper($vars); } elsif (Bugzilla->error_mode == ERROR_MODE_DIE) { - die("$message\n"); + die "$message\n"; } elsif (Bugzilla->error_mode == ERROR_MODE_DIE_SOAP_FAULT || Bugzilla->error_mode == ERROR_MODE_JSON_RPC @@ -141,6 +100,7 @@ sub _throw_error { } if (Bugzilla->error_mode == ERROR_MODE_DIE_SOAP_FAULT) { + $logfunc->("XMLRPC error: $error ($code)"); die SOAP::Fault->faultcode($code)->faultstring($message); } else { @@ -150,6 +110,11 @@ sub _throw_error { if (Bugzilla->error_mode == ERROR_MODE_REST) { my %status_code_map = %{ REST_STATUS_CODE_MAP() }; $status_code = $status_code_map{$code} || $status_code_map{'_default'}; + $logfunc->("REST error: $error (HTTP $status_code, internal code $code)"); + } + else { + my $fake_code = 100000 + $code; + $logfunc->("JSONRPC error: $error ($fake_code)"); } # Technically JSON-RPC isn't allowed to have error numbers # higher than 999, but we do this to avoid conflicts with @@ -170,22 +135,44 @@ sub _throw_error { exit; } + +sub _add_vars_to_logging_fields { + my ($vars) = @_; + + foreach my $key (keys %$vars) { + Bugzilla::Logging->fields->{"var_$key"} = $vars->{$key}; + } +} + +sub _make_logfunc { + my ($type) = @_; + my $logger = Log::Log4perl->get_logger("Bugzilla.Error.$type"); + return sub { + local $Log::Log4perl::caller_depth = $Log::Log4perl::caller_depth + 3; + if ($type eq 'User') { + $logger->warn(@_); + } + else { + $logger->error(@_); + } + }; +} + + sub ThrowUserError { - _throw_error("global/user-error.html.tmpl", @_); + my ($error, $vars) = @_; + my $logfunc = _make_logfunc('User'); + _add_vars_to_logging_fields($vars); + + _throw_error( 'global/user-error.html.tmpl', $error, $vars, $logfunc); } sub ThrowCodeError { - my (undef, $vars) = @_; - - # Don't show function arguments, in case they contain - # confidential data. - local $Carp::MaxArgNums = -1; - # Don't show the error as coming from Bugzilla::Error, show it - # as coming from the caller. - local $Carp::CarpInternal{'Bugzilla::Error'} = 1; - $vars->{traceback} //= Carp::longmess(); + my ($error, $vars) = @_; + my $logfunc = _make_logfunc('User'); + _add_vars_to_logging_fields($vars); - _throw_error("global/code-error.html.tmpl", @_); + _throw_error( 'global/code-error.html.tmpl', $error, $vars, $logfunc ); } sub ThrowTemplateError { @@ -211,10 +198,12 @@ sub ThrowTemplateError { # we never want to display this to the user exit if $template_err =~ /\bModPerl::Util::exit\b/; + state $logger = Log::Log4perl->get_logger('Bugzilla.Error.Template'); + $logger->error($template_err); + $vars->{'template_error_msg'} = $template_err; $vars->{'error'} = "template_error"; - sentry_handle_error('error', $template_err); $vars->{'template_error_msg'} =~ s/ at \S+ line \d+\.\s*$//; my $template = Bugzilla->template; diff --git a/Bugzilla/Install/Filesystem.pm b/Bugzilla/Install/Filesystem.pm index 71169345b0..70b195090e 100644 --- a/Bugzilla/Install/Filesystem.pm +++ b/Bugzilla/Install/Filesystem.pm @@ -186,7 +186,6 @@ sub FILESYSTEM { my $template_cache = bz_locations()->{'template_cache'}; my $graphsdir = bz_locations()->{'graphsdir'}; my $assetsdir = bz_locations()->{'assetsdir'}; - my $error_reports = bz_locations()->{'error_reports'}; # We want to set the permissions the same for all localconfig files # across all PROJECTs, so we do something special with $localconfig, @@ -221,7 +220,6 @@ sub FILESYSTEM { 'runtests.pl' => { perms => OWNER_EXECUTE }, 'jobqueue.pl' => { perms => OWNER_EXECUTE }, 'migrate.pl' => { perms => OWNER_EXECUTE }, - 'sentry.pl' => { perms => WS_EXECUTE }, 'metrics.pl' => { perms => WS_EXECUTE }, 'Makefile.PL' => { perms => OWNER_EXECUTE }, 'gen-cpanfile.pl' => { perms => OWNER_EXECUTE }, @@ -271,8 +269,6 @@ sub FILESYSTEM { # Writeable directories $template_cache => { files => CGI_READ, dirs => DIR_CGI_OVERWRITE }, - $error_reports => { files => CGI_READ, - dirs => DIR_CGI_WRITE }, $attachdir => { files => CGI_WRITE, dirs => DIR_CGI_WRITE }, $webdotdir => { files => WS_SERVE, @@ -365,7 +361,6 @@ sub FILESYSTEM { $webdotdir => DIR_CGI_WRITE | DIR_ALSO_WS_SERVE, $assetsdir => DIR_CGI_WRITE | DIR_ALSO_WS_SERVE, $template_cache => DIR_CGI_WRITE, - $error_reports => DIR_CGI_WRITE, # Directories that contain content served directly by the web server. "$skinsdir/custom" => DIR_WS_SERVE, "$skinsdir/contrib" => DIR_WS_SERVE, @@ -466,8 +461,6 @@ sub FILESYSTEM { contents => HT_DEFAULT_DENY }, "$datadir/.htaccess" => { perms => WS_SERVE, contents => HT_DEFAULT_DENY }, - "$error_reports/.htaccess" => { perms => WS_SERVE, - contents => HT_DEFAULT_DENY }, "$graphsdir/.htaccess" => { perms => WS_SERVE, contents => HT_GRAPHS_DIR }, "$webdotdir/.htaccess" => { perms => WS_SERVE, diff --git a/Bugzilla/Logging.pm b/Bugzilla/Logging.pm index 769485c864..4a7abcb21e 100644 --- a/Bugzilla/Logging.pm +++ b/Bugzilla/Logging.pm @@ -10,7 +10,7 @@ use 5.10.1; use strict; use warnings; -use Log::Log4perl; +use Log::Log4perl qw(:easy); use Log::Log4perl::MDC; use File::Spec::Functions qw(rel2abs); use Bugzilla::Constants qw(bz_locations); @@ -20,11 +20,15 @@ sub is_interactive { return not exists $ENV{SERVER_SOFTWARE} } +sub fields { + return Log::Log4perl::MDC->get_context->{fields} //= {}; +} + BEGIN { my $file = $ENV{LOG4PERL_CONFIG_FILE} // 'log4perl-syslog.conf'; Log::Log4perl::Logger::create_custom_level('NOTICE', 'WARN', 5, 2); Log::Log4perl->init(rel2abs($file, bz_locations->{confdir})); - Log::Log4perl->get_logger(__PACKAGE__)->trace("logging enabled in $PROGRAM_NAME"); + TRACE("logging enabled in $PROGRAM_NAME"); } # this is copied from Log::Log4perl's :easy handling, diff --git a/Bugzilla/Sentry.pm b/Bugzilla/Sentry.pm deleted file mode 100644 index 0d7a9c980f..0000000000 --- a/Bugzilla/Sentry.pm +++ /dev/null @@ -1,374 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. -# -# This Source Code Form is "Incompatible With Secondary Licenses", as -# defined by the Mozilla Public License, v. 2.0. - -package Bugzilla::Sentry; - -use 5.10.1; -use strict; -use warnings; - -use base qw(Exporter); -our @EXPORT = qw( - sentry_handle_error - sentry_should_notify -); - -use Carp; -use DateTime; -use File::Temp; -use JSON (); -use List::MoreUtils qw( any ); -use LWP::UserAgent; -use Sys::Hostname; -use URI; -use URI::QueryParam; - -use Bugzilla::Constants; -use Bugzilla::RNG qw(irand); -use Bugzilla::Util; -use Bugzilla::WebService::Constants; - -use constant CONFIG => { - # 'codes' lists the code-errors which are sent to sentry - codes => [qw( - bug_error - chart_datafile_corrupt - chart_dir_nonexistent - chart_file_open_fail - illegal_content_type_method - jobqueue_insert_failed - ldap_bind_failed - mail_send_error - template_error - token_generation_error - param_must_be_numeric - )], - - # any error/warning messages matching these regex's will not be logged or - # sent to sentry - ignore => [ - qr/^compiled template :\s*$/, - qr/^Use of uninitialized value \$compiled in concatenation \(\.\) or string/, - ], - - # any error/warning messages matching these regex's will be logged but not - # sent to sentry - sentry_ignore => [ - qr/Software caused connection abort/, - qr/Could not check out .*\/cvsroot/, - qr/Unicode character \S+ is illegal/, - qr/Lost connection to MySQL server during query/, - qr/Call me again when you have some data to chart/, - qr/relative paths are not allowed/, - qr/Illegal mix of collations for operation/, - ], - - # (ab)use the logger to classify error/warning types - logger => [ - { - match => [ - qr/DBD::mysql/, - qr/Can't connect to the database/, - ], - logger => 'database_error', - }, - { - match => [ qr/PatchReader/ ], - logger => 'patchreader', - }, - { - match => [ qr/Use of uninitialized value/ ], - logger => 'uninitialized_warning', - }, - ], -}; - -sub sentry_generate_id { - return sprintf('%04x%04x%04x%04x%04x%04x%04x%04x', - irand(0xffff), irand(0xffff), - irand(0xffff), - irand(0x0fff) | 0x4000, - irand(0x3fff) | 0x8000, - irand(0xffff), irand(0xffff), irand(0xffff) - ); -} - -sub sentry_should_notify { - my $code_error = shift; - return grep { $_ eq $code_error } @{ CONFIG->{codes} }; -} - -sub sentry_handle_error { - my $level = shift; - my @message = split(/\n/, shift); - my $id = sentry_generate_id(); - - my $is_error = $level eq 'error'; - if ($level ne 'error' && $level ne 'warning') { - # it's a code-error - return 0 unless sentry_should_notify($level); - $is_error = 1; - $level = 'error'; - } - - # build traceback - my $traceback; - { - # for now don't show function arguments, in case they contain - # confidential data. waiting on bug 700683 - #local $Carp::MaxArgLen = 256; - #local $Carp::MaxArgNums = 0; - local $Carp::MaxArgNums = -1; - local $Carp::CarpInternal{'CGI::Carp'} = 1; - local $Carp::CarpInternal{'Bugzilla::Error'} = 1; - local $Carp::CarpInternal{'Bugzilla::Sentry'} = 1; - $traceback = trim(Carp::longmess()); - } - - # strip timestamp - foreach my $line (@message) { - $line =~ s/^\[[^\]]+\] //; - } - my $message = join(" ", map { trim($_) } grep { $_ ne '' } @message); - - # message content filtering - foreach my $re (@{ CONFIG->{ignore} }) { - return 0 if $message =~ $re; - } - - # determine logger - my $logger; - foreach my $config (@{ CONFIG->{logger} }) { - foreach my $re (@{ $config->{match} }) { - if ($message =~ $re) { - $logger = $config->{logger}; - last; - } - } - last if $logger; - } - $logger ||= $level; - - # don't send to sentry unless configured - my $send_to_sentry = Bugzilla->params->{sentry_uri} ? 1 : 0; - - # web service filtering - if ($send_to_sentry - && (Bugzilla->error_mode == ERROR_MODE_DIE_SOAP_FAULT || Bugzilla->error_mode == ERROR_MODE_JSON_RPC)) - { - my ($code) = $message =~ /^(-?\d+): /; - if ($code - && !($code == ERROR_UNKNOWN_FATAL || $code == ERROR_UNKNOWN_TRANSIENT)) - { - $send_to_sentry = 0; - } - } - - # message content filtering - if ($send_to_sentry) { - foreach my $re (@{ CONFIG->{sentry_ignore} }) { - if ($message =~ $re) { - $send_to_sentry = 0; - last; - } - } - } - - # invalid boolean search errors need special handling - if ($message =~ /selectcol_arrayref failed: syntax error/ - && $message =~ /IN BOOLEAN MODE/ - && $message =~ /Bugzilla\/Search\.pm/) - { - $send_to_sentry = 0; - } - - # for now, don't send patchreader errors to sentry - $send_to_sentry = 0 - if $logger eq 'patchreader'; - - # log to apache's error_log - if ($send_to_sentry) { - _write_to_error_log("$message [#$id]", $is_error); - } else { - $traceback =~ s/\n/ /g; - _write_to_error_log("$message $traceback", $is_error); - } - - return 0 unless $send_to_sentry; - - my $user_data = undef; - eval { - my $user = Bugzilla->user; - if ($user->id) { - $user_data = { - id => $user->login, - name => $user->name, - }; - } - }; - - my $uri = URI->new(Bugzilla->cgi->self_url); - $uri->query(undef); - - # sanitise - - # sanitise these query-string params - # names are checked as-is as well as prefixed by BUGZILLA_ - my @sanitise_params = qw( PASSWORD TOKEN API_KEY ); - - # remove these ENV vars - my @sanitise_vars = qw( HTTP_COOKIE HTTP_X_BUGZILLA_PASSWORD HTTP_X_BUGZILLA_API_KEY HTTP_X_BUGZILLA_TOKEN ); - - foreach my $var (qw( QUERY_STRING REDIRECT_QUERY_STRING )) { - next unless exists $ENV{$var}; - my @pairs = split('&', $ENV{$var}); - foreach my $pair (@pairs) { - next unless $pair =~ /^([^=]+)=(.+)$/; - my ($param, $value) = ($1, $2); - if (any { uc($param) eq $_ || uc($param) eq "BUGZILLA_$_" } @sanitise_params) { - $value = '*'; - } - $pair = $param . '=' . $value; - } - $ENV{$var} = join('&', @pairs); - } - foreach my $var (qw( REQUEST_URI HTTP_REFERER )) { - next unless exists $ENV{$var}; - my $uri = URI->new($ENV{$var}); - foreach my $param ($uri->query_param) { - if (any { uc($param) eq $_ || uc($param) eq "BUGZILLA_$_" } @sanitise_params) { - $uri->query_param($param, '*'); - } - } - $ENV{$var} = $uri->as_string; - } - foreach my $var (@sanitise_vars) { - delete $ENV{$var}; - } - - my $now = DateTime->now(); - my $data = { - event_id => $id, - message => $message, - timestamp => $now->iso8601(), - level => $level, - platform => 'Other', - logger => $logger, - server_name => hostname(), - 'sentry.interfaces.User' => $user_data, - 'sentry.interfaces.Http' => { - url => $uri->as_string, - method => $ENV{REQUEST_METHOD}, - query_string => $ENV{QUERY_STRING}, - env => \%ENV, - }, - extra => { - stacktrace => $traceback, - }, - }; - - my $fh = File::Temp->new( - DIR => bz_locations()->{error_reports}, - TEMPLATE => $now->ymd('') . $now->hms('') . '-XXXX', - SUFFIX => '.dump', - UNLINK => 0, - - ); - if (!$fh) { - warn "Failed to create dump file: $!\n"; - return; - } - print $fh JSON->new->utf8(1)->pretty(0)->allow_nonref(1)->encode($data); - close($fh); - return 1; -} - -sub _write_to_error_log { - my ($message, $is_error) = @_; - if ($ENV{MOD_PERL}) { - require Apache2::Log; - if ($is_error) { - Apache2::ServerRec::log_error($message); - } else { - Apache2::ServerRec::warn($message); - } - } else { - print STDERR $message, "\n"; - } -} - -# lifted from Bugzilla::Error -sub _in_eval { - my $in_eval = 0; - for (my $stack = 1; my $sub = (caller($stack))[3]; $stack++) { - last if $sub =~ /^ModPerl/; - last if $sub =~ /^Bugzilla::Template/; - $in_eval = 1 if $sub =~ /^\(eval\)/; - } - return $in_eval; -} - -sub _sentry_die_handler { - my $message = shift; - $message =~ s/^undef error - //; - - # avoid recursion, and check for CGI::Carp::die failures - my $in_cgi_carp_die = 0; - for (my $stack = 1; my $sub = (caller($stack))[3]; $stack++) { - return if $sub =~ /:_sentry_die_handler$/; - $in_cgi_carp_die = 1 if $sub =~ /CGI::Carp::die$/; - } - - return if $Bugzilla::Template::is_processing; - return if _in_eval(); - - # mod_perl overrides exit to call die with this string - exit if $message =~ /\bModPerl::Util::exit\b/; - - my $nested_error = ''; - my $is_compilation_failure = $message =~ /\bcompilation (aborted|failed)\b/i; - - # if we are called via CGI::Carp::die chances are something is seriously - # wrong, so skip trying to use ThrowTemplateError - if (!$in_cgi_carp_die && !$is_compilation_failure) { - eval { - my $cgi = Bugzilla->cgi; - $cgi->close_standby_message('text/html', 'inline', 'error', 'html'); - Bugzilla::Error::ThrowTemplateError($message); - print $cgi->multipart_final() if $cgi->{_multipart_in_progress}; - }; - $nested_error = $@ if $@; - } - - if ($is_compilation_failure || - $in_cgi_carp_die || - ($nested_error && $nested_error !~ /\bModPerl::Util::exit\b/) - ) { - sentry_handle_error('error', $message); - - # and call the normal error management - # (ISE for web pages, error response for web services, etc) - CORE::die($message); - } - exit; -} - -sub install_sentry_handler { - $SIG{__DIE__} = \&sentry_die_handler; - $SIG{__WARN__} = sub { - return if _in_eval(); - sentry_handle_error('warning', shift); - }; -} - -BEGIN { - if ($ENV{SCRIPT_NAME} || $ENV{MOD_PERL}) { - install_sentry_handler(); - } -} - -1; diff --git a/Log/Log4perl/Layout/Mozilla.pm b/Log/Log4perl/Layout/Mozilla.pm index 67a070c546..b625c54f48 100644 --- a/Log/Log4perl/Layout/Mozilla.pm +++ b/Log/Log4perl/Layout/Mozilla.pm @@ -52,6 +52,7 @@ sub render { ); my $mdc = Log::Log4perl::MDC->get_context; + my $fields = $mdc->{fields} // {}; my %out = ( EnvVersion => LOGGING_FORMAT_VERSION, Hostname => $HOSTNAME, @@ -60,12 +61,12 @@ sub render { Severity => $Log::Log4perl::Level::SYSLOG{$priority}, Timestamp => time() * 1e9, Type => $category, - Fields => { msg => $msg, %$mdc }, + Fields => { msg => $msg, %$fields }, ); my $json_text = $JSON->encode(\%out) . "\n"; if (length($json_text) > $self->max_json_length) { - my $scary_msg = sprintf( "DANGER! LOG MESSAGE TOO BIG %d > %d", length($json_text), $self->max_json_length ); + my $scary_msg = sprintf 'DANGER! LOG MESSAGE TOO BIG %d > %d', length($json_text), $self->max_json_length; $out{Fields} = { remote_ip => $mdc->{remote_ip}, msg => $scary_msg }; $out{Severity} = 1; # alert $json_text = $JSON->encode(\%out) . "\n"; diff --git a/conf/log4perl-test.conf b/conf/log4perl-test.conf index 65558ba4fc..34b1630732 100644 --- a/conf/log4perl-test.conf +++ b/conf/log4perl-test.conf @@ -16,3 +16,5 @@ log4perl.appender.File = Log::Log4perl::Appender::File log4perl.appender.File.layout = Log::Log4perl::Layout::Mozilla log4perl.appender.File.filename = /app/bugzilla.log log4perl.appender.File.mode = append +log4perl.appender.File.syswrite = 1 +log4perl.appender.File.autoflush = 1 diff --git a/scripts/nagios_blocker_checker.pl b/scripts/nagios_blocker_checker.pl index 0a9ec96b63..a02a1602a3 100755 --- a/scripts/nagios_blocker_checker.pl +++ b/scripts/nagios_blocker_checker.pl @@ -12,10 +12,10 @@ use lib qw(. lib local/lib/perl5); use Bugzilla; +use Bugzilla::Logging; use Bugzilla::Constants; use Bugzilla::Product; use Bugzilla::User; -use Bugzilla::Sentry; use Getopt::Long; use English qw(-no_match_vars); @@ -141,7 +141,7 @@ # nagios check does no good, we terminate if we stick around too long. local $SIG{ALRM} = sub { my $message = "$PROGRAM_NAME ran for longer than $config->{max_runtime} seconds and was auto-terminated."; - sentry_handle_error('error', $message); + FATAL($message); die "$message\n"; }; alarm($config->{max_runtime}); diff --git a/sentry.pl b/sentry.pl deleted file mode 100755 index ebb221cfda..0000000000 --- a/sentry.pl +++ /dev/null @@ -1,94 +0,0 @@ -#!/usr/bin/perl - -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. -# -# This Source Code Form is "Incompatible With Secondary Licenses", as -# defined by the Mozilla Public License, v. 2.0. - -# -# report errors to sentry -# expects a filename with a Data::Dumper serialised parameters -# called by Bugzilla::Sentry -# - -use 5.10.1; -use strict; -use warnings; -use lib qw(. lib local/lib/perl5); - -BEGIN { - delete $ENV{SERVER_SOFTWARE}; - - use Bugzilla::Constants; - exit(0) unless glob(bz_locations()->{error_reports} . '/*.dump'); -} - -use Bugzilla; -use Fcntl qw(:flock); -use File::Slurp qw(read_file); -use HTTP::Request::Common; -use LWP::UserAgent; -use POSIX qw(nice); -use URI; - -Bugzilla->usage_mode(USAGE_MODE_CMDLINE); -nice(19); - -exit(1) unless Bugzilla->params->{sentry_uri}; -my $uri = URI->new(Bugzilla->params->{sentry_uri}); -my $header = build_header($uri); -exit(1) unless $header; - -my $ua = LWP::UserAgent->new(timeout => 10); -if (my $proxy_url = Bugzilla->params->{proxy_url}) { - $ua->proxy(['http', 'https'], $proxy_url); -} - -flock(DATA, LOCK_EX); -foreach my $file (glob(bz_locations()->{error_reports} . '/*.dump')) { - eval { - send_file($uri, $header, $file); - }; -} - -sub build_header { - my ($uri) = @_; - - # split the sentry uri - return undef unless $uri->userinfo && $uri->path; - my ($public_key, $secret_key) = split(/:/, $uri->userinfo); - $uri->userinfo(undef); - my $project_id = $uri->path; - $project_id =~ s/^\///; - $uri->path("/api/$project_id/store/"); - - # build the header - return { - 'X-Sentry-Auth' => sprintf( - "Sentry sentry_version=%s, sentry_timestamp=%s, sentry_key=%s, sentry_client=%s, sentry_secret=%s", - '2.0', - (time), - $public_key, - 'bmo/' . BUGZILLA_VERSION, - $secret_key, - ), - 'Content-Type' => 'application/json' - }; -} - -sub send_file { - my ($uri, $header, $filename) = @_; - # read data dump - my $message = read_file($filename); - unlink($filename); - - # and post to sentry - my $request = POST $uri->canonical, %$header, Content => $message; - my $response = $ua->request($request); -} - -__DATA__ -this exists so the flock() code works. -do not remove this data section. diff --git a/template/en/default/admin/params/advanced.html.tmpl b/template/en/default/admin/params/advanced.html.tmpl index 92c84d7031..7c85881d7e 100644 --- a/template/en/default/admin/params/advanced.html.tmpl +++ b/template/en/default/admin/params/advanced.html.tmpl @@ -68,11 +68,6 @@ disable_bug_updates => "When enabled, all updates to $terms.bugs will be blocked.", - sentry_uri => - "When set, important errors and warnings will be sent to the" - _ " specified Sentry server. Enter the full API KEY URL." - _ " eg https://01234567890123456780123456780123:01234567890123456780123456780123@errormill.mozilla.org/10.", - metrics_enabled => "Collect metrics for reporting to ElasticSearch", metrics_user_ids => diff --git a/template/en/default/global/code-error.html.tmpl b/template/en/default/global/code-error.html.tmpl index bf1ff5ad37..b37e7ca7bf 100644 --- a/template/en/default/global/code-error.html.tmpl +++ b/template/en/default/global/code-error.html.tmpl @@ -532,9 +532,6 @@ admindocslinks = admindocslinks %] -[%# return the generated error_message for sentry %] -[% processed.error_message = error_message %] -

[% terms.Bugzilla %] has suffered an internal error:

@@ -543,13 +540,6 @@ [% error_message FILTER none %]

-[% IF maintainers_notified %] -

- The [% terms.Bugzilla %] maintainers have been notified of this error - [#[% uid FILTER html %]]. -

-[% END %] - [% IF variables %]
 Variables:

From 58c4f6814461ee4e35f0cffbdd63d948df940d35 Mon Sep 17 00:00:00 2001
From: dklawren 
Date: Mon, 9 Apr 2018 23:17:44 -0400
Subject: [PATCH 002/119] Bug 1328900 - Create new group called 'disableusers'
 that can only edit the bugmail and disabledtext fields of a user

---
 Bugzilla/Install.pm                           |  4 +
 admin.cgi                                     |  1 +
 editusers.cgi                                 | 32 +++++---
 .../en/default/bug_modal/user.html.tmpl       |  2 +-
 .../hook/admin/users/userdata-end.html.tmpl   |  4 +-
 template/en/default/admin/admin.html.tmpl     |  2 +-
 .../en/default/admin/users/edit.html.tmpl     | 73 ++++++++++---------
 .../en/default/admin/users/list.html.tmpl     | 15 ++--
 .../en/default/admin/users/userdata.html.tmpl | 49 +++++++------
 template/en/default/global/header.html.tmpl   |  2 +-
 .../default/global/site-navigation.html.tmpl  | 20 ++---
 template/en/default/global/user.html.tmpl     |  2 +-
 12 files changed, 114 insertions(+), 92 deletions(-)

diff --git a/Bugzilla/Install.pm b/Bugzilla/Install.pm
index ced5591114..8bce9b5e7b 100644
--- a/Bugzilla/Install.pm
+++ b/Bugzilla/Install.pm
@@ -203,6 +203,10 @@ use constant SYSTEM_GROUPS => (
         name        => 'editusers',
         description => 'Can edit or disable users'
     },
+    {
+        name        => 'disableusers',
+        description => 'Can disable users'
+    },
     {
         name        => 'creategroups',
         description => 'Can create and destroy groups'
diff --git a/admin.cgi b/admin.cgi
index d8fc0475d9..801b26e20c 100755
--- a/admin.cgi
+++ b/admin.cgi
@@ -25,6 +25,7 @@ print $cgi->header();
 $user->in_group('admin')
   || $user->in_group('tweakparams')
   || $user->in_group('editusers')
+  || $user->in_group('disableusers')
   || $user->can_bless
   || (Bugzilla->params->{'useclassification'} && $user->in_group('editclassifications'))
   || $user->in_group('editcomponents')
diff --git a/editusers.cgi b/editusers.cgi
index 934e0a4ef9..9fbd550fed 100755
--- a/editusers.cgi
+++ b/editusers.cgi
@@ -26,15 +26,18 @@ use Bugzilla::Token;
 
 my $user = Bugzilla->login(LOGIN_REQUIRED);
 
-my $cgi       = Bugzilla->cgi;
-my $template  = Bugzilla->template;
-my $dbh       = Bugzilla->dbh;
-my $userid    = $user->id;
-my $editusers = $user->in_group('editusers');
+my $cgi          = Bugzilla->cgi;
+my $template     = Bugzilla->template;
+my $dbh          = Bugzilla->dbh;
+my $userid       = $user->id;
+my $editusers    = $user->in_group('editusers');
+my $disableusers = $user->in_group('disableusers');
+
 local our $vars     = {};
 
 # Reject access if there is no sense in continuing.
 $editusers
+    || $disableusers
     || $user->can_bless()
     || ThrowUserError("auth_failure", {group  => "editusers",
                                        reason => "cant_bless",
@@ -51,6 +54,7 @@ my $token          = $cgi->param('token');
 
 # Prefill template vars with data used in all or nearly all templates
 $vars->{'editusers'} = $editusers;
+$vars->{'disableusers'} = $disableusers;
 mirrorListSelectionValues();
 
 Bugzilla::Hook::process('admin_editusers_action',
@@ -234,7 +238,7 @@ if ($action eq 'search') {
     # Lock tables during the check+update session.
     $dbh->bz_start_transaction();
 
-    $editusers || $user->can_see_user($otherUser)
+    $editusers || $disableusers || $user->can_see_user($otherUser)
         || ThrowUserError('auth_failure', {reason => "not_visible",
                                            action => "modify",
                                            object => "user"});
@@ -246,11 +250,8 @@ if ($action eq 'search') {
     my $changes = {};
     if ($editusers) {
         $otherUser->set_login($cgi->param('login'));
-        $otherUser->set_name($cgi->param('name'));
         $otherUser->set_password($cgi->param('password'))
             if $cgi->param('password');
-        $otherUser->set_disabledtext($cgi->param('disabledtext'));
-        $otherUser->set_disable_mail($cgi->param('disable_mail'));
         $otherUser->set_extern_id($cgi->param('extern_id'))
             if defined($cgi->param('extern_id'));
         $otherUser->set_password_change_required($cgi->param('password_change_required'));
@@ -262,9 +263,16 @@ if ($action eq 'search') {
         if ($user->in_group('bz_can_disable_mfa') && $otherUser->mfa && $cgi->param('mfa') eq '') {
             $otherUser->set_mfa('');
         }
-        $changes = $otherUser->update();
     }
 
+    if ($editusers || $disableusers) {
+        $otherUser->set_name($cgi->param('name'));
+        $otherUser->set_disabledtext($cgi->param('disabledtext'));
+        $otherUser->set_disable_mail($cgi->param('disable_mail'));
+    }
+
+    $changes = $otherUser->update();
+
     # Update group settings.
     my $sth_add_mapping = $dbh->prepare(
         qq{INSERT INTO user_group_map (
@@ -850,7 +858,9 @@ sub edit_processing {
     my $user = Bugzilla->user;
     my $template = Bugzilla->template;
 
-    $user->in_group('editusers') || $user->can_see_user($otherUser)
+    $user->in_group('editusers')
+        || $user->in_group('disableusers')
+        || $user->can_see_user($otherUser)
         || ThrowUserError('auth_failure', {reason => "not_visible",
                                            action => "modify",
                                            object => "user"});
diff --git a/extensions/BugModal/template/en/default/bug_modal/user.html.tmpl b/extensions/BugModal/template/en/default/bug_modal/user.html.tmpl
index cd05d053fa..9eda7b9367 100644
--- a/extensions/BugModal/template/en/default/bug_modal/user.html.tmpl
+++ b/extensions/BugModal/template/en/default/bug_modal/user.html.tmpl
@@ -46,7 +46,7 @@ END;
             href="mailto:[% u.email FILTER html %]"
             data-user-email="[% u.email FILTER html %]"
             data-user-id="[% u.id FILTER html %]"
-            data-show-edit="[% user.in_group('editusers') || user.bless_groups.size > 0 ? 1 : 0 %]"
+            data-show-edit="[% user.in_group('editusers') || user.in_group('disableusers') || user.bless_groups.size > 0 ? 1 : 0 %]"
             title="[% u.identity FILTER html %]"
           [% ELSE %]
             href="user_profile?user_id=[% u.id FILTER none %]"
diff --git a/extensions/SecureMail/template/en/default/hook/admin/users/userdata-end.html.tmpl b/extensions/SecureMail/template/en/default/hook/admin/users/userdata-end.html.tmpl
index a90266dae6..e5e299ef97 100644
--- a/extensions/SecureMail/template/en/default/hook/admin/users/userdata-end.html.tmpl
+++ b/extensions/SecureMail/template/en/default/hook/admin/users/userdata-end.html.tmpl
@@ -6,7 +6,7 @@
   # defined by the Mozilla Public License, v. 2.0.
   #%]
 
-[% RETURN UNLESS otheruser.id %]
+[% RETURN UNLESS otheruser.id && user.in_group('editusers') %]
 
 
   Has Secure Mail Key/Cert:
@@ -14,7 +14,7 @@
     [% otheruser.public_key ? "Yes" : "No" %]
   
 
-
+ 
 
   Member of Secure Mail Group:
   
diff --git a/template/en/default/admin/admin.html.tmpl b/template/en/default/admin/admin.html.tmpl
index 62a246ceb5..09fe008358 100644
--- a/template/en/default/admin/admin.html.tmpl
+++ b/template/en/default/admin/admin.html.tmpl
@@ -56,7 +56,7 @@
         You can also automate this check by running sanitycheck.pl from a cron job.
         A notification will be sent per email to the specified user if errors are detected.
 
-        [% class = (user.in_group('editusers') || user.can_bless) ? "" : "forbidden" %]
+        [% class = (user.in_group('editusers') || user.in_group('disableusers') || user.can_bless) ? "" : "forbidden" %]
         
Users
Create new user accounts or edit existing ones. You can also add and remove users from groups (also known as "user privileges").
diff --git a/template/en/default/admin/users/edit.html.tmpl b/template/en/default/admin/users/edit.html.tmpl index 4eb62e7633..de98268007 100644 --- a/template/en/default/admin/users/edit.html.tmpl +++ b/template/en/default/admin/users/edit.html.tmpl @@ -68,9 +68,10 @@ $(function() {
[% PROCESS admin/users/userdata.html.tmpl - editform = 1 - editusers = editusers - otheruser = otheruser + editform = 1 + editusers = editusers + disableusers = disableusers + otheruser = otheruser %] [% IF groups.size %] @@ -125,43 +126,46 @@ $(function() { [% END %] - - - - - - [% IF otheruser.groups_owned.size %] + [% IF editusers %] - + + + + [% IF otheruser.groups_owned.size %] + + + + + [% END %] + + + + [% END %] - - - -
Product responsibilities: - [% IF otheruser.product_responsibilities.size %] - [% PROCESS admin/users/responsibilities.html.tmpl otheruser = otheruser %] - [% ELSE %] - none - [% END %] -
Groups Owned:Product responsibilities: - [% can_edit_groups = user.in_group('creategroups') %] - [% FOREACH group = otheruser.groups_owned %] - [% IF can_edit_groups %] - + [% IF otheruser.product_responsibilities.size %] + [% PROCESS admin/users/responsibilities.html.tmpl otheruser = otheruser %] + [% ELSE %] + none + [% END %] +
Groups Owned: + [% can_edit_groups = user.in_group('creategroups') %] + [% FOREACH group = otheruser.groups_owned %] + [% IF can_edit_groups %] + + [% END %] + [% group.name FILTER html %] + [% '' IF can_edit_groups %]
[% END %] - [% group.name FILTER html %] - [% '' IF can_edit_groups %]
+
Last Login: + [% IF otheruser.last_seen_date %] + [% otheruser.last_seen_date FILTER html %] + [% ELSE %] + never [% END %]
Last Login: - [% IF otheruser.last_seen_date %] - [% otheruser.last_seen_date FILTER html %] - [% ELSE %] - never - [% END %] -

@@ -171,11 +175,10 @@ $(function() { [% INCLUDE listselectionhiddenfields %] - [% IF editusers %], [% ELSE %] or [% END %] - View Account History - [% IF editusers %] or View Admin History diff --git a/template/en/default/admin/users/list.html.tmpl b/template/en/default/admin/users/list.html.tmpl index 3ebfc29707..db425b40dc 100644 --- a/template/en/default/admin/users/list.html.tmpl +++ b/template/en/default/admin/users/list.html.tmpl @@ -45,23 +45,24 @@ {name => 'last_seen_date' heading => 'Last Login' } - {heading => 'Account History' - content => 'View' - contentlink => 'editusers.cgi?action=activity' _ - '&userid=%%userid%%' _ - listselectionurlparams - } ] %] [% IF editusers %] [% columns.push({ + heading => 'Account History' + content => 'View' + contentlink => 'editusers.cgi?action=activity' _ + '&userid=%%userid%%' _ + listselectionurlparams + } + { heading => 'Admin History' content => 'View' contentlink => 'editusers.cgi?action=admin_activity' _ '&userid=%%userid%%' _ listselectionurlparams - }) + }) %] [% END %] diff --git a/template/en/default/admin/users/userdata.html.tmpl b/template/en/default/admin/users/userdata.html.tmpl index 449a1b1433..c4ab070100 100644 --- a/template/en/default/admin/users/userdata.html.tmpl +++ b/template/en/default/admin/users/userdata.html.tmpl @@ -54,7 +54,7 @@ - [% IF editusers %] + [% IF editusers || disableusers %] @@ -94,7 +94,9 @@ [% END %] +[% END %] +[% IF editusers || disableusers %] @@ -122,30 +124,31 @@ explain why.) - [% IF editform %] - - - - [% IF user.in_group('bz_can_disable_mfa') %] - [% IF otheruser.mfa %] - - [% ELSE %] - Disabled - [% END %] +[% END %] + +[% IF editform && editusers %] + + + + [% IF user.in_group('bz_can_disable_mfa') %] + [% IF otheruser.mfa %] + [% ELSE %] - [% otheruser.mfa ? "Enabled - " _ otheruser.mfa : "Disabled" FILTER html %] + Disabled [% END %] - - - [% END %] + [% ELSE %] + [% otheruser.mfa ? "Enabled - " _ otheruser.mfa : "Disabled" FILTER html %] + [% END %] + + [% END %] [% Hook.process('end') %] diff --git a/template/en/default/global/header.html.tmpl b/template/en/default/global/header.html.tmpl index 1d304ad04c..cf1c8b9913 100644 --- a/template/en/default/global/header.html.tmpl +++ b/template/en/default/global/header.html.tmpl @@ -294,7 +294,7 @@

  • Reports
  • - [% IF user.in_group('tweakparams') || user.in_group('editusers') || user.can_bless + [% IF user.in_group('tweakparams') || user.in_group('editusers') || user.can_bless || user.in_group('disableusers') || (Param('useclassification') && user.in_group('editclassifications')) || user.in_group('editcomponents') || user.in_group('admin') || user.in_group('creategroups') || user.in_group('editkeywords') || user.in_group('bz_canusewhines') diff --git a/template/en/default/global/site-navigation.html.tmpl b/template/en/default/global/site-navigation.html.tmpl index 06b0eaa921..5108754229 100644 --- a/template/en/default/global/site-navigation.html.tmpl +++ b/template/en/default/global/site-navigation.html.tmpl @@ -69,22 +69,22 @@ [% END %] [%# *** Bugzilla Administration Tools *** %] - [% IF user.login %] - [% '' IF user.in_group('tweakparams') %] - [% '' IF user.in_group('editusers') %] + [% '' IF user.in_group('editusers') || user.in_group('disableusers') %] [% '' IF user.in_group('editcomponents') || user.get_products_by_permission("editcomponents").size %] - [% '' IF user.in_group('editcomponents') %] - [% '' IF user.in_group('creategroups') %] - [% '' IF user.in_group('editkeywords') %] - [% '' IF user.in_group('bz_canusewhines') %] - [% '' IF user.in_group('editcomponents') %] - [% END %] + [% END %] [% END %] diff --git a/template/en/default/global/user.html.tmpl b/template/en/default/global/user.html.tmpl index caea27c464..876d128054 100644 --- a/template/en/default/global/user.html.tmpl +++ b/template/en/default/global/user.html.tmpl @@ -28,7 +28,7 @@ [% IF user.id %]

    #[% id FILTER html %] -

    - -[% PROCESS global/footer.html.tmpl %] diff --git a/extensions/BMO/template/en/default/bug/create/custom_forms.none.tmpl b/extensions/BMO/template/en/default/bug/create/custom_forms.none.tmpl index f0ddf0229b..7b588f765a 100644 --- a/extensions/BMO/template/en/default/bug/create/custom_forms.none.tmpl +++ b/extensions/BMO/template/en/default/bug/create/custom_forms.none.tmpl @@ -152,16 +152,6 @@ custom_forms = { title => "Internet Public Policy Issue", }, ], - "Marketplace" => [ - { - link => "form.fxos.preload.app", - title => "Firefox OS Pre-load App", - }, - { - link => "form.third.party", - title => "Third Party Applications Issue Form", - }, - ], "Data Compliance" => [ { link => "form.data.compliance", From 5a966f5a68136e17f7ab86d3be1cdf3ba5eacb9c Mon Sep 17 00:00:00 2001 From: dklawren Date: Thu, 12 Apr 2018 13:21:21 -0400 Subject: [PATCH 013/119] Bug 1453681 - Phabricator project.search when searching for a specific project name can return more than one match --- extensions/PhabBugz/lib/Project.pm | 13 ++++++++++++- extensions/PhabBugz/lib/Util.pm | 9 ++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/extensions/PhabBugz/lib/Project.pm b/extensions/PhabBugz/lib/Project.pm index b0babc58b8..0fb9ecaa52 100644 --- a/extensions/PhabBugz/lib/Project.pm +++ b/extensions/PhabBugz/lib/Project.pm @@ -47,7 +47,18 @@ sub new_from_query { my $result = request( 'project.search', $data ); if ( exists $result->{result}{data} && @{ $result->{result}{data} } ) { - return $class->new( $result->{result}{data}[0] ); + # If name is used as a query param, we need to loop through and look + # for exact match as Conduit will tokenize the name instead of doing + # exact string match :( If name is not used, then return first one. + if ( exists $params->{name} ) { + foreach my $item ( @{ $result->{result}{data} } ) { + next if $item->{fields}{name} ne $params->{name}; + return $class->new($item); + } + } + else { + return $class->new( $result->{result}{data}[0] ); + } } } diff --git a/extensions/PhabBugz/lib/Util.pm b/extensions/PhabBugz/lib/Util.pm index 52ea9c0d58..cd396602e0 100644 --- a/extensions/PhabBugz/lib/Util.pm +++ b/extensions/PhabBugz/lib/Util.pm @@ -297,7 +297,14 @@ sub get_project_phid { return undef unless (exists $result->{result}{data} && @{ $result->{result}{data} }); - $project_phid = $result->{result}{data}[0]{phid}; + # If name is used as a query param, we need to loop through and look + # for exact match as Conduit will tokenize the name instead of doing + # exact string match :( + foreach my $item ( @{ $result->{result}{data} } ) { + next if $item->{fields}{name} ne $project; + $project_phid = $item->{phid}; + } + $memcache->set_config({ key => "phab_project_phid_" . $project, data => $project_phid }); } return $project_phid; From 99a5f75c6da5e1af5bf01a0b9e0aa8c88bcfdf35 Mon Sep 17 00:00:00 2001 From: Dylan William Hardison Date: Thu, 12 Apr 2018 13:27:19 -0400 Subject: [PATCH 014/119] Bug 1453697 - ensure error_message is escaped in opengraph description --- .../template/en/default/hook/global/header-start.html.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/OpenGraph/template/en/default/hook/global/header-start.html.tmpl b/extensions/OpenGraph/template/en/default/hook/global/header-start.html.tmpl index dc0dc1ad9b..51c388d427 100644 --- a/extensions/OpenGraph/template/en/default/hook/global/header-start.html.tmpl +++ b/extensions/OpenGraph/template/en/default/hook/global/header-start.html.tmpl @@ -14,7 +14,7 @@ [% ELSIF error_message %] - + [% END %] [% IF og_image %] From 610bca4869a9320ff91d84c421b61d3346658d64 Mon Sep 17 00:00:00 2001 From: dklawren Date: Thu, 12 Apr 2018 15:01:10 -0400 Subject: [PATCH 015/119] Bump version to 20180412.2 (#538) --- Bugzilla.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Bugzilla.pm b/Bugzilla.pm index e5857caa68..ef6c804338 100644 --- a/Bugzilla.pm +++ b/Bugzilla.pm @@ -22,7 +22,7 @@ BEGIN { } } -our $VERSION = '20180412.1'; +our $VERSION = '20180412.2'; use Bugzilla::Auth; use Bugzilla::Auth::Persist::Cookie; From 23b1be3839f09d4806bf841c4f03743eb78eb1ab Mon Sep 17 00:00:00 2001 From: Israel Madueme Date: Wed, 18 Apr 2018 16:57:27 -0400 Subject: [PATCH 016/119] Bug 1450325 - add unsubscribe link to html bug email template --- extensions/BMO/template/en/default/email/bugmail.html.tmpl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/extensions/BMO/template/en/default/email/bugmail.html.tmpl b/extensions/BMO/template/en/default/email/bugmail.html.tmpl index d5cb3af9a3..0b08e4a866 100644 --- a/extensions/BMO/template/en/default/email/bugmail.html.tmpl +++ b/extensions/BMO/template/en/default/email/bugmail.html.tmpl @@ -111,7 +111,10 @@ [% END %] [% END %] + Configure your email settings at + [% urlbase FILTER none %]userprefs.cgi?tab=email.
    +
    [%# Filtering of the URL param is not required & would break the URL when the comment anchor is set %] From 99005e14ea722032a25fd83ba275e1c8c2e18b8e Mon Sep 17 00:00:00 2001 From: Israel Madueme Date: Fri, 20 Apr 2018 12:51:00 -0400 Subject: [PATCH 017/119] Bug 1451599 - Move guidelines checkbox on signup page to the left --- extensions/BMO/template/en/default/account/create.html.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/BMO/template/en/default/account/create.html.tmpl b/extensions/BMO/template/en/default/account/create.html.tmpl index 59ff822bd1..64cf9bad81 100644 --- a/extensions/BMO/template/en/default/account/create.html.tmpl +++ b/extensions/BMO/template/en/default/account/create.html.tmpl @@ -156,10 +156,10 @@ function onSubmit() { + I have read [% terms.Bugzilla %] Etiquette and the Mozilla Community Participation Guidelines and agree to abide by them. - From 90150e46031cabbe94239820e5010fb88200d52b Mon Sep 17 00:00:00 2001 From: Israel Madueme Date: Fri, 20 Apr 2018 12:52:29 -0400 Subject: [PATCH 018/119] Bug 1438205 - Preserve comments in progress across page reloads Comments being typed will be saved in Local Storage for a week. When the user somehow reloads the show bug page, the comment box will be filled in with what was last saved. This also fixes the bug where you type up a comment, go to change the bug details, hit cancel, and ultimately lose your comment in progress because of the reloading nature of the cancel button. --- extensions/BugModal/web/bug_modal.js | 59 ++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/extensions/BugModal/web/bug_modal.js b/extensions/BugModal/web/bug_modal.js index c6abb5d7a4..bf5c300e14 100644 --- a/extensions/BugModal/web/bug_modal.js +++ b/extensions/BugModal/web/bug_modal.js @@ -96,6 +96,54 @@ $(function() { $('#editing').val(''); } + function saveBugComment(text) { + if (text.length < 1) return clearSavedBugComment(); + if (text.length > 65535) return; + let key = `bug-modal-saved-comment-${BUGZILLA.bug_id}`; + let value = { + text: text, + savedAt: Date.now() + }; + localStorage.setItem(key, JSON.stringify(value)); + } + + function clearSavedBugComment() { + let key = `bug-modal-saved-comment-${BUGZILLA.bug_id}`; + localStorage.removeItem(key); + } + + function restoreSavedBugComment() { + expireSavedComments(); + let key = `bug-modal-saved-comment-${BUGZILLA.bug_id}`; + let value = JSON.parse(localStorage.getItem(key)); + if (value){ + let commentBox = document.querySelector("textarea#comment"); + commentBox.value = value['text']; + if (BUGZILLA.user.settings.autosize_comments) { + autosize.update(commentBox); + } + } + } + + function expireSavedComments() { + const AGE_THRESHOLD = 7 * 24 * 60 * 60 * 1000; // 7 days in milliseconds. + let expiredKeys = []; + for (let i = 0; i < localStorage.length; i++) { + let key = localStorage.key(i); + if (key.match(/^bug-modal-saved-comment-/)) { + let value = JSON.parse(localStorage.getItem(key)); + let savedAt = value['savedAt'] || 0; + let age = Date.now() - savedAt; + if (age < 0 || age > AGE_THRESHOLD) { + expiredKeys.push(key); + } + } + } + expiredKeys.forEach((key) => { + localStorage.removeItem(key); + }); + } + // expand/colapse module $('.module-latch') .click(function(event) { @@ -586,6 +634,8 @@ $(function() { .toArray() .join(' ') ); + + clearSavedBugComment(); }) .attr('disabled', false); @@ -1272,9 +1322,18 @@ $(function() { } }); + // Save comments in progress + $('#comment') + .on('input', function(event) { + saveBugComment(event.target.value); + }); + // finally switch to edit mode if we navigate back to a page that was editing $(window).on('pageshow', restoreEditMode); + $(window).on('pageshow', restoreSavedBugComment); + $(window).on('focus', restoreSavedBugComment); restoreEditMode(); + restoreSavedBugComment(); }); function confirmUnsafeURL(url) { From 5b0812a368b9e646f393ccbf62186ed1b2b535d2 Mon Sep 17 00:00:00 2001 From: dklawren Date: Fri, 20 Apr 2018 12:57:40 -0400 Subject: [PATCH 019/119] Code Pull requests 33 Insights Settings Bug 1452531 - PhabBugz code should add allow visibility to reviewers when creating custom policies --- extensions/PhabBugz/lib/Feed.pm | 3 +-- extensions/PhabBugz/lib/Policy.pm | 4 ++++ extensions/PhabBugz/lib/Util.pm | 19 ++++++++++++------- extensions/Push/lib/Connector/Phabricator.pm | 2 +- 4 files changed, 18 insertions(+), 10 deletions(-) diff --git a/extensions/PhabBugz/lib/Feed.pm b/extensions/PhabBugz/lib/Feed.pm index a51f240a83..2904e9dede 100644 --- a/extensions/PhabBugz/lib/Feed.pm +++ b/extensions/PhabBugz/lib/Feed.pm @@ -28,7 +28,6 @@ use Bugzilla::Extension::PhabBugz::User; use Bugzilla::Extension::PhabBugz::Util qw( add_security_sync_comments create_revision_attachment - edit_revision_policy get_bug_role_phids get_phab_bmo_ids get_project_phid @@ -253,7 +252,7 @@ sub process_revision_change { my ($added, $removed) = diff_arrays($current_projects, \@set_projects); if (@$added || @$removed) { DEBUG('Project groups do not match. Need new custom policy'); - $current_policy= undef; + $current_policy = undef; } else { DEBUG('Project groups match. Leaving current policy as-is'); diff --git a/extensions/PhabBugz/lib/Policy.pm b/extensions/PhabBugz/lib/Policy.pm index 8162ac52cb..0beecc8e13 100644 --- a/extensions/PhabBugz/lib/Policy.pm +++ b/extensions/PhabBugz/lib/Policy.pm @@ -97,6 +97,10 @@ sub create { { action => 'allow', rule => 'PhabricatorSubscriptionsSubscribersPolicyRule', + }, + { + action => 'allow', + rule => 'PhabricatorDifferentialReviewersPolicyRule' } ] }; diff --git a/extensions/PhabBugz/lib/Util.pm b/extensions/PhabBugz/lib/Util.pm index cd396602e0..a640f52a1f 100644 --- a/extensions/PhabBugz/lib/Util.pm +++ b/extensions/PhabBugz/lib/Util.pm @@ -136,7 +136,7 @@ sub get_bug_role_phids { } sub create_private_revision_policy { - my ($bug, $groups) = @_; + my ( $groups ) = @_; my $data = { objectType => 'DREV', @@ -144,7 +144,11 @@ sub create_private_revision_policy { policy => [ { action => 'allow', - rule => 'PhabricatorSubscriptionsSubscribersPolicyRule', + rule => 'PhabricatorSubscriptionsSubscribersPolicyRule' + }, + { + action => 'allow', + rule => 'PhabricatorDifferentialReviewersPolicyRule' } ] }; @@ -197,24 +201,25 @@ sub make_revision_public { ], objectIdentifier => $revision_phid }); + } sub make_revision_private { my ($revision_phid) = @_; - my $secure_revision = Bugzilla::Extension::PhabBugz::Project->new_from_query({ - name => 'secure-revision' - }); + # When creating a private policy with no args it + # creates one with the secure-revision project. + my $private_policy = create_private_revision_policy(); return request('differential.revision.edit', { transactions => [ { type => "view", - value => $secure_revision->phid + value => $private_policy->phid }, { type => "edit", - value => $secure_revision->phid + value => $private_policy->phid } ], objectIdentifier => $revision_phid diff --git a/extensions/Push/lib/Connector/Phabricator.pm b/extensions/Push/lib/Connector/Phabricator.pm index 5da64901af..1878834a92 100644 --- a/extensions/Push/lib/Connector/Phabricator.pm +++ b/extensions/Push/lib/Connector/Phabricator.pm @@ -128,7 +128,7 @@ sub send { $revision->{id}, $bug->id )); - my $policy_phid = create_private_revision_policy( $bug, \@set_groups ); + my $policy_phid = create_private_revision_policy( \@set_groups ); edit_revision_policy( $revision_phid, $policy_phid, $subscribers ); $rev_obj->add_project($secure_project_phid); $revision_updated = 1; From 867057e89a8f309cedcc1affbe00923fd8f8b3c2 Mon Sep 17 00:00:00 2001 From: byron jones Date: Sat, 21 Apr 2018 01:18:53 +0800 Subject: [PATCH 020/119] Bug 1440828 - Phabricator review requests should show up on the BMO dashboard --- .../en/default/pages/mydashboard.html.tmpl | 42 ++--- extensions/MyDashboard/web/js/flags.js | 144 +++++++++++++++--- extensions/MyDashboard/web/js/query.js | 7 + .../MyDashboard/web/styles/mydashboard.css | 5 +- extensions/PhabBugz/lib/Util.pm | 50 +++++- extensions/PhabBugz/lib/WebService.pm | 114 +++++++++++++- t/008filter.t | 3 + 7 files changed, 312 insertions(+), 53 deletions(-) diff --git a/extensions/MyDashboard/template/en/default/pages/mydashboard.html.tmpl b/extensions/MyDashboard/template/en/default/pages/mydashboard.html.tmpl index 36a8db6f24..a81a5903b7 100644 --- a/extensions/MyDashboard/template/en/default/pages/mydashboard.html.tmpl +++ b/extensions/MyDashboard/template/en/default/pages/mydashboard.html.tmpl @@ -105,6 +105,7 @@
    + Loading... 0 [% terms.bugs %] found | Refresh @@ -126,29 +127,28 @@ %]
    -
    -
    - Flags Requested of You + [% BLOCK requests_table %] +
    +
    [% title FILTER html %]
    + Loading... + + 0 reviews found + | Refresh + | Buglist + +
    - - 0 flags found - | Refresh - | Buglist - -
    -
    + [% END %] -
    -
    - Flags You Have Requested -
    - - 0 flags found - | Refresh - | Buglist - -
    -
    + [% ## no-008filter + # requires PhabBugz extension + IF Param('phabricator_enabled'); + PROCESS requests_table name='reviews' title='Reviews Requested of You'; + END; + + PROCESS requests_table name='requestee' title='Flags Requested of You'; + PROCESS requests_table name='requester' title='Flags You Have Requested'; + %]
    [% IF user.showmybugslink OR user.queries.size OR user.queries_subscribed.size %] diff --git a/extensions/MyDashboard/web/js/flags.js b/extensions/MyDashboard/web/js/flags.js index 95b2567083..b56559ae38 100644 --- a/extensions/MyDashboard/web/js/flags.js +++ b/extensions/MyDashboard/web/js/flags.js @@ -16,15 +16,18 @@ $(function () { // Common var counter = 0; var dataSource = { + reviews: null, requestee: null, requester: null }; var dataTable = { + reviews: null, requestee: null, requester: null }; + var hasReviews = !!document.getElementById('reviews_container'); - var updateFlagTable = function(type) { + var updateRequestsTable = function(type) { if (!type) return; counter = counter + 1; @@ -32,32 +35,40 @@ $(function () { var callback = { success: function(e) { if (e.response) { + Y.one('#' + type + '_loading').addClass('bz_default_hidden'); Y.one('#' + type + '_count_refresh').removeClass('bz_default_hidden'); Y.one("#" + type + "_flags_found").setHTML( - e.response.results.length + ' flags found'); + e.response.results.length + + ' request' + (e.response.results.length == 1 ? '' : 's') + + ' found'); dataTable[type].set('data', e.response.results); } }, failure: function(o) { - if (o.error) { - alert("Failed to load flag list from Bugzilla:\n\n" + o.error.message); + Y.one('#' + type + '_loading').addClass('bz_default_hidden'); + Y.one('#' + type + '_count_refresh').removeClass('bz_default_hidden'); + if (o.error && o.error.message) { + alert("Failed to load requests:\n\n" + o.error.message); } else { - alert("Failed to load flag list from Bugzilla."); + alert("Failed to load requests."); } } }; + var method = type === 'reviews' ? 'PhabBugz.needs_review' : 'MyDashboard.run_flag_query'; var json_object = { version: "1.1", - method: "MyDashboard.run_flag_query", + method: method, id: counter, - params: { type : type, - Bugzilla_api_token : (BUGZILLA.api_token ? BUGZILLA.api_token : '') + params: { + type : type, + Bugzilla_api_token : (BUGZILLA.api_token ? BUGZILLA.api_token : '') } }; var stringified = Y.JSON.stringify(json_object); + Y.one('#' + type + '_loading').removeClass('bz_default_hidden'); Y.one('#' + type + '_count_refresh').addClass('bz_default_hidden'); dataTable[type].set('data', []); @@ -86,14 +97,17 @@ $(function () { }; var bugLinkFormatter = function(o) { + if (!o.data.bug_id) { + return '-'; + } var bug_closed = ""; if (o.data.bug_status == 'RESOLVED' || o.data.bug_status == 'VERIFIED') { bug_closed = "bz_closed"; } - return '' + o.value + ''; + Y.Escape.html(o.data.bug_summary) + '" class="' + bug_closed + + '">' + o.data.bug_id + ''; }; var updatedFormatter = function(o) { @@ -124,6 +138,85 @@ $(function () { } }; + var phabAuthorFormatter = function(o) { + return '' + + Y.Escape.html(o.data.author_name) + ''; + }; + + var phabRowFormatter = function(o) { + var row = o.cell.ancestor(); + + // space in the 'flags' tables is tight + // render requests as two rows - diff title on first row, columns + // on second + + row.insert( + '' + + '' + + '' + + Y.Escape.html('D' + o.data.id + ' - ' + o.data.title) + + '', + 'before'); + + o.cell.set('text', o.data.status == 'added' ? 'pending' : o.data.status); + + return false; + }; + + // Reviews + if (hasReviews) { + dataSource.reviews = new Y.DataSource.IO({ source: 'jsonrpc.cgi' }); + dataSource.reviews.on('error', function(e) { + console.log(e); + try { + var response = Y.JSON.parse(e.data.responseText); + if (response.error) + e.error.message = response.error.message; + } catch(ex) { + // ignore + } + }); + dataTable.reviews = new Y.DataTable({ + columns: [ + { key: 'author_email', label: 'Requester', sortable: true, + formattter: phabAuthorFormatter, allowHTML: true }, + { key: 'id', label: 'Status', sortable: true, + nodeFormatter: phabRowFormatter, allowHTML: true }, + { key: 'bug_id', label: 'Bug', sortable: true, + formatter: bugLinkFormatter, allowHTML: true }, + { key: 'updated', label: 'Updated', sortable: true, + formatter: updatedFormatter, allowHTML: true } + ], + strings: { + emptyMessage: 'No review requests.', + } + }); + + dataTable.reviews.plug(Y.Plugin.DataTableSort); + + dataTable.reviews.plug(Y.Plugin.DataTableDataSource, { + datasource: dataSource.reviews + }); + + dataSource.reviews.plug(Y.Plugin.DataSourceJSONSchema, { + schema: { + resultListLocator: 'result.result', + resultFields: [ 'author_email', 'author_name', 'bug_id', + 'bug_status', 'bug_summary', 'id', 'status', 'title', + 'updated', 'updated_fancy', 'url' ] + } + }); + + dataTable.reviews.render("#reviews_table"); + + Y.one('#reviews_refresh').on('click', function(e) { + updateRequestsTable('reviews'); + }); + Y.one('#reviews_buglist').on('click', function(e) { + loadBugList('reviews'); + }); + } + // Requestee dataSource.requestee = new Y.DataSource.IO({ source: 'jsonrpc.cgi' }); dataSource.requestee.on('error', function(e) { @@ -146,7 +239,7 @@ $(function () { formatter: updatedFormatter, allowHTML: true } ], strings: { - emptyMessage: 'No flag data found.', + emptyMessage: 'No flags requested of you.', } }); @@ -167,7 +260,7 @@ $(function () { dataTable.requestee.render("#requestee_table"); Y.one('#requestee_refresh').on('click', function(e) { - updateFlagTable('requestee'); + updateRequestsTable('requestee'); }); Y.one('#requestee_buglist').on('click', function(e) { loadBugList('requestee'); @@ -196,7 +289,7 @@ $(function () { formatter: updatedFormatter, allowHTML: true } ], strings: { - emptyMessage: 'No flag data found.', + emptyMessage: 'No requested flags found.', } }); @@ -214,19 +307,24 @@ $(function () { } }); - // Initial load - Y.on("contentready", function (e) { - updateFlagTable("requestee"); - }, "#requestee_table"); - Y.on("contentready", function (e) { - updateFlagTable("requester"); - }, "#requester_table"); - Y.one('#requester_refresh').on('click', function(e) { - updateFlagTable('requester'); + updateRequestsTable('requester'); }); Y.one('#requester_buglist').on('click', function(e) { loadBugList('requester'); }); + + // Initial load + if (hasReviews) { + Y.on("contentready", function (e) { + updateRequestsTable('reviews'); + }, "#reviews_table"); + } + Y.on("contentready", function (e) { + updateRequestsTable("requestee"); + }, "#requestee_table"); + Y.on("contentready", function (e) { + updateRequestsTable("requester"); + }, "#requester_table"); }); }); diff --git a/extensions/MyDashboard/web/js/query.js b/extensions/MyDashboard/web/js/query.js index a95c0be617..e5e0979a16 100644 --- a/extensions/MyDashboard/web/js/query.js +++ b/extensions/MyDashboard/web/js/query.js @@ -77,6 +77,7 @@ $(function() { var bugQueryCallback = { success: function(e) { if (e.response) { + Y.one('#query_loading').addClass('bz_default_hidden'); Y.one('#query_count_refresh').removeClass('bz_default_hidden'); Y.one("#query_container .query_description").setHTML(e.response.meta.description); Y.one("#query_container .query_heading").setHTML(e.response.meta.heading); @@ -100,6 +101,8 @@ $(function() { } }, failure: function(o) { + Y.one('#query_loading').addClass('bz_default_hidden'); + Y.one('#query_count_refresh').removeClass('bz_default_hidden'); if (o.error) { alert("Failed to load bug list from Bugzilla:\n\n" + o.error.message); } else { @@ -114,6 +117,7 @@ $(function() { counter = counter + 1; lastChangesCache = {}; + Y.one('#query_loading').removeClass('bz_default_hidden'); Y.one('#query_count_refresh').addClass('bz_default_hidden'); bugQueryTable.set('data', []); bugQueryTable.render("#query_table"); @@ -173,6 +177,9 @@ $(function() { { key: "bug_status", label: "Status", sortable: true }, { key: "short_desc", label: "Summary", sortable: true }, ], + strings: { + emptyMessage: 'Zarro Boogs found' + } }); var last_changes_source = Y.one('#last-changes-template').getHTML(), diff --git a/extensions/MyDashboard/web/styles/mydashboard.css b/extensions/MyDashboard/web/styles/mydashboard.css index 1011a91431..ef34bb100f 100644 --- a/extensions/MyDashboard/web/styles/mydashboard.css +++ b/extensions/MyDashboard/web/styles/mydashboard.css @@ -18,10 +18,13 @@ white-space: nowrap; } +#mydashboard .requests { + margin-bottom: 2em; +} + .query_heading { font-size: 18px; font-weight: 600; - margin: 5px 0; color: rgb(72, 72, 72); } diff --git a/extensions/PhabBugz/lib/Util.pm b/extensions/PhabBugz/lib/Util.pm index a640f52a1f..0f93512851 100644 --- a/extensions/PhabBugz/lib/Util.pm +++ b/extensions/PhabBugz/lib/Util.pm @@ -21,20 +21,22 @@ use Bugzilla::Extension::PhabBugz::Constants; use JSON::XS qw(encode_json decode_json); use List::Util qw(first); use LWP::UserAgent; +use Taint::Util qw(untaint); use base qw(Exporter); -our @EXPORT = qw( +our @EXPORT_OK = qw( add_comment_to_revision add_security_sync_comments - create_revision_attachment create_private_revision_policy create_project + create_revision_attachment edit_revision_policy get_attachment_revisions get_bug_role_phids get_members_by_bmo_id get_members_by_phid + get_needs_review get_phab_bmo_ids get_project_phid get_revisions_by_ids @@ -490,7 +492,12 @@ sub request { if $response->is_error; my $result; - my $result_ok = eval { $result = decode_json( $response->content); 1 }; + my $result_ok = eval { + my $content = $response->content; + untaint($content); + $result = decode_json( $content ); + 1; + }; if (!$result_ok || $result->{error_code}) { ThrowCodeError('phabricator_api_error', { reason => 'JSON decode failure' }) if !$result_ok; @@ -548,4 +555,41 @@ sub add_security_sync_comments { Bugzilla->set_user($old_user); } +sub get_needs_review { + my ($user) = @_; + $user //= Bugzilla->user; + return unless $user->id; + + my $ids = get_members_by_bmo_id([$user]); + return [] unless @$ids; + my $phid_user = $ids->[0]; + + my $diffs = request( + 'differential.revision.search', + { + attachments => { + reviewers => 1, + }, + constraints => { + reviewerPHIDs => [$phid_user], + statuses => [qw( needs-review )], + }, + order => 'newest', + } + ); + ThrowCodeError('phabricator_api_error', { reason => 'Malformed Response' }) + unless exists $diffs->{result}{data}; + + # extract this reviewer's status from 'attachments' + my @result; + foreach my $diff (@{ $diffs->{result}{data} }) { + my $attachments = delete $diff->{attachments}; + my $reviewers = $attachments->{reviewers}{reviewers}; + my $review = first { $_->{reviewerPHID} eq $phid_user } @$reviewers; + $diff->{fields}{review_status} = $review->{status}; + push @result, $diff; + } + return \@result; +} + 1; diff --git a/extensions/PhabBugz/lib/WebService.pm b/extensions/PhabBugz/lib/WebService.pm index 5b6310de6c..adf127a1f1 100644 --- a/extensions/PhabBugz/lib/WebService.pm +++ b/extensions/PhabBugz/lib/WebService.pm @@ -20,34 +20,42 @@ use Bugzilla::Constants; use Bugzilla::Error; use Bugzilla::Extension::Push::Util qw(is_public); use Bugzilla::User; -use Bugzilla::Util qw(detaint_natural); +use Bugzilla::Util qw(detaint_natural datetime_from time_ago); use Bugzilla::WebService::Constants; use Bugzilla::Extension::PhabBugz::Constants; use Bugzilla::Extension::PhabBugz::Util qw( add_security_sync_comments - create_revision_attachment create_private_revision_policy + create_revision_attachment edit_revision_policy get_bug_role_phids + get_phab_bmo_ids + get_needs_review get_project_phid get_revisions_by_ids + get_security_sync_groups intersect is_attachment_phab_revision make_revision_public request - get_security_sync_groups ); -use List::Util qw(first); +use DateTime (); +use List::Util qw(first uniq); use List::MoreUtils qw(any); use MIME::Base64 qw(decode_base64); +use constant READ_ONLY => qw( + needs_review +); + use constant PUBLIC_METHODS => qw( check_user_permission_for_bug obsolete_attachments revision update_reviewer_statuses + needs_review ); sub revision { @@ -291,6 +299,96 @@ sub obsolete_attachments { return { result => \@updated_attach_ids }; } +sub needs_review { + my ($self, $params) = @_; + ThrowUserError('phabricator_not_enabled') + unless Bugzilla->params->{phabricator_enabled}; + my $user = Bugzilla->login(LOGIN_REQUIRED); + my $dbh = Bugzilla->dbh; + + my $reviews = get_needs_review(); + + # map author phids to bugzilla users + my $author_id_map = get_phab_bmo_ids({ + phids => [ + uniq + grep { defined } + map { $_->{fields}{authorPHID} } + @$reviews + ] + }); + my %author_phab_to_id = map { $_->{phid} => $_->{id} } @$author_id_map; + my $author_users = Bugzilla::User->new_from_list([ map { $_->{id} } @$author_id_map ]); + my %author_id_to_user = map { $_->id => $_ } @$author_users; + + # bug data + my $visible_bugs = $user->visible_bugs([ + uniq + grep { $_ } + map { $_->{fields}{'bugzilla.bug-id'} } + @$reviews + ]); + + # get all bug statuses and summaries in a single query to avoid creation of + # many bug objects + my %bugs; + if (@$visible_bugs) { + #<<< + my $bug_rows =$dbh->selectall_arrayref( + 'SELECT bug_id, bug_status, short_desc ' . + ' FROM bugs ' . + ' WHERE bug_id IN (' . join(',', ('?') x @$visible_bugs) . ')', + { Slice => {} }, + @$visible_bugs + ); + #>>> + %bugs = map { $_->{bug_id} => $_ } @$bug_rows; + } + + # build result + my $datetime_now = DateTime->now(time_zone => $user->timezone); + my @result; + foreach my $review (@$reviews) { + my $review_flat = { + id => $review->{id}, + status => $review->{fields}{review_status}, + title => $review->{fields}{title}, + url => Bugzilla->params->{phabricator_base_uri} . 'D' . $review->{id}, + }; + + # show date in user's timezone + my $datetime = DateTime->from_epoch( + epoch => $review->{fields}{dateModified}, + time_zone => 'UTC' + ); + $datetime->set_time_zone($user->timezone); + $review_flat->{updated} = $datetime->strftime('%Y-%m-%d %T %Z'); + $review_flat->{updated_fancy} = time_ago($datetime, $datetime_now); + + # review requester + if (my $author = $author_id_to_user{$author_phab_to_id{ $review->{fields}{authorPHID} }}) { + $review_flat->{author_name} = $author->name; + $review_flat->{author_email} = $author->email; + } + else { + $review_flat->{author_name} = 'anonymous'; + $review_flat->{author_email} = 'anonymous'; + } + + # referenced bug + if (my $bug_id = $review->{fields}{'bugzilla.bug-id'}) { + my $bug = $bugs{$bug_id}; + $review_flat->{bug_id} = $bug_id; + $review_flat->{bug_status} = $bug->{bug_status}; + $review_flat->{bug_summary} = $bug->{short_desc}; + } + + push @result, $review_flat; + } + + return { result => \@result }; +} + sub _phabricator_precheck { my ($user) = @_; @@ -334,7 +432,13 @@ sub rest_resources { PUT => { method => 'obsolete_attachments', } - } + }, + # Review requests + qw{^/phabbugz/needs_review$}, { + GET => { + method => 'needs_review', + }, + }, ]; } diff --git a/t/008filter.t b/t/008filter.t index cae1e68803..443fb2b4f0 100644 --- a/t/008filter.t +++ b/t/008filter.t @@ -147,6 +147,9 @@ sub directive_ok { $directive =~ s/^\s*//; $directive =~ s/\s*$//; + # Ignore blocks explicitly marked as ok + return 1 if $directive =~ /\b## no-008filter\b/; + # Empty directives are ok; they are usually line break helpers return 1 if $directive eq ''; From d9b2390aea6abf3074c7949b46531ecb9f93aa3f Mon Sep 17 00:00:00 2001 From: dklawren Date: Fri, 20 Apr 2018 15:40:05 -0400 Subject: [PATCH 021/119] Bug 1452241 - Improve feed error handling and logging --- extensions/PhabBugz/lib/Feed.pm | 76 +++++++++++++++++++-------------- 1 file changed, 43 insertions(+), 33 deletions(-) diff --git a/extensions/PhabBugz/lib/Feed.pm b/extensions/PhabBugz/lib/Feed.pm index 2904e9dede..1907b86d7f 100644 --- a/extensions/PhabBugz/lib/Feed.pm +++ b/extensions/PhabBugz/lib/Feed.pm @@ -86,6 +86,8 @@ sub start { sub feed_query { my ($self) = @_; + Bugzilla::Logging->fields->{type} = 'FEED'; + # Ensure Phabricator syncing is enabled if (!Bugzilla->params->{phabricator_enabled}) { INFO("PHABRICATOR SYNC DISABLED"); @@ -94,13 +96,13 @@ sub feed_query { # PROCESS NEW FEED TRANSACTIONS - INFO("FEED: Fetching new transactions"); + INFO("Fetching new transactions"); my $story_last_id = $self->get_last_id('feed'); # Check for new transctions (stories) my $new_stories = $self->new_stories($story_last_id); - INFO("FEED: No new stories") unless @$new_stories; + INFO("No new stories") unless @$new_stories; # Process each story foreach my $story_data (@$new_stories) { @@ -110,15 +112,15 @@ sub feed_query { my $object_phid = $story_data->{objectPHID}; my $story_text = $story_data->{text}; - DEBUG("STORY ID: $story_id"); - DEBUG("STORY PHID: $story_phid"); - DEBUG("AUTHOR PHID: $author_phid"); - DEBUG("OBJECT PHID: $object_phid"); + TRACE("STORY ID: $story_id"); + TRACE("STORY PHID: $story_phid"); + TRACE("AUTHOR PHID: $author_phid"); + TRACE("OBJECT PHID: $object_phid"); INFO("STORY TEXT: $story_text"); # Only interested in changes to revisions for now. if ($object_phid !~ /^PHID-DREV/) { - DEBUG("SKIPPING: Not a revision change"); + INFO("SKIPPING: Not a revision change"); $self->save_last_id($story_id, 'feed'); next; } @@ -128,7 +130,7 @@ sub feed_query { if (@$phab_users) { my $user = Bugzilla::User->new({ id => $phab_users->[0]->{id}, cache => 1 }); if ($user->login eq PHAB_AUTOMATION_USER) { - DEBUG("SKIPPING: Change made by phabricator user"); + INFO("SKIPPING: Change made by phabricator user"); $self->save_last_id($story_id, 'feed'); next; } @@ -144,6 +146,8 @@ sub feed_query { sub user_query { my ( $self ) = @_; + Bugzilla::Logging->fields->{type} = 'USERS'; + # Ensure Phabricator syncing is enabled if (!Bugzilla->params->{phabricator_enabled}) { INFO("PHABRICATOR SYNC DISABLED"); @@ -152,13 +156,13 @@ sub user_query { # PROCESS NEW USERS - INFO("USERS: Fetching new users"); + INFO("Fetching new users"); my $user_last_id = $self->get_last_id('user'); # Check for new users my $new_users = $self->new_users($user_last_id); - INFO("FEED: No new users") unless @$new_users; + INFO("No new users") unless @$new_users; # Process each new user foreach my $user_data (@$new_users) { @@ -167,10 +171,10 @@ sub user_query { my $user_realname = $user_data->{fields}{realName}; my $object_phid = $user_data->{phid}; - DEBUG("USER ID: $user_id"); - DEBUG("USER LOGIN: $user_login"); - DEBUG("USER REALNAME: $user_realname"); - DEBUG("OBJECT PHID: $object_phid"); + TRACE("ID: $user_id"); + TRACE("LOGIN: $user_login"); + TRACE("REALNAME: $user_realname"); + TRACE("OBJECT PHID: $object_phid"); with_readonly_database { $self->process_new_user($user_data); @@ -190,7 +194,7 @@ sub process_revision_change { if (!$revision->bug_id) { if ($story_text =~ /\s+created\s+D\d+/) { # If new revision and bug id was omitted, make revision public - DEBUG("No bug associated with new revision. Marking public."); + INFO("No bug associated with new revision. Marking public."); $revision->set_policy('view', 'public'); $revision->set_policy('edit', 'users'); $revision->update(); @@ -198,7 +202,7 @@ sub process_revision_change { return; } else { - DEBUG("SKIPPING: No bug associated with revision change"); + INFO("SKIPPING: No bug associated with revision change"); return; } } @@ -219,7 +223,7 @@ sub process_revision_change { # If bug is public then remove privacy policy if (!@{ $bug->groups_in }) { - DEBUG('Bug is public so setting view/edit public'); + INFO('Bug is public so setting view/edit public'); $revision->set_policy('view', 'public'); $revision->set_policy('edit', 'users'); my $secure_project_phid = get_project_phid('secure-revision'); @@ -232,7 +236,7 @@ sub process_revision_change { # If bug privacy groups do not have any matching synchronized groups, # then leave revision private and it will have be dealt with manually. if (!@set_groups) { - DEBUG('No matching groups. Adding comments to bug and revision'); + INFO('No matching groups. Adding comments to bug and revision'); add_security_sync_comments([$revision], $bug); } # Otherwise, we create a new custom policy containing the project @@ -244,23 +248,24 @@ sub process_revision_change { # we leave the current policy alone. my $current_policy; if ($revision->view_policy =~ /^PHID-PLCY/) { - DEBUG("Loading current policy: " . $revision->view_policy); + INFO("Loading current policy: " . $revision->view_policy); $current_policy = Bugzilla::Extension::PhabBugz::Policy->new_from_query({ phids => [ $revision->view_policy ]}); my $current_projects = $current_policy->rule_projects; - DEBUG("Current policy projects: " . join(", ", @$current_projects)); + INFO("Current policy projects: " . join(", ", @$current_projects)); my ($added, $removed) = diff_arrays($current_projects, \@set_projects); if (@$added || @$removed) { - DEBUG('Project groups do not match. Need new custom policy'); + INFO('Project groups do not match. Need new custom policy'); $current_policy = undef; + } else { - DEBUG('Project groups match. Leaving current policy as-is'); + INFO('Project groups match. Leaving current policy as-is'); } } if (!$current_policy) { - DEBUG("Creating new custom policy: " . join(", ", @set_projects)); + INFO("Creating new custom policy: " . join(", ", @set_projects)); my $new_policy = Bugzilla::Extension::PhabBugz::Policy->create(\@set_projects); $revision->set_policy('view', $new_policy->phid); $revision->set_policy('edit', $new_policy->phid); @@ -291,11 +296,11 @@ sub process_revision_change { next if $attach_revision_id != $revision->id; my $make_obsolete = $revision->status eq 'abandoned' ? 1 : 0; - DEBUG('Updating obsolete status on attachmment ' . $attachment->id); + INFO('Updating obsolete status on attachmment ' . $attachment->id); $attachment->set_is_obsolete($make_obsolete); if ($revision->title ne $attachment->description) { - DEBUG('Updating description on attachment ' . $attachment->id); + INFO('Updating description on attachment ' . $attachment->id); $attachment->set_description($revision->title); } @@ -311,8 +316,8 @@ sub process_revision_change { }); foreach my $attachment (@$other_attachments) { $other_bugs{$attachment->bug_id}++; - DEBUG('Updating obsolete status on attachment ' . - $attachment->id . " for bug " . $attachment->bug_id); + INFO('Updating obsolete status on attachment ' . + $attachment->id . " for bug " . $attachment->bug_id); $attachment->set_is_obsolete(1); $attachment->update($timestamp); } @@ -383,6 +388,7 @@ sub process_revision_change { if ($comment) { $comment .= "\n" . Bugzilla->params->{phabricator_base_uri} . "D" . $revision->id; + INFO("Flag comment: $comment"); # Add transaction_id as anchor if one present # $comment .= "#" . $params->{transaction_id} if $params->{transaction_id}; $bug->add_comment($comment, { @@ -419,7 +425,7 @@ sub process_new_user { my $phab_user = Bugzilla::Extension::PhabBugz::User->new($user_data); if (!$phab_user->bugzilla_id) { - DEBUG("SKIPPING: No bugzilla id associated with user"); + WARN("SKIPPING: No bugzilla id associated with user"); return; } @@ -472,7 +478,7 @@ sub process_new_user { my @bug_ids = map { shift @$_ } @$data; foreach my $bug_id (@bug_ids) { - DEBUG("Processing bug $bug_id"); + INFO("Processing bug $bug_id"); my $bug = Bugzilla::Bug->new({ id => $bug_id, cache => 1 }); @@ -481,7 +487,7 @@ sub process_new_user { foreach my $attachment (@attachments) { my ($revision_id) = ($attachment->filename =~ PHAB_ATTACHMENT_PATTERN); - DEBUG("Processing revision D$revision_id"); + INFO("Processing revision D$revision_id"); my $revision = Bugzilla::Extension::PhabBugz::Revision->new_from_query( { ids => [ int($revision_id) ] }); @@ -489,7 +495,7 @@ sub process_new_user { $revision->add_subscriber($phab_user->phid); $revision->update(); - DEBUG("Revision $revision_id updated"); + INFO("Revision $revision_id updated"); } } @@ -506,7 +512,9 @@ sub new_stories { my ( $self, $after ) = @_; my $data = { view => 'text' }; $data->{after} = $after if $after; + my $result = request( 'feed.query_id', $data ); + unless ( ref $result->{result}{data} eq 'ARRAY' && @{ $result->{result}{data} } ) { @@ -526,7 +534,9 @@ sub new_users { } }; $data->{before} = $after if $after; + my $result = request( 'user.search', $data ); + unless ( ref $result->{result}{data} eq 'ARRAY' && @{ $result->{result}{data} } ) { @@ -543,7 +553,7 @@ sub get_last_id { my $last_id = Bugzilla->dbh->selectrow_array( " SELECT value FROM phabbugz WHERE name = ?", undef, $type_full ); $last_id ||= 0; - DEBUG( "QUERY " . uc($type_full) . ": $last_id" ); + TRACE(uc($type_full) . ": $last_id" ); return $last_id; } @@ -552,7 +562,7 @@ sub save_last_id { # Store the largest last key so we can start from there in the next session my $type_full = $type . "_last_id"; - DEBUG( "UPDATING " . uc($type_full) . ": $last_id" ); + TRACE("UPDATING " . uc($type_full) . ": $last_id" ); Bugzilla->dbh->do( "REPLACE INTO phabbugz (name, value) VALUES (?, ?)", undef, $type_full, $last_id ); } From d4dd9d2e3e15497cbcb565a05b0c3a0a4a8ab542 Mon Sep 17 00:00:00 2001 From: dklawren Date: Fri, 20 Apr 2018 16:01:16 -0400 Subject: [PATCH 022/119] Bug 1453124 - extensions/PhabBugz/bin/update_project_members.pl should be combined with the normal feed daemon --- .../PhabBugz/bin/update_project_members.pl | 99 --------------- extensions/PhabBugz/lib/Constants.pm | 6 +- extensions/PhabBugz/lib/Feed.pm | 114 +++++++++++++++++- extensions/PhabBugz/lib/Project.pm | 2 +- 4 files changed, 115 insertions(+), 106 deletions(-) delete mode 100755 extensions/PhabBugz/bin/update_project_members.pl diff --git a/extensions/PhabBugz/bin/update_project_members.pl b/extensions/PhabBugz/bin/update_project_members.pl deleted file mode 100755 index fe62170a6b..0000000000 --- a/extensions/PhabBugz/bin/update_project_members.pl +++ /dev/null @@ -1,99 +0,0 @@ -#!/usr/bin/perl - -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. -# -# This Source Code Form is "Incompatible With Secondary Licenses", as -# defined by the Mozilla Public License, v. 2.0. - -use 5.10.1; -use strict; -use warnings; - -use lib qw(. lib local/lib/perl5); - -use Bugzilla; -BEGIN { Bugzilla->extensions() } - -use Bugzilla::Constants; -use Bugzilla::Error; -use Bugzilla::Group; - -use Bugzilla::Extension::PhabBugz::Project; -use Bugzilla::Extension::PhabBugz::Util qw( - get_phab_bmo_ids -); - -Bugzilla->usage_mode(USAGE_MODE_CMDLINE); - -my ($phab_uri, $phab_sync_groups); - -if (!Bugzilla->params->{phabricator_enabled}) { - exit; -} - -# Sanity checks -unless ($phab_uri = Bugzilla->params->{phabricator_base_uri}) { - ThrowUserError('invalid_phabricator_uri'); -} - -unless ($phab_sync_groups = Bugzilla->params->{phabricator_sync_groups}) { - ThrowUserError('invalid_phabricator_sync_groups'); -} - -# Loop through each group and perform the following: -# -# 1. Load flattened list of group members -# 2. Check to see if Phab project exists for 'bmo-' -# 3. Create if does not exist with locked down policy. -# 4. Set project members to exact list -# 5. Profit - -my $sync_groups = Bugzilla::Group->match({ name => [ split('[,\s]+', $phab_sync_groups) ] }); - -foreach my $group (@$sync_groups) { - # Create group project if one does not yet exist - my $phab_project_name = 'bmo-' . $group->name; - my $project = Bugzilla::Extension::PhabBugz::Project->new_from_query({ - name => $phab_project_name - }); - if (!$project) { - my $secure_revision = Bugzilla::Extension::PhabBugz::Project->new_from_query({ - name => 'secure-revision' - }); - $project = Bugzilla::Extension::PhabBugz::Project->create({ - name => $phab_project_name, - description => 'BMO Security Group for ' . $group->name, - view_policy => $secure_revision->phid, - edit_policy => $secure_revision->phid, - join_policy => $secure_revision->phid - }); - } - - if (my @group_members = get_group_members($group)) { - $project->set_members(\@group_members); - $project->update(); - } -} - -sub get_group_members { - my ($group) = @_; - my $group_obj = ref $group ? $group : Bugzilla::Group->check({ name => $group }); - my $members_all = $group_obj->members_complete(); - my %users; - foreach my $name (keys %$members_all) { - foreach my $user (@{ $members_all->{$name} }) { - $users{$user->id} = $user; - } - } - - # Look up the phab ids for these users - my $phab_users = get_phab_bmo_ids({ ids => [ keys %users ] }); - foreach my $phab_user (@{ $phab_users }) { - $users{$phab_user->{id}}->{phab_phid} = $phab_user->{phid}; - } - - # We only need users who have accounts in phabricator - return grep { $_->phab_phid } values %users; -} \ No newline at end of file diff --git a/extensions/PhabBugz/lib/Constants.pm b/extensions/PhabBugz/lib/Constants.pm index 2fd8613a07..1692f8fb98 100644 --- a/extensions/PhabBugz/lib/Constants.pm +++ b/extensions/PhabBugz/lib/Constants.pm @@ -18,12 +18,14 @@ our @EXPORT = qw( PHAB_CONTENT_TYPE PHAB_FEED_POLL_SECONDS PHAB_USER_POLL_SECONDS + PHAB_GROUP_POLL_SECONDS ); use constant PHAB_ATTACHMENT_PATTERN => qr/^phabricator-D(\d+)/; use constant PHAB_AUTOMATION_USER => 'phab-bot@bmo.tld'; use constant PHAB_CONTENT_TYPE => 'text/x-phabricator-request'; -use constant PHAB_FEED_POLL_SECONDS => 5; -use constant PHAB_USER_POLL_SECONDS => 60; +use constant PHAB_FEED_POLL_SECONDS => $ENV{PHAB_FEED_POLL} // 5; +use constant PHAB_USER_POLL_SECONDS => $ENV{PHAB_USER_POLL} // 60; +use constant PHAB_GROUP_POLL_SECONDS => $ENV{PHAB_GROUP_POLL} // 300; 1; diff --git a/extensions/PhabBugz/lib/Feed.pm b/extensions/PhabBugz/lib/Feed.pm index 1907b86d7f..275d71976c 100644 --- a/extensions/PhabBugz/lib/Feed.pm +++ b/extensions/PhabBugz/lib/Feed.pm @@ -48,7 +48,7 @@ sub start { first_interval => 0, interval => PHAB_FEED_POLL_SECONDS, reschedule => 'drift', - on_tick => sub { + on_tick => sub { try{ $self->feed_query(); } @@ -64,7 +64,7 @@ sub start { first_interval => 0, interval => PHAB_USER_POLL_SECONDS, reschedule => 'drift', - on_tick => sub { + on_tick => sub { try{ $self->user_query(); } @@ -75,11 +75,29 @@ sub start { }, ); + # Update project membership in Phabricator based on Bugzilla groups + my $group_timer = IO::Async::Timer::Periodic->new( + first_interval => 0, + interval => PHAB_GROUP_POLL_SECONDS, + reschedule => 'drift', + on_tick => sub { + try{ + $self->group_query(); + } + catch { + FATAL($_); + }; + Bugzilla->_cleanup(); + }, + ); + my $loop = IO::Async::Loop->new; $loop->add($feed_timer); $loop->add($user_timer); + $loop->add($group_timer); $feed_timer->start; $user_timer->start; + $group_timer->start; $loop->run; } @@ -90,7 +108,7 @@ sub feed_query { # Ensure Phabricator syncing is enabled if (!Bugzilla->params->{phabricator_enabled}) { - INFO("PHABRICATOR SYNC DISABLED"); + WARN("PHABRICATOR SYNC DISABLED"); return; } @@ -150,7 +168,7 @@ sub user_query { # Ensure Phabricator syncing is enabled if (!Bugzilla->params->{phabricator_enabled}) { - INFO("PHABRICATOR SYNC DISABLED"); + WARN("PHABRICATOR SYNC DISABLED"); return; } @@ -183,6 +201,72 @@ sub user_query { } } +sub group_query { + my ($self) = @_; + + # Ensure Phabricator syncing is enabled + if ( !Bugzilla->params->{phabricator_enabled} ) { + WARN("PHABRICATOR SYNC DISABLED"); + return; + } + + my $phab_sync_groups = Bugzilla->params->{phabricator_sync_groups}; + if ( !$phab_sync_groups ) { + WARN('A comma delimited list of security groups was not provided.'); + return; + } + + # PROCESS SECURITY GROUPS + + INFO("GROUPS: Updating group memberships"); + + # Loop through each group and perform the following: + # + # 1. Load flattened list of group members + # 2. Check to see if Phab project exists for 'bmo-' + # 3. Create if does not exist with locked down policy. + # 4. Set project members to exact list + # 5. Profit + + my $sync_groups = Bugzilla::Group->match( + { name => [ split( '[,\s]+', $phab_sync_groups ) ] } ); + + foreach my $group (@$sync_groups) { + + # Create group project if one does not yet exist + my $phab_project_name = 'bmo-' . $group->name; + my $project = Bugzilla::Extension::PhabBugz::Project->new_from_query( + { + name => $phab_project_name + } + ); + if ( !$project ) { + INFO("Project $project not found. Creating."); + my $secure_revision = + Bugzilla::Extension::PhabBugz::Project->new_from_query( + { + name => 'secure-revision' + } + ); + $project = Bugzilla::Extension::PhabBugz::Project->create( + { + name => $phab_project_name, + description => 'BMO Security Group for ' . $group->name, + view_policy => $secure_revision->phid, + edit_policy => $secure_revision->phid, + join_policy => $secure_revision->phid + } + ); + } + + if ( my @group_members = get_group_members($group) ) { + INFO("Setting group members for " . $project->name); + $project->set_members( \@group_members ); + $project->update(); + } + } +} + sub process_revision_change { my ($self, $revision_phid, $story_text) = @_; @@ -567,4 +651,26 @@ sub save_last_id { undef, $type_full, $last_id ); } +sub get_group_members { + my ($group) = @_; + my $group_obj = + ref $group ? $group : Bugzilla::Group->check( { name => $group, cache => 1 } ); + my $members_all = $group_obj->members_complete(); + my %users; + foreach my $name ( keys %$members_all ) { + foreach my $user ( @{ $members_all->{$name} } ) { + $users{ $user->id } = $user; + } + } + + # Look up the phab ids for these users + my $phab_users = get_phab_bmo_ids( { ids => [ keys %users ] } ); + foreach my $phab_user ( @{$phab_users} ) { + $users{ $phab_user->{id} }->{phab_phid} = $phab_user->{phid}; + } + + # We only need users who have accounts in phabricator + return grep { $_->phab_phid } values %users; +} + 1; diff --git a/extensions/PhabBugz/lib/Project.pm b/extensions/PhabBugz/lib/Project.pm index 0fb9ecaa52..cbf1bdcafd 100644 --- a/extensions/PhabBugz/lib/Project.pm +++ b/extensions/PhabBugz/lib/Project.pm @@ -168,7 +168,7 @@ sub create { my $result = request( 'project.edit', $data ); return $class->new_from_query( - { phids => $result->{result}{object}{phid} } ); + { phids => [ $result->{result}{object}{phid} ] } ); } sub update { From 58648590b14ea5c04c97c1ab12e8c296ed22b20c Mon Sep 17 00:00:00 2001 From: Dylan William Hardison Date: Fri, 20 Apr 2018 16:57:10 -0400 Subject: [PATCH 023/119] Bug 1455493 - cleanup push connector logging --- extensions/Push/lib/Backoff.pm | 9 ++++----- extensions/Push/lib/Config.pm | 5 ++--- extensions/Push/lib/Connectors.pm | 28 ++++++++++++---------------- extensions/Push/lib/Push.pm | 3 ++- 4 files changed, 20 insertions(+), 25 deletions(-) diff --git a/extensions/Push/lib/Backoff.pm b/extensions/Push/lib/Backoff.pm index f0116a2a7b..0436cdf145 100644 --- a/extensions/Push/lib/Backoff.pm +++ b/extensions/Push/lib/Backoff.pm @@ -19,6 +19,7 @@ use constant AUDIT_REMOVES => 0; use constant USE_MEMCACHED => 0; use Bugzilla; +use Bugzilla::Logging; use Bugzilla::Util; # @@ -67,9 +68,7 @@ sub reset { my ($self) = @_; $self->{next_attempt_ts} = Bugzilla->dbh->selectrow_array('SELECT NOW()'); $self->{attempts} = 0; - Bugzilla->push_ext->logger->debug( - sprintf("resetting backoff for %s", $self->connector) - ); + INFO( sprintf 'resetting backoff for %s', $self->connector ); } sub inc { @@ -82,8 +81,8 @@ sub inc { $self->{next_attempt_ts} = $date; $self->{attempts} = $attempts; - Bugzilla->push_ext->logger->debug( - sprintf("setting next attempt for %s to %s (attempt %s)", $self->connector, $date, $attempts) + INFO( + sprintf 'setting next attempt for %s to %s (attempt %s)', $self->connector, $date, $attempts ); } diff --git a/extensions/Push/lib/Config.pm b/extensions/Push/lib/Config.pm index 337c2696d6..2db95b972d 100644 --- a/extensions/Push/lib/Config.pm +++ b/extensions/Push/lib/Config.pm @@ -11,7 +11,7 @@ use 5.10.1; use strict; use warnings; -use Bugzilla; +use Bugzilla::Logging; use Bugzilla::Constants; use Bugzilla::Extension::Push::Option; use Crypt::CBC; @@ -52,7 +52,6 @@ sub option { sub load { my ($self) = @_; my $config = {}; - my $logger = Bugzilla->push_ext->logger; # prime $config with defaults foreach my $rh ($self->options) { @@ -81,7 +80,7 @@ sub load { # done, update self foreach my $name (keys %$config) { my $value = $self->option($name)->{type} eq 'password' ? '********' : $config->{$name}; - $logger->debug(sprintf("%s: set %s=%s\n", $self->{_name}, $name, $value || '')); + TRACE( sprintf "%s: set %s=%s\n", $self->{_name}, $name, $value || '' ); $self->{$name} = $config->{$name}; } } diff --git a/extensions/Push/lib/Connectors.pm b/extensions/Push/lib/Connectors.pm index 75a5083ff5..d3c55d3ca6 100644 --- a/extensions/Push/lib/Connectors.pm +++ b/extensions/Push/lib/Connectors.pm @@ -11,10 +11,12 @@ use 5.10.1; use strict; use warnings; +use Bugzilla::Logging; use Bugzilla::Extension::Push::Util; use Bugzilla::Constants; use Bugzilla::Util qw(trick_taint); use File::Basename; +use Try::Tiny; sub new { my ($class) = @_; @@ -25,16 +27,15 @@ sub new { $self->{objects} = {}; $self->{path} = bz_locations->{'extensionsdir'} . '/Push/lib/Connector'; - my $logger = Bugzilla->push_ext->logger; foreach my $file (glob($self->{path} . '/*.pm')) { my $name = basename($file); $name =~ s/\.pm$//; next if $name eq 'Base'; if (length($name) > 32) { - $logger->info("Ignoring connector '$name': Name longer than 32 characters"); + WARN("Ignoring connector '$name': Name longer than 32 characters"); } push @{$self->{names}}, $name; - $logger->debug("Found connector '$name'"); + TRACE("Found connector '$name'"); } return $self; @@ -44,7 +45,6 @@ sub _load { my ($self) = @_; return if scalar keys %{$self->{objects}}; - my $logger = Bugzilla->push_ext->logger; foreach my $name (@{$self->{names}}) { next if exists $self->{objects}->{$name}; my $file = $self->{path} . "/$name.pm"; @@ -52,34 +52,30 @@ sub _load { require $file; my $package = "Bugzilla::Extension::Push::Connector::$name"; - $logger->debug("Loading connector '$name'"); + TRACE("Loading connector '$name'"); my $old_error_mode = Bugzilla->error_mode; Bugzilla->error_mode(ERROR_MODE_DIE); - eval { + try { my $connector = $package->new(); $connector->load_config(); $self->{objects}->{$name} = $connector; + } catch { + ERROR("Connector '$name' failed to load: " . clean_error($_)); }; - if ($@) { - $logger->error("Connector '$name' failed to load: " . clean_error($@)); - } Bugzilla->error_mode($old_error_mode); } } sub stop { my ($self) = @_; - my $logger = Bugzilla->push_ext->logger; foreach my $connector ($self->list) { next unless $connector->enabled; - $logger->debug("Stopping '" . $connector->name . "'"); - eval { + TRACE("Stopping '" . $connector->name . "'"); + try { $connector->stop(); + } catch { + ERROR("Connector '" . $connector->name . "' failed to stop: " . clean_error($_)); }; - if ($@) { - $logger->error("Connector '" . $connector->name . "' failed to stop: " . clean_error($@)); - $logger->debug("Connector '" . $connector->name . "' failed to stop: $@"); - } } } diff --git a/extensions/Push/lib/Push.pm b/extensions/Push/lib/Push.pm index 45b9b05dd5..670b2aa56d 100644 --- a/extensions/Push/lib/Push.pm +++ b/extensions/Push/lib/Push.pm @@ -11,6 +11,7 @@ use 5.10.1; use strict; use warnings; +use Bugzilla::Logging; use Bugzilla::Extension::Push::BacklogMessage; use Bugzilla::Extension::Push::Config; use Bugzilla::Extension::Push::Connectors; @@ -107,7 +108,7 @@ sub push { # if the connector is backlogged, push to the backlog queue if ($is_backlogged) { - $logger->debug("backlogged"); + INFO('connector is backlogged'); my $backlog = Bugzilla::Extension::Push::BacklogMessage->create_from_message($message, $connector); } } From 27756cc164f311eca3d16fbf6fd889dc41303ad8 Mon Sep 17 00:00:00 2001 From: Dylan William Hardison Date: Fri, 20 Apr 2018 17:20:55 -0400 Subject: [PATCH 024/119] Bug 1427395 - Allow request_cache to be constant-folded in Bugzilla.pm --- Bugzilla.pm | 152 ++++++++++++++++++++++++++-------------------------- 1 file changed, 75 insertions(+), 77 deletions(-) diff --git a/Bugzilla.pm b/Bugzilla.pm index ef6c804338..c95b3a3bad 100644 --- a/Bugzilla.pm +++ b/Bugzilla.pm @@ -81,6 +81,11 @@ use constant SHUTDOWNHTML_EXIT_SILENTLY => qw( # should search engines attempt to index the page again? use constant SHUTDOWNHTML_RETRY_AFTER => 3600; +# This is identical to Install::Util::_cache so that things loaded +# into Install::Util::_cache during installation can be read out +# of request_cache later in installation. +use constant request_cache => Bugzilla::Install::Util::_cache(); + ##################################################################### # Global Code ##################################################################### @@ -226,26 +231,25 @@ sub init_page { sub template { # BMO - use metrics subclass if required if (Bugzilla->metrics_enabled) { - $_[0]->request_cache->{template} ||= Bugzilla::Metrics::Template->create(); + request_cache->{template} ||= Bugzilla::Metrics::Template->create(); } else { - $_[0]->request_cache->{template} ||= Bugzilla::Template->create(); + request_cache->{template} ||= Bugzilla::Template->create(); } - $_[0]->request_cache->{template}->{_is_main} = 1; + request_cache->{template}->{_is_main} = 1; - return $_[0]->request_cache->{template}; + return request_cache->{template}; } sub template_inner { - my ($class, $lang) = @_; - my $cache = $class->request_cache; + my (undef, $lang) = @_; + my $cache = request_cache; my $current_lang = $cache->{template_current_lang}->[0]; $lang ||= $current_lang || ''; return $cache->{"template_inner_$lang"} ||= Bugzilla::Template->create(language => $lang); } sub extensions { - my ($class) = @_; - my $cache = $class->request_cache; + my $cache = request_cache; if (!$cache->{extensions}) { my $extension_packages = Bugzilla::Extension->load_all(); my @extensions; @@ -261,12 +265,12 @@ sub extensions { } sub cgi { - return $_[0]->request_cache->{cgi} ||= new Bugzilla::CGI(); + return request_cache->{cgi} ||= new Bugzilla::CGI(); } sub input_params { my ($class, $params) = @_; - my $cache = $class->request_cache; + my $cache = request_cache; # This is how the WebService and other places set input_params. if (defined $params) { $cache->{input_params} = $params; @@ -285,7 +289,7 @@ sub localconfig { } sub params { - return $_[0]->request_cache->{params} ||= Bugzilla::Config::read_param_file(); + return request_cache->{params} ||= Bugzilla::Config::read_param_file(); } sub get_param_with_override { @@ -294,32 +298,32 @@ sub get_param_with_override { } sub user { - return $_[0]->request_cache->{user} ||= new Bugzilla::User; + return request_cache->{user} ||= new Bugzilla::User; } sub set_user { - my ($class, $user) = @_; - $class->request_cache->{user} = $user; + my (undef, $user) = @_; + request_cache->{user} = $user; } sub sudoer { - return $_[0]->request_cache->{sudoer}; + return request_cache->{sudoer}; } sub sudo_request { - my ($class, $new_user, $new_sudoer) = @_; - $class->request_cache->{user} = $new_user; - $class->request_cache->{sudoer} = $new_sudoer; + my (undef, $new_user, $new_sudoer) = @_; + request_cache->{user} = $new_user; + request_cache->{sudoer} = $new_sudoer; # NOTE: If you want to log the start of an sudo session, do it here. } sub page_requires_login { - return $_[0]->request_cache->{page_requires_login}; + return request_cache->{page_requires_login}; } sub github_secret { my ($class) = @_; - my $cache = $class->request_cache; + my $cache = request_cache; my $cgi = $class->cgi; $cache->{github_secret} //= $cgi->cookie('github_secret') // generate_random_password(256); @@ -331,7 +335,7 @@ sub passwdqc { my ($class) = @_; require Data::Password::passwdqc; - my $cache = $class->request_cache; + my $cache = request_cache; my $params = $class->params; return $cache->{passwdqc} if $cache->{passwdqc}; @@ -380,7 +384,7 @@ sub login { # Allow templates to know that we're in a page that always requires # login. if ($type == LOGIN_REQUIRED) { - $class->request_cache->{page_requires_login} = 1; + request_cache->{page_requires_login} = 1; } my $authenticated_user = $authorizer->login($type); @@ -469,7 +473,7 @@ sub login { && !$sudo_target->in_group('bz_sudo_protect')) { $class->set_user($sudo_target); - $class->request_cache->{sudoer} = $authenticated_user; + request_cache->{sudoer} = $authenticated_user; # And make sure that both users have the same Auth object, # since we never call Auth::login for the sudo target. $sudo_target->set_authorizer($authenticated_user->authorizer); @@ -524,24 +528,25 @@ sub logout_user_by_id { # hack that invalidates credentials for a single request sub logout_request { my $class = shift; - delete $class->request_cache->{user}; - delete $class->request_cache->{sudoer}; + delete request_cache->{user}; + delete request_cache->{sudoer}; # We can't delete from $cgi->cookie, so logincookie data will remain # there. Don't rely on it: use Bugzilla->user->login instead! } sub job_queue { require Bugzilla::JobQueue; - return $_[0]->request_cache->{job_queue} ||= Bugzilla::JobQueue->new(); + return request_cache->{job_queue} ||= Bugzilla::JobQueue->new(); } sub dbh { + my ($class) = @_; # If we're not connected, then we must want the main db - return $_[0]->request_cache->{dbh} ||= $_[0]->dbh_main; + return request_cache->{dbh} ||= $class->dbh_main; } sub dbh_main { - return $_[0]->request_cache->{dbh_main} ||= Bugzilla::DB::connect_main(); + return request_cache->{dbh_main} ||= Bugzilla::DB::connect_main(); } sub languages { @@ -549,25 +554,25 @@ sub languages { } sub current_language { - return $_[0]->request_cache->{current_language} ||= (include_languages())[0]; + return request_cache->{current_language} ||= (include_languages())[0]; } sub error_mode { - my ($class, $newval) = @_; + my (undef, $newval) = @_; if (defined $newval) { - $class->request_cache->{error_mode} = $newval; + request_cache->{error_mode} = $newval; } - return $class->request_cache->{error_mode} + return request_cache->{error_mode} || (i_am_cgi() ? ERROR_MODE_WEBPAGE : ERROR_MODE_DIE); } # This is used only by Bugzilla::Error to throw errors. sub _json_server { - my ($class, $newval) = @_; + my (undef, $newval) = @_; if (defined $newval) { - $class->request_cache->{_json_server} = $newval; + request_cache->{_json_server} = $newval; } - return $class->request_cache->{_json_server}; + return request_cache->{_json_server}; } sub usage_mode { @@ -598,21 +603,21 @@ sub usage_mode { ThrowCodeError('usage_mode_invalid', {'invalid_usage_mode', $newval}); } - $class->request_cache->{usage_mode} = $newval; + request_cache->{usage_mode} = $newval; } - return $class->request_cache->{usage_mode} + return request_cache->{usage_mode} || (i_am_cgi()? USAGE_MODE_BROWSER : USAGE_MODE_CMDLINE); } sub installation_mode { - my ($class, $newval) = @_; - ($class->request_cache->{installation_mode} = $newval) if defined $newval; - return $class->request_cache->{installation_mode} + my (undef, $newval) = @_; + (request_cache->{installation_mode} = $newval) if defined $newval; + return request_cache->{installation_mode} || INSTALLATION_MODE_INTERACTIVE; } sub installation_answers { - my ($class, $filename) = @_; + my (undef, $filename) = @_; if ($filename) { my $s = new Safe; $s->rdo($filename); @@ -621,23 +626,23 @@ sub installation_answers { die "Error evaluating $filename: $@" if $@; # Now read the param back out from the sandbox - $class->request_cache->{installation_answers} = $s->varglob('answer'); + request_cache->{installation_answers} = $s->varglob('answer'); } - return $class->request_cache->{installation_answers} || {}; + return request_cache->{installation_answers} || {}; } sub switch_to_shadow_db { my $class = shift; - if (!$class->request_cache->{dbh_shadow}) { + if (!request_cache->{dbh_shadow}) { if ($class->get_param_with_override('shadowdb')) { - $class->request_cache->{dbh_shadow} = Bugzilla::DB::connect_shadow(); + request_cache->{dbh_shadow} = Bugzilla::DB::connect_shadow(); } else { - $class->request_cache->{dbh_shadow} = $class->dbh_main; + request_cache->{dbh_shadow} = $class->dbh_main; } } - $class->request_cache->{dbh} = $class->request_cache->{dbh_shadow}; + request_cache->{dbh} = request_cache->{dbh_shadow}; # we have to return $class->dbh instead of {dbh} as # {dbh_shadow} may be undefined if no shadow DB is used # and no connection to the main DB has been established yet. @@ -647,7 +652,7 @@ sub switch_to_shadow_db { sub switch_to_main_db { my $class = shift; - $class->request_cache->{dbh} = $class->dbh_main; + request_cache->{dbh} = $class->dbh_main; return $class->dbh_main; } @@ -681,7 +686,7 @@ sub log_user_request { } eval { - local $class->request_cache->{dbh}; + local request_cache->{dbh}; $class->switch_to_main_db(); $class->dbh->do("INSERT INTO user_request_log (user_id, ip_address, user_agent, request_url, @@ -693,13 +698,13 @@ sub log_user_request { sub is_shadow_db { my $class = shift; - return $class->request_cache->{dbh} != $class->dbh_main; + return request_cache->{dbh} != $class->dbh_main; } sub fields { - my ($class, $criteria) = @_; + my (undef, $criteria) = @_; $criteria ||= {}; - my $cache = $class->request_cache; + my $cache = request_cache; # We create an advanced cache for fields by type, so that we # can avoid going back to the database for every fields() call. @@ -746,29 +751,28 @@ sub fields { } sub active_custom_fields { - my ($class, $params) = @_; + my (undef, $params) = @_; my $cache_id = 'active_custom_fields'; if ($params) { $cache_id .= ($params->{product} ? '_p' . $params->{product}->id : '') . ($params->{component} ? '_c' . $params->{component}->id : ''); $cache_id .= ':noext' if $params->{skip_extensions}; } - if (!exists $class->request_cache->{$cache_id}) { + if (!exists request_cache->{$cache_id}) { my $fields = Bugzilla::Field->match({ custom => 1, obsolete => 0, skip_extensions => 1 }); Bugzilla::Hook::process('active_custom_fields', { fields => \$fields, params => $params }); - $class->request_cache->{$cache_id} = $fields; + request_cache->{$cache_id} = $fields; } - return @{$class->request_cache->{$cache_id}}; + return @{request_cache->{$cache_id}}; } sub has_flags { - my $class = shift; - if (!defined $class->request_cache->{has_flags}) { - $class->request_cache->{has_flags} = Bugzilla::Flag->any_exist; + if (!defined request_cache->{has_flags}) { + request_cache->{has_flags} = Bugzilla::Flag->any_exist; } - return $class->request_cache->{has_flags}; + return request_cache->{has_flags}; } sub local_timezone { @@ -778,19 +782,13 @@ sub local_timezone { # Send messages to syslog for the auditing systems (eg. mozdef) to pick up. sub audit { - my ($class, $message) = @_; + my (undef, $message) = @_; state $logger = Log::Log4perl->get_logger("audit"); $logger->notice(encode_utf8($message)); } -# This creates the request cache for non-mod_perl installations. -# This is identical to Install::Util::_cache so that things loaded -# into Install::Util::_cache during installation can be read out -# of request_cache later in installation. -use constant request_cache => Bugzilla::Install::Util::_cache(); - sub clear_request_cache { - my ($class, %option) = @_; + my (undef, %option) = @_; my $request_cache = request_cache(); my @except = $option{except} ? @{ $option{except} } : (); @@ -811,21 +809,21 @@ sub process_cache { sub metrics_enabled { if (defined $_[1]) { if (!$_[1] - && $_[0]->request_cache->{metrics_enabled} - && $_[0]->request_cache->{metrics}) + && request_cache->{metrics_enabled} + && request_cache->{metrics}) { - $_[0]->request_cache->{metrics}->cancel(); - delete $_[0]->request_cache->{metrics}; + request_cache->{metrics}->cancel(); + delete request_cache->{metrics}; } - $_[0]->request_cache->{metrics_enabled} = $_[1]; + request_cache->{metrics_enabled} = $_[1]; } else { - return $_[0]->request_cache->{metrics_enabled}; + return request_cache->{metrics_enabled}; } } sub metrics { - return $_[0]->request_cache->{metrics} ||= Bugzilla::Metrics::Collector->new($_[1]); + return request_cache->{metrics} ||= Bugzilla::Metrics::Collector->new($_[1]); } # This is a memcached wrapper, which provides cross-process and cross-system @@ -833,9 +831,9 @@ sub metrics { sub memcached { # BMO - use metrics subclass if required if (Bugzilla->metrics_enabled) { - return $_[0]->request_cache->{memcached} ||= Bugzilla::Metrics::Memcached->_new(); + return request_cache->{memcached} ||= Bugzilla::Metrics::Memcached->_new(); } else { - return $_[0]->request_cache->{memcached} ||= Bugzilla::Memcached->_new(); + return request_cache->{memcached} ||= Bugzilla::Memcached->_new(); } } From 50ef8fcbb13f77eeded121d32be3f1a98cd17640 Mon Sep 17 00:00:00 2001 From: Israel Madueme Date: Wed, 25 Apr 2018 08:53:08 -0400 Subject: [PATCH 025/119] Bug 1455772 - Label bug bounty form credit fields --- .../en/default/pages/attachment_bounty_form.html.tmpl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/BMO/template/en/default/pages/attachment_bounty_form.html.tmpl b/extensions/BMO/template/en/default/pages/attachment_bounty_form.html.tmpl index faf32aa368..0f7966097f 100644 --- a/extensions/BMO/template/en/default/pages/attachment_bounty_form.html.tmpl +++ b/extensions/BMO/template/en/default/pages/attachment_bounty_form.html.tmpl @@ -211,12 +211,12 @@ function validateAndSubmit() {
    - +
    - +
    From 396ac9ccd1170b42c14bc50d7706be657a4fb114 Mon Sep 17 00:00:00 2001 From: Israel Madueme Date: Wed, 25 Apr 2018 08:54:10 -0400 Subject: [PATCH 026/119] Bug 1373280 - Highlight private comments in new bug modal UI Prior to this commit, the only indication that a comment was private was the small "Private" tag in the comment header. This commit restores the red font color to private comments and adds a small dashed border for those that have trouble seeing the color red. --- extensions/BugModal/web/bug_modal.css | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/extensions/BugModal/web/bug_modal.css b/extensions/BugModal/web/bug_modal.css index 65aeec86d0..eeba78d74e 100644 --- a/extensions/BugModal/web/bug_modal.css +++ b/extensions/BugModal/web/bug_modal.css @@ -626,6 +626,11 @@ body.platform-Win32 .comment-text, body.platform-Win64 .comment-text { width: 99% !important; } +.comment-text.bz_private { + color: darkred; + border: 1px dashed darkred; +} + .comment-tags { padding: 0 8px 2px 8px !important; } From 91631953125a743702cbd6783b7d889721db3bbc Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Wed, 25 Apr 2018 18:10:47 +0200 Subject: [PATCH 027/119] Bug 1430367 - "preconnect" to google-analytics domain for improved performance --- Bugzilla/CGI.pm | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Bugzilla/CGI.pm b/Bugzilla/CGI.pm index b0bc15e786..6e48a2355a 100644 --- a/Bugzilla/CGI.pm +++ b/Bugzilla/CGI.pm @@ -590,6 +590,9 @@ sub header { "skins/standard/fonts/MaterialIcons-Regular.woff2", ); $headers{'-link'} = join(", ", map { sprintf('; rel="preload"; as="font"', Bugzilla->VERSION, $_) } @fonts); + if (Bugzilla->params->{google_analytics_tracking_id}) { + $headers{'-link'} .= ', ; rel="preconnect"; crossorigin'; + } } return $self->SUPER::header(%headers) || ""; From 2987c141485979c911a9da275c316567442a8190 Mon Sep 17 00:00:00 2001 From: Dylan William Hardison Date: Wed, 25 Apr 2018 12:11:16 -0400 Subject: [PATCH 028/119] Bug 1456529 - Support SameSite attribute on session cookies --- Bugzilla/CGI.pm | 2 ++ Makefile.PL | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Bugzilla/CGI.pm b/Bugzilla/CGI.pm index 6e48a2355a..9e8ba6c09b 100644 --- a/Bugzilla/CGI.pm +++ b/Bugzilla/CGI.pm @@ -687,6 +687,8 @@ sub send_cookie { $paramhash{'-secure'} = 1 if lc( $uri->scheme ) eq 'https'; + $paramhash{'-samesite'} = 'Lax'; + push(@{$self->{'Bugzilla_cookie_list'}}, $self->cookie(%paramhash)); } diff --git a/Makefile.PL b/Makefile.PL index 4cea5b0665..96e340b4f7 100755 --- a/Makefile.PL +++ b/Makefile.PL @@ -34,7 +34,7 @@ BEGIN { # PREREQ_PM my %requires = ( 'Algorithm::BloomFilter' => '0.02', - 'CGI' => '3.51', + 'CGI' => '4.31', 'CPAN::Meta::Prereqs' => '2.132830', 'CPAN::Meta::Requirements' => '2.121', 'Class::XSAccessor' => '1.18', From 8ad45c8d3660e41fb2c195451a2cc3d3935781e1 Mon Sep 17 00:00:00 2001 From: Dylan William Hardison Date: Wed, 25 Apr 2018 16:19:49 -0400 Subject: [PATCH 029/119] bump version to 20180426.1 (#550) --- Bugzilla.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Bugzilla.pm b/Bugzilla.pm index c95b3a3bad..bc3ceb58c7 100644 --- a/Bugzilla.pm +++ b/Bugzilla.pm @@ -22,7 +22,7 @@ BEGIN { } } -our $VERSION = '20180412.2'; +our $VERSION = '20180426.1'; use Bugzilla::Auth; use Bugzilla::Auth::Persist::Cookie; From 993e754afdf6a94e128633be7261e27cfc384f2c Mon Sep 17 00:00:00 2001 From: Dylan William Hardison Date: Wed, 25 Apr 2018 16:20:13 -0400 Subject: [PATCH 030/119] no bug - fix permissions on log files and a few other things (#552) --- Vagrantfile | 1 + vagrant_support/apache.yml | 8 +++++++- vagrant_support/checksetup.yml | 6 ++++++ vagrant_support/playbook.yml | 6 ------ 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/Vagrantfile b/Vagrantfile index 2a1c4e9c46..f63c707a09 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -22,6 +22,7 @@ RSYNC_ARGS = [ '--copy-links', '--exclude=local/', '--exclude=data/', + '--exclude=logs/', '--exclude=template_cache/', '--exclude=localconfig', '--include=.git/' diff --git a/vagrant_support/apache.yml b/vagrant_support/apache.yml index c7159371c6..5031187e35 100644 --- a/vagrant_support/apache.yml +++ b/vagrant_support/apache.yml @@ -10,5 +10,11 @@ service: name=httpd enabled=yes when: LAZY == 0 +- name: ensure bugzilla.log has right permissions + file: path=/vagrant/logs/bugzilla.log state=touch owner=vagrant group=apache mode=0660 + +- name: ensure bugzilla-json.log has right permissions + file: path=/vagrant/logs/bugzilla-json.log state=touch owner=vagrant group=apache mode=0660 + - name: restart httpd - service: name=httpd state=restarted + service: name=httpd state=restarted \ No newline at end of file diff --git a/vagrant_support/checksetup.yml b/vagrant_support/checksetup.yml index 580cc9dd9f..455986148f 100644 --- a/vagrant_support/checksetup.yml +++ b/vagrant_support/checksetup.yml @@ -22,6 +22,12 @@ mode: 0644 when: LAZY == 0 +- name: fix owner of /vagrant/template_cache + file: path=/vagrant/template_cache state=directory owner=vagrant group=apache recurse=yes + +- name: fix owner of /data + file: path=/data state=directory owner=vagrant group=apache recurse=yes + - name: run checksetup become: false shell: sg apache -c '/usr/local/bin/bmo-checksetup --no-templates' diff --git a/vagrant_support/playbook.yml b/vagrant_support/playbook.yml index b394f5592f..5041cefcb0 100644 --- a/vagrant_support/playbook.yml +++ b/vagrant_support/playbook.yml @@ -216,9 +216,3 @@ LAZY: 0 - import_tasks: devtools.yml - - name: fix owner of /vagrant/template_cache - file: path=/vagrant/template_cache state=directory owner=vagrant group=apache recurse=yes - - - name: fix owner of /data - file: path=/data state=directory owner=vagrant group=apache recurse=yes - From 132700438a4d409a7c9f76be7cfed4d18130a4dd Mon Sep 17 00:00:00 2001 From: Dylan William Hardison Date: Wed, 25 Apr 2018 17:02:30 -0400 Subject: [PATCH 031/119] Bug 1441732 - Improve missing module error in Bugzilla::Extensions and catch more compile errors in tests --- .circleci/config.yml | 3 ++- Bugzilla.pm | 2 ++ Bugzilla/Extension.pm | 6 +++++- Dockerfile | 1 + 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 64e6c58313..5ca2de73b7 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -180,8 +180,9 @@ jobs: [[ -f build_info/only_version_changed.txt ]] && exit 0 mv /opt/bmo/local /app/local mkdir artifacts + - run: perl -I/app -I/app/local/lib/perl5 -c -E 'use Bugzilla; BEGIN { Bugzilla->extensions }' - run: | - [[ -f build_info/only_version_changed.txt ]] && exit 0 + [[ -f build_info/only_version_changed.txt ]] && exit 0 perl Makefile.PL - run: name: run sanity tests diff --git a/Bugzilla.pm b/Bugzilla.pm index bc3ceb58c7..36711f4eaa 100644 --- a/Bugzilla.pm +++ b/Bugzilla.pm @@ -870,6 +870,8 @@ sub check_rate_limit { # Per-process cleanup. Note that this is a plain subroutine, not a method, # so we don't have $class available. sub _cleanup { + return if $^C; + # BMO - finalise and report on metrics if (Bugzilla->metrics_enabled) { Bugzilla->metrics->finish(); diff --git a/Bugzilla/Extension.pm b/Bugzilla/Extension.pm index a41ac9326c..8e173c711b 100644 --- a/Bugzilla/Extension.pm +++ b/Bugzilla/Extension.pm @@ -35,7 +35,11 @@ sub INC_HOOK { my $first = 1; untaint($real_file); $INC{$fake_file} = $real_file; - open my $fh, '<', $real_file or die "invalid file: $real_file"; + my $found = open my $fh, '<', $real_file; + unless ($found) { + require Carp; + Carp::croak "Can't locate $fake_file while looking for $real_file in \@INC (\@INC contains: @INC)"; + } return sub { no warnings; if ( !$first ) { diff --git a/Dockerfile b/Dockerfile index 0e7bb7acd9..78152531c7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -26,6 +26,7 @@ COPY . . RUN mv /opt/bmo/local /app && \ chown -R app:app /app && \ + perl -I/app -I/app/local/lib/perl5 -c -E 'use Bugzilla; BEGIN { Bugzilla->extensions }' && \ perl -c /app/scripts/entrypoint.pl && \ setcap 'cap_net_bind_service=+ep' /usr/sbin/httpd From 92d0e0f268550627a05f75cede34d67e91a91a1b Mon Sep 17 00:00:00 2001 From: Dylan William Hardison Date: Wed, 25 Apr 2018 19:43:23 -0400 Subject: [PATCH 032/119] Update CONTRIBUTING.md --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e3c3bd4a4e..ffe627d303 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -12,7 +12,7 @@ feedback for changes that would be required. All contributions should follow this format, even those from core contributors. Should you wish to work on an issue, please claim it first by commenting on -the GitHub issue that you want to work on it. This is to prevent duplicated +the bug that you want to work on it. This is to prevent duplicated efforts from contributors on the same issue. Head over to [BugsAhoy!](https://www.joshmatthews.net/bugsahoy/?bugzilla=1) From 4ab9b74bbb2c1f35138de017aac100cc499cbd0d Mon Sep 17 00:00:00 2001 From: dklawren Date: Thu, 26 Apr 2018 10:09:12 -0400 Subject: [PATCH 033/119] Bug 1457031 - When a revision does not have an bug id, the bug is made public but we also need to remove secure-revision tag --- extensions/PhabBugz/lib/Feed.pm | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/extensions/PhabBugz/lib/Feed.pm b/extensions/PhabBugz/lib/Feed.pm index 275d71976c..4c7fe54a5a 100644 --- a/extensions/PhabBugz/lib/Feed.pm +++ b/extensions/PhabBugz/lib/Feed.pm @@ -272,6 +272,13 @@ sub process_revision_change { # Load the revision from Phabricator my $revision = Bugzilla::Extension::PhabBugz::Revision->new_from_query({ phids => [ $revision_phid ] }); + + my $secure_revision = + Bugzilla::Extension::PhabBugz::Project->new_from_query( + { + name => 'secure-revision' + } + ); # NO BUG ID @@ -281,6 +288,7 @@ sub process_revision_change { INFO("No bug associated with new revision. Marking public."); $revision->set_policy('view', 'public'); $revision->set_policy('edit', 'users'); + $revision->remove_project($secure_revision->phid); $revision->update(); INFO("SUCCESS"); return; @@ -310,8 +318,7 @@ sub process_revision_change { INFO('Bug is public so setting view/edit public'); $revision->set_policy('view', 'public'); $revision->set_policy('edit', 'users'); - my $secure_project_phid = get_project_phid('secure-revision'); - $revision->remove_project($secure_project_phid); + $revision->remove_project($secure_revision->phid); } # else bug is private. else { @@ -355,8 +362,7 @@ sub process_revision_change { $revision->set_policy('edit', $new_policy->phid); } - my $secure_project_phid = get_project_phid('secure-revision'); - $revision->add_project($secure_project_phid); + $revision->add_project($secure_revision->phid); } # Subscriber list of the private revision should always match From a7ff5b98d8aea3b432913772e5d1bf345fbf87e0 Mon Sep 17 00:00:00 2001 From: Dylan William Hardison Date: Thu, 26 Apr 2018 10:25:46 -0400 Subject: [PATCH 034/119] fix minor bug in release process --- .circleci/config.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 5ca2de73b7..5d273e4ba8 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -180,7 +180,10 @@ jobs: [[ -f build_info/only_version_changed.txt ]] && exit 0 mv /opt/bmo/local /app/local mkdir artifacts - - run: perl -I/app -I/app/local/lib/perl5 -c -E 'use Bugzilla; BEGIN { Bugzilla->extensions }' + - name: test code + run: | + [[ -f build_info/only_version_changed.txt ]] && exit 0 + perl -I/app -I/app/local/lib/perl5 -c -E 'use Bugzilla; BEGIN { Bugzilla->extensions }' - run: | [[ -f build_info/only_version_changed.txt ]] && exit 0 perl Makefile.PL From 1f07e9fca637b7758b3679562672ef890479b369 Mon Sep 17 00:00:00 2001 From: Dylan William Hardison Date: Thu, 26 Apr 2018 10:34:40 -0400 Subject: [PATCH 035/119] no bug - remove name label --- .circleci/config.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 5d273e4ba8..e6db6b7fa5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -180,8 +180,7 @@ jobs: [[ -f build_info/only_version_changed.txt ]] && exit 0 mv /opt/bmo/local /app/local mkdir artifacts - - name: test code - run: | + - run: | [[ -f build_info/only_version_changed.txt ]] && exit 0 perl -I/app -I/app/local/lib/perl5 -c -E 'use Bugzilla; BEGIN { Bugzilla->extensions }' - run: | From 79dd60d845e426dcd243057cd1bca36be27076db Mon Sep 17 00:00:00 2001 From: Dylan William Hardison Date: Thu, 26 Apr 2018 11:06:33 -0400 Subject: [PATCH 036/119] bump version to 20180426.4 (#556) --- Bugzilla.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Bugzilla.pm b/Bugzilla.pm index 36711f4eaa..efe1580a2e 100644 --- a/Bugzilla.pm +++ b/Bugzilla.pm @@ -22,7 +22,7 @@ BEGIN { } } -our $VERSION = '20180426.1'; +our $VERSION = '20180426.4'; use Bugzilla::Auth; use Bugzilla::Auth::Persist::Cookie; From ba5f2af68c6dd8639c6bd45d3e419741e1946134 Mon Sep 17 00:00:00 2001 From: dklawren Date: Thu, 26 Apr 2018 13:41:27 -0400 Subject: [PATCH 037/119] Bug 1454647 - Mirror all BMO groups as Phabricator projects and keep them in sync --- extensions/PhabBugz/lib/Config.pm | 5 ----- extensions/PhabBugz/lib/Feed.pm | 20 ++++++++----------- extensions/PhabBugz/lib/Policy.pm | 2 +- extensions/PhabBugz/lib/Util.pm | 7 +++---- extensions/PhabBugz/lib/WebService.pm | 1 - .../default/admin/params/phabbugz.html.tmpl | 1 - .../hook/global/user-error-errors.html.tmpl | 7 +++---- extensions/Push/lib/Connector/Phabricator.pm | 1 - scripts/generate_conduit_data.pl | 1 - 9 files changed, 15 insertions(+), 30 deletions(-) diff --git a/extensions/PhabBugz/lib/Config.pm b/extensions/PhabBugz/lib/Config.pm index 85ba37e74b..d4b71430b2 100644 --- a/extensions/PhabBugz/lib/Config.pm +++ b/extensions/PhabBugz/lib/Config.pm @@ -30,11 +30,6 @@ sub get_param_list { default => '', checker => \&check_urlbase }, - { - name => 'phabricator_sync_groups', - type => 't', - default => '', - }, { name => 'phabricator_api_key', type => 't', diff --git a/extensions/PhabBugz/lib/Feed.pm b/extensions/PhabBugz/lib/Feed.pm index 4c7fe54a5a..c35eeba4d5 100644 --- a/extensions/PhabBugz/lib/Feed.pm +++ b/extensions/PhabBugz/lib/Feed.pm @@ -16,8 +16,9 @@ use List::MoreUtils qw(any); use Moo; use Try::Tiny; -use Bugzilla::Logging; use Bugzilla::Constants; +use Bugzilla::Field; +use Bugzilla::Logging; use Bugzilla::Search; use Bugzilla::Util qw(diff_arrays with_writable_database with_readonly_database); @@ -104,7 +105,7 @@ sub start { sub feed_query { my ($self) = @_; - Bugzilla::Logging->fields->{type} = 'FEED'; + local Bugzilla::Logging->fields->{type} = 'FEED'; # Ensure Phabricator syncing is enabled if (!Bugzilla->params->{phabricator_enabled}) { @@ -164,7 +165,7 @@ sub feed_query { sub user_query { my ( $self ) = @_; - Bugzilla::Logging->fields->{type} = 'USERS'; + local Bugzilla::Logging->fields->{type} = 'USERS'; # Ensure Phabricator syncing is enabled if (!Bugzilla->params->{phabricator_enabled}) { @@ -204,21 +205,17 @@ sub user_query { sub group_query { my ($self) = @_; + local Bugzilla::Logging->fields->{type} = 'GROUPS'; + # Ensure Phabricator syncing is enabled if ( !Bugzilla->params->{phabricator_enabled} ) { WARN("PHABRICATOR SYNC DISABLED"); return; } - my $phab_sync_groups = Bugzilla->params->{phabricator_sync_groups}; - if ( !$phab_sync_groups ) { - WARN('A comma delimited list of security groups was not provided.'); - return; - } - # PROCESS SECURITY GROUPS - INFO("GROUPS: Updating group memberships"); + INFO("Updating group memberships"); # Loop through each group and perform the following: # @@ -228,8 +225,7 @@ sub group_query { # 4. Set project members to exact list # 5. Profit - my $sync_groups = Bugzilla::Group->match( - { name => [ split( '[,\s]+', $phab_sync_groups ) ] } ); + my $sync_groups = Bugzilla::Group->match( { isactive => 1, isbuggroup => 1 } ); foreach my $group (@$sync_groups) { diff --git a/extensions/PhabBugz/lib/Policy.pm b/extensions/PhabBugz/lib/Policy.pm index 0beecc8e13..3ad6f284c3 100644 --- a/extensions/PhabBugz/lib/Policy.pm +++ b/extensions/PhabBugz/lib/Policy.pm @@ -112,7 +112,7 @@ sub create { push @$project_phids, $project->phid if $project; } - ThrowUserError('invalid_phabricator_sync_groups') unless @$project_phids; + ThrowUserError('invalid_phabricator_projects') unless @$project_phids; push @{ $data->{policy} }, { action => 'allow', diff --git a/extensions/PhabBugz/lib/Util.pm b/extensions/PhabBugz/lib/Util.pm index 0f93512851..42da79c39d 100644 --- a/extensions/PhabBugz/lib/Util.pm +++ b/extensions/PhabBugz/lib/Util.pm @@ -162,7 +162,7 @@ sub create_private_revision_policy { push(@$project_phids, $phid) if $phid; } - ThrowUserError('invalid_phabricator_sync_groups') unless @$project_phids; + ThrowUserError('invalid_phabricator_projects') unless @$project_phids; push(@{ $data->{policy} }, { @@ -512,9 +512,8 @@ sub request { sub get_security_sync_groups { my $bug = shift; - my $phab_sync_groups = Bugzilla->params->{phabricator_sync_groups} - || ThrowUserError('invalid_phabricator_sync_groups'); - my $sync_group_names = [ split('[,\s]+', $phab_sync_groups) ]; + my $sync_groups = Bugzilla::Group->match( { isactive => 1, isbuggroup => 1 } ); + my $sync_group_names = [ map { $_->name } @$sync_groups ]; my $bug_groups = $bug->groups_in; my $bug_group_names = [ map { $_->name } @$bug_groups ]; diff --git a/extensions/PhabBugz/lib/WebService.pm b/extensions/PhabBugz/lib/WebService.pm index adf127a1f1..32cea1b510 100644 --- a/extensions/PhabBugz/lib/WebService.pm +++ b/extensions/PhabBugz/lib/WebService.pm @@ -35,7 +35,6 @@ use Bugzilla::Extension::PhabBugz::Util qw( get_project_phid get_revisions_by_ids get_security_sync_groups - intersect is_attachment_phab_revision make_revision_public request diff --git a/extensions/PhabBugz/template/en/default/admin/params/phabbugz.html.tmpl b/extensions/PhabBugz/template/en/default/admin/params/phabbugz.html.tmpl index d67839cc8d..1b5bdda4bf 100644 --- a/extensions/PhabBugz/template/en/default/admin/params/phabbugz.html.tmpl +++ b/extensions/PhabBugz/template/en/default/admin/params/phabbugz.html.tmpl @@ -16,7 +16,6 @@ phabricator_enabled => 'Enable Phabricator Integration', phabricator_base_uri => 'Phabricator Base URI', phabricator_api_key => 'Phabricator User API Key', - phabricator_sync_groups => 'Comma delimited list of Bugzilla groups to sync to Phabricator projects', phabricator_auth_callback_url => 'Phabricator Auth Delegation URL', phabricator_app_id => 'app_id for API Keys delegated to Phabricator', } diff --git a/extensions/PhabBugz/template/en/default/hook/global/user-error-errors.html.tmpl b/extensions/PhabBugz/template/en/default/hook/global/user-error-errors.html.tmpl index 1457e35255..f1366e7b6e 100644 --- a/extensions/PhabBugz/template/en/default/hook/global/user-error-errors.html.tmpl +++ b/extensions/PhabBugz/template/en/default/hook/global/user-error-errors.html.tmpl @@ -14,10 +14,9 @@ [% title = "Invalid Phabricator API Key" %] You must provide a valid Phabricator API Key. -[% ELSIF error == "invalid_phabricator_sync_groups" %] - [% title = "Invalid Phabricator Sync Groups" %] - You must provide a comma delimited list of security groups - to sync with Phabricator. +[% ELSIF error == "invalid_phabricator_projects" %] + [% title = "Invalid Phabricator Projects" %] + One or more Phabricator projects selected are invalid. [% ELSIF error == "invalid_phabricator_revision_id" %] [% title = "Invalid Phabricator Revision ID" %] diff --git a/extensions/Push/lib/Connector/Phabricator.pm b/extensions/Push/lib/Connector/Phabricator.pm index 1878834a92..cea73f4104 100644 --- a/extensions/Push/lib/Connector/Phabricator.pm +++ b/extensions/Push/lib/Connector/Phabricator.pm @@ -28,7 +28,6 @@ use Bugzilla::Extension::PhabBugz::Util qw( get_bug_role_phids get_project_phid get_security_sync_groups - intersect make_revision_public make_revision_private set_revision_subscribers diff --git a/scripts/generate_conduit_data.pl b/scripts/generate_conduit_data.pl index 50a7379f45..541afb52a8 100755 --- a/scripts/generate_conduit_data.pl +++ b/scripts/generate_conduit_data.pl @@ -133,7 +133,6 @@ BEGIN password_check_on_login => 0, phabricator_base_uri => 'http://phabricator.test/', phabricator_enabled => 1, - phabricator_sync_groups => 'core-security', ); set_push_connector_options(); From 9184ec2442347f0c4f7a4c0b4e4436ea8d41e875 Mon Sep 17 00:00:00 2001 From: dklawren Date: Tue, 1 May 2018 11:10:03 -0400 Subject: [PATCH 038/119] Bug 1452984 - double-check new accounts with BMO to catch and notify of username squatting --- extensions/PhabBugz/lib/Feed.pm | 57 ++++++++++++++++++- .../admin/email/squatter-alert.txt.tmpl | 34 +++++++++++ 2 files changed, 89 insertions(+), 2 deletions(-) create mode 100644 extensions/PhabBugz/template/en/default/admin/email/squatter-alert.txt.tmpl diff --git a/extensions/PhabBugz/lib/Feed.pm b/extensions/PhabBugz/lib/Feed.pm index c35eeba4d5..3517d44fde 100644 --- a/extensions/PhabBugz/lib/Feed.pm +++ b/extensions/PhabBugz/lib/Feed.pm @@ -17,10 +17,11 @@ use Moo; use Try::Tiny; use Bugzilla::Constants; -use Bugzilla::Field; +use Bugzilla::Error; use Bugzilla::Logging; +use Bugzilla::Mailer; use Bugzilla::Search; -use Bugzilla::Util qw(diff_arrays with_writable_database with_readonly_database); +use Bugzilla::Util qw(diff_arrays format_time with_writable_database with_readonly_database); use Bugzilla::Extension::PhabBugz::Constants; use Bugzilla::Extension::PhabBugz::Policy; @@ -520,6 +521,55 @@ sub process_new_user { # Pre setup before querying DB my $old_user = set_phab_user(); + # CHECK AND WARN FOR POSSIBLE USERNAME SQUATTING + INFO("Checking for username squatters"); + my $dbh = Bugzilla->dbh; + my $regexp = $dbh->quote( ":?:" . quotemeta($phab_user->name) . "[[:>:]]" ); + my $results = $dbh->selectall_arrayref( " + SELECT userid, login_name, realname + FROM profiles + WHERE userid != ? AND " . $dbh->sql_regexp( 'realname', $regexp ), + { Slice => {} }, + $bug_user->id ); + if (@$results) { + # The email client will display the Date: header in the desired timezone, + # so we can always use UTC here. + my $timestamp = Bugzilla->dbh->selectrow_array('SELECT LOCALTIMESTAMP(0)'); + $timestamp = format_time($timestamp, '%a, %d %b %Y %T %z', 'UTC'); + + foreach my $row (@$results) { + WARN( + 'Possible username squatter: ', + 'phab user login: ' . $phab_user->name, + ' phab user realname: ' . $phab_user->realname, + ' bugzilla user id: ' . $row->{userid}, + ' bugzilla login: ' . $row->{login_name}, + ' bugzilla realname: ' . $row->{realname} + ); + + my $vars = { + date => $timestamp, + phab_user_login => $phab_user->name, + phab_user_realname => $phab_user->realname, + bugzilla_userid => $phab_user->bugzilla_user->id, + bugzilla_login => $phab_user->bugzilla_user->login, + bugzilla_realname => $phab_user->bugzilla_user->name, + squat_userid => $row->{userid}, + squat_login => $row->{login_name}, + squat_realname => $row->{realname} + }; + + my $message; + my $template = Bugzilla->template; + $template->process("admin/email/squatter-alert.txt.tmpl", $vars, \$message) + || ThrowTemplateError($template->error()); + + MessageToMTA($message); + } + } + + # ADD SUBSCRIBERS TO REVSISIONS FOR CURRENT PRIVATE BUGS + my $params = { f3 => 'OP', j3 => 'OR', @@ -563,6 +613,8 @@ sub process_new_user { # the first value of each row should be the bug id my @bug_ids = map { shift @$_ } @$data; + INFO("Updating subscriber values for old private bugs"); + foreach my $bug_id (@bug_ids) { INFO("Processing bug $bug_id"); @@ -573,6 +625,7 @@ sub process_new_user { foreach my $attachment (@attachments) { my ($revision_id) = ($attachment->filename =~ PHAB_ATTACHMENT_PATTERN); + INFO("Processing revision D$revision_id"); my $revision = Bugzilla::Extension::PhabBugz::Revision->new_from_query( diff --git a/extensions/PhabBugz/template/en/default/admin/email/squatter-alert.txt.tmpl b/extensions/PhabBugz/template/en/default/admin/email/squatter-alert.txt.tmpl new file mode 100644 index 0000000000..98e92a3798 --- /dev/null +++ b/extensions/PhabBugz/template/en/default/admin/email/squatter-alert.txt.tmpl @@ -0,0 +1,34 @@ +[%# This Source Code Form is subject to the terms of the Mozilla Public + # License, v. 2.0. If a copy of the MPL was not distributed with this + # file, You can obtain one at http://mozilla.org/MPL/2.0/. + # + # This Source Code Form is "Incompatible With Secondary Licenses", as + # defined by the Mozilla Public License, v. 2.0. + #%] + +[% PROCESS global/variables.none.tmpl %] + +From: [% Param('mailfrom') %] +To: phabricator-admin@mozilla.com +Subject: Possible Phabricator Username Squatter Alert +Date: [% date %] +X-Bugzilla-Type: squatter-alert + +Possible username squatter: + +Phabricator Account + +login: [% phab_user_login %] +realname: [% phab_user_realname %] + +Bugzilla Account Matching Phabricator Account + +user id: [% bugzilla_userid %] +login: [% bugzilla_login %] +realname: [% bugzilla_realname %] + +Possible Bugzilla Account Squatting On + +user id: [% squat_userid %] +login: [% squat_login %] +realname: [% squat_realname %] From 39f6b6e1f5f602f31fa7cd844809a4a7d4e579d2 Mon Sep 17 00:00:00 2001 From: Israel Madueme Date: Tue, 1 May 2018 17:32:45 -0400 Subject: [PATCH 039/119] Bug 1453759 - Port OrangeFactor extension to treeherder --- Bugzilla/CGI.pm | 7 ++- extensions/OrangeFactor/Extension.pm | 4 ++ .../bug/edit-after_custom_fields.html.tmpl | 9 ++- .../hook/bug_modal/edit-details_rhs.html.tmpl | 9 ++- .../OrangeFactor/web/js/orange_factor.js | 59 +++++++------------ 5 files changed, 45 insertions(+), 43 deletions(-) diff --git a/Bugzilla/CGI.pm b/Bugzilla/CGI.pm index 9e8ba6c09b..03805ad1ec 100644 --- a/Bugzilla/CGI.pm +++ b/Bugzilla/CGI.pm @@ -42,6 +42,11 @@ sub DEFAULT_CSP { img_src => [ 'self', 'https://secure.gravatar.com', 'https://www.google-analytics.com' ], style_src => [ 'self', 'unsafe-inline' ], object_src => [ 'none' ], + connect_src => [ + 'self', + # This is from extensions/OrangeFactor/web/js/orange_factor.js + 'https://treeherder.mozilla.org/api/failurecount/', + ], form_action => [ 'self', # used in template/en/default/search/search-google.html.tmpl @@ -69,7 +74,7 @@ sub SHOW_BUG_MODAL_CSP { connect_src => [ 'self', # This is from extensions/OrangeFactor/web/js/orange_factor.js - 'https://brasstacks.mozilla.com/orangefactor/api/count', + 'https://treeherder.mozilla.org/api/failurecount/', ], frame_src => [ 'self', ], worker_src => [ 'none', ], diff --git a/extensions/OrangeFactor/Extension.pm b/extensions/OrangeFactor/Extension.pm index ab2f1d749e..56dd5dc6e0 100644 --- a/extensions/OrangeFactor/Extension.pm +++ b/extensions/OrangeFactor/Extension.pm @@ -17,6 +17,8 @@ use Bugzilla::User::Setting; use Bugzilla::Constants; use Bugzilla::Attachment; +use DateTime; + our $VERSION = '1.0'; sub template_before_process { @@ -38,6 +40,8 @@ sub template_before_process { my $bug = exists $vars->{'bugs'} ? $vars->{'bugs'}[0] : $vars->{'bug'}; if ($bug && grep($_->name eq 'intermittent-failure', @{ $bug->keyword_objects })) { $vars->{'orange_factor'} = 1; + $vars->{'date_start'} = ( DateTime->now() - DateTime::Duration->new( days => 7 ) )->ymd(); + $vars->{'date_end'} = DateTime->now->ymd(); } } diff --git a/extensions/OrangeFactor/template/en/default/hook/bug/edit-after_custom_fields.html.tmpl b/extensions/OrangeFactor/template/en/default/hook/bug/edit-after_custom_fields.html.tmpl index a41188a630..1eedab479f 100644 --- a/extensions/OrangeFactor/template/en/default/hook/bug/edit-after_custom_fields.html.tmpl +++ b/extensions/OrangeFactor/template/en/default/hook/bug/edit-after_custom_fields.html.tmpl @@ -17,9 +17,14 @@ [% IF cgi.user_agent.match('(?i)gecko') %] - + [% END %] - (link) diff --git a/extensions/OrangeFactor/template/en/default/hook/bug_modal/edit-details_rhs.html.tmpl b/extensions/OrangeFactor/template/en/default/hook/bug_modal/edit-details_rhs.html.tmpl index 30cd65cd2c..d6ee5f127e 100644 --- a/extensions/OrangeFactor/template/en/default/hook/bug_modal/edit-details_rhs.html.tmpl +++ b/extensions/OrangeFactor/template/en/default/hook/bug_modal/edit-details_rhs.html.tmpl @@ -16,10 +16,15 @@ %] [% IF cgi.user_agent.match('(?i)gecko') %] - + [% END %] - (link) [% END %] diff --git a/extensions/OrangeFactor/web/js/orange_factor.js b/extensions/OrangeFactor/web/js/orange_factor.js index fa9411cf84..78fbb5eb32 100644 --- a/extensions/OrangeFactor/web/js/orange_factor.js +++ b/extensions/OrangeFactor/web/js/orange_factor.js @@ -8,21 +8,17 @@ $(function() { 'use strict'; - var dayMs = 24 * 60 * 60 * 1000; - var limit = 7; function getOrangeCount(data) { - data = data.oranges; - var total = 0, - days = [], - date = getCurrentDateMs() - limit * dayMs; - for(var i = 0; i < limit; i++) { - var iso = dateString(new Date(date)); - var count = data[iso] ? data[iso].orangecount : 0; - days.push(count); - total += count; - date += dayMs; - } + let days = []; + let total = 0; + + data.forEach(entry => { + let failureCount = entry["failure_count"]; + days.push(failureCount); + total += failureCount; + }); + displayGraph(days); displayCount(total); } @@ -53,39 +49,26 @@ $(function() { $('#orange-count').text(count + ' failures on trunk in the past week'); } - function dateString(date) { - function norm(part) { - return JSON.stringify(part).length == 2 ? part : '0' + part; - } - return date.getFullYear() - + "-" + norm(date.getMonth() + 1) - + "-" + norm(date.getDate()); - } - - function getCurrentDateMs() { - var d = new Date; - return d.getTime(); - }; - function orangify() { - $('#orange-count') - .text('Loading...') - .show(); - var bugId = document.forms['changeform'].id.value; - var request = { + let $orangeCount = $('#orange-count'); + let queryParams = $.param({ + bug: $orangeCount.data('bug-id'), + startday: $orangeCount.data('date-start'), + endday: $orangeCount.data('date-end'), + tree: 'trunk' + }); + let request = { dataType: "json", - xhrFields: { - withCredentials: true - }, - url: "https://brasstacks.mozilla.com/orangefactor/api/count?" + - "bugid=" + encodeURIComponent(bugId) + "&tree=trunk" + url: `https://treeherder.mozilla.org/api/failurecount/?${queryParams}` }; + + $orangeCount.text('Loading...').show(); $.ajax(request) .done(function(data) { getOrangeCount(data); }) .fail(function() { - $('#orange-count').text('Please sign into OrangeFactor first'); + $orangeCount.text('Unable to load OrangeFactor at this time.'); }); } From ba53123f4d6f5d3911ca013eb6801e1a11c3fccd Mon Sep 17 00:00:00 2001 From: Dylan William Hardison Date: Fri, 4 May 2018 15:30:08 -0400 Subject: [PATCH 040/119] no bug - add tct to dev environment --- vagrant_support/playbook.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/vagrant_support/playbook.yml b/vagrant_support/playbook.yml index 5041cefcb0..c067eab05d 100644 --- a/vagrant_support/playbook.yml +++ b/vagrant_support/playbook.yml @@ -157,11 +157,19 @@ - ncurses-devel - readline-devel + - name: fetch tct + unarchive: + src: https://github.com/dylanwh/tocotrienol/releases/download/1.0.6/tct-centos6.tar.xz + dest: /usr/local/bin + remote_src: yes + mode: '0755' + - name: fetch cpanm get_url: url: http://cpanmin.us dest: /usr/local/bin/cpanm mode: '0755' + - name: install more recent Apache2::SizeLimit cpanm: name=Apache2::SizeLimit executable=/usr/local/bin/cpanm From 32783c128899521fafe0f6b6b915255082f318ed Mon Sep 17 00:00:00 2001 From: Dylan William Hardison Date: Fri, 4 May 2018 15:32:03 -0400 Subject: [PATCH 041/119] no bug - fix typo in meta referer directive --- template/en/default/global/header.html.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/template/en/default/global/header.html.tmpl b/template/en/default/global/header.html.tmpl index cf1c8b9913..6b56a5d30b 100644 --- a/template/en/default/global/header.html.tmpl +++ b/template/en/default/global/header.html.tmpl @@ -101,7 +101,7 @@ [% IF Bugzilla.cgi.should_block_referrer %] [% ELSE %] - + [% END %] [%- js_BUGZILLA = { From 779252b143809c134aae82333e8456b566b054be Mon Sep 17 00:00:00 2001 From: Dylan William Hardison Date: Fri, 4 May 2018 15:41:39 -0400 Subject: [PATCH 042/119] no bug - untaint default values in localconfig --- Bugzilla/Install/Localconfig.pm | 1 + 1 file changed, 1 insertion(+) diff --git a/Bugzilla/Install/Localconfig.pm b/Bugzilla/Install/Localconfig.pm index 7a913358c9..55394bc2eb 100644 --- a/Bugzilla/Install/Localconfig.pm +++ b/Bugzilla/Install/Localconfig.pm @@ -211,6 +211,7 @@ sub _read_localconfig_from_env { else { my $default = $var->{default}; $localconfig{$name} = ref($default) eq 'CODE' ? $default->() : $default; + untaint($localconfig{$name}); } } From dcb3506da0be31ec92950e85f51688cecef1b0f8 Mon Sep 17 00:00:00 2001 From: Dylan William Hardison Date: Mon, 7 May 2018 18:52:29 -0400 Subject: [PATCH 043/119] Bug 1413328 - Use tct (tocotrienol on npm) to encrypt bugmail --- .circleci/config.yml | 4 +- Bugzilla/Install/Localconfig.pm | 4 + Dockerfile | 2 +- extensions/SecureMail/Extension.pm | 61 ++++++------ extensions/SecureMail/lib/TCT.pm | 112 +++++++++++++++++++++++ template/en/default/setup/strings.txt.pl | 3 +- 6 files changed, 149 insertions(+), 37 deletions(-) create mode 100644 extensions/SecureMail/lib/TCT.pm diff --git a/.circleci/config.yml b/.circleci/config.yml index e6db6b7fa5..cef28d2be0 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -7,7 +7,7 @@ version: 2 defaults: bmo_slim_image: &bmo_slim_image - image: mozillabteam/bmo-slim:20180410.1 + image: mozillabteam/bmo-slim:20180503.1 user: app mysql_image: &mysql_image @@ -184,7 +184,7 @@ jobs: [[ -f build_info/only_version_changed.txt ]] && exit 0 perl -I/app -I/app/local/lib/perl5 -c -E 'use Bugzilla; BEGIN { Bugzilla->extensions }' - run: | - [[ -f build_info/only_version_changed.txt ]] && exit 0 + [[ -f build_info/only_version_changed.txt ]] && exit 0 perl Makefile.PL - run: name: run sanity tests diff --git a/Bugzilla/Install/Localconfig.pm b/Bugzilla/Install/Localconfig.pm index 55394bc2eb..e1a8e09094 100644 --- a/Bugzilla/Install/Localconfig.pm +++ b/Bugzilla/Install/Localconfig.pm @@ -125,6 +125,10 @@ use constant LOCALCONFIG_VARS => ( name => 'diffpath', default => sub { dirname( bin_loc('diff') ) }, }, + { + name => 'tct_bin', + default => sub { bin_loc('tct') }, + }, { name => 'site_wide_secret', diff --git a/Dockerfile b/Dockerfile index 78152531c7..bd16516da0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM mozillabteam/bmo-slim:20180410.1 +FROM mozillabteam/bmo-slim:20180503.1 ARG CI ARG CIRCLE_SHA1 diff --git a/extensions/SecureMail/Extension.pm b/extensions/SecureMail/Extension.pm index d684b5a399..508b1f5e80 100644 --- a/extensions/SecureMail/Extension.pm +++ b/extensions/SecureMail/Extension.pm @@ -27,6 +27,7 @@ use warnings; use base qw(Bugzilla::Extension); +use Bugzilla::Logging; use Bugzilla::Attachment; use Bugzilla::Comment; use Bugzilla::Group; @@ -35,6 +36,7 @@ use Bugzilla::User; use Bugzilla::Util qw(trim trick_taint is_7bit_clean); use Bugzilla::Error; use Bugzilla::Mailer; +use Bugzilla::Extension::SecureMail::TCT; use Crypt::OpenPGP::Armour; use Crypt::OpenPGP::KeyRing; @@ -125,9 +127,12 @@ sub object_validators { if ($value =~ /PUBLIC KEY/) { # PGP keys must be ASCII-armoured. - if (!Crypt::OpenPGP::Armour->unarmour($value)) { - ThrowUserError('securemail_invalid_key', - { errstr => Crypt::OpenPGP::Armour->errstr }); + my $tct = Bugzilla::Extension::SecureMail::TCT->new( + public_key => $value, + command => Bugzilla->localconfig->{tct_bin}, + ); + unless ($tct->is_valid->get) { + ThrowUserError( 'securemail_invalid_key', { errstr => 'key is invalid or expired' } ); } } elsif ($value =~ /BEGIN CERTIFICATE/) { @@ -468,8 +473,10 @@ sub _make_secure { # PGP Encryption # ################## - my $pubring = new Crypt::OpenPGP::KeyRing(Data => $key); - my $pgp = new Crypt::OpenPGP(PubRing => $pubring); + my $tct = Bugzilla::Extension::SecureMail::TCT->new( + public_key => $key, + command => Bugzilla->localconfig->{tct_bin}, + ); if (scalar $email->parts > 1) { my $old_boundary = $email->{ct}{attributes}{boundary}; @@ -508,7 +515,7 @@ sub _make_secure { disposition => 'inline', encoding => '7bit', }, - body => _pgp_encrypt($pgp, $to_encrypt, $bug_id) + body => _tct_encrypt($tct, $to_encrypt, $bug_id) ), ); $email->parts_set(\@new_parts); @@ -525,7 +532,7 @@ sub _make_secure { if ($sanitise_subject) { _insert_subject($email, $subject); } - $email->body_set(_pgp_encrypt($pgp, $email->body, $bug_id)); + $email->body_set(_tct_encrypt($tct, $email->body, $bug_id)); } } @@ -601,33 +608,21 @@ sub _make_secure { } } -sub _pgp_encrypt { - my ($pgp, $text, $bug_id) = @_; - # "@" matches every key in the public key ring, which is fine, - # because there's only one key in our keyring. - # - # We use the CAST5 cipher because the Rijndael (AES) module doesn't - # like us for some reason I don't have time to debug fully. - # ("key must be an untainted string scalar") - my $encrypted = $pgp->encrypt( - Data => $text, - Recipients => "@", - Cipher => 'CAST5', - Armour => 0 - ); - if (!defined $encrypted) { - return 'Error during Encryption: ' . $pgp->errstr; +sub _tct_encrypt { + my ($tct, $text, $bug_id) = @_; + + my $comment = Bugzilla->localconfig->{urlbase} . ( $bug_id ? 'show_bug.cgi?id=' . $bug_id : '' ); + my $encrypted; + my $ok = eval { $encrypted = $tct->encrypt( $text, $comment )->get; 1 }; + if (!$ok) { + WARN("Error: $@"); + $encrypted = "$comment\nOpenPGP Encryption failed. Check if your key is expired."; } - $encrypted = Crypt::OpenPGP::Armour->armour( - Data => $encrypted, - Object => 'MESSAGE', - Headers => { - Comment => Bugzilla->localconfig->{urlbase} . ($bug_id ? 'show_bug.cgi?id=' . $bug_id : ''), - }, - ); - # until Crypt::OpenPGP makes the Version header optional we have to strip - # it out manually (bug 1181406). - $encrypted =~ s/\nVersion:[^\n]+//; + elsif (!$encrypted) { + WARN('message empty!'); + $encrypted = "$comment\nOpenPGP Encryption failed for unknown reason."; + } + return $encrypted; } diff --git a/extensions/SecureMail/lib/TCT.pm b/extensions/SecureMail/lib/TCT.pm new file mode 100644 index 0000000000..3a16309c29 --- /dev/null +++ b/extensions/SecureMail/lib/TCT.pm @@ -0,0 +1,112 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# This Source Code Form is "Incompatible With Secondary Licenses", as +# defined by the Mozilla Public License, v. 2.0. + +package Bugzilla::Extension::SecureMail::TCT; +use 5.10.1; +use Moo; + +use Bugzilla::DaemonControl qw( on_finish on_exception ); +use File::Temp; +use Future::Utils qw(call); +use Future; +use IO::Async::Process; + +has 'public_key' => ( is => 'ro', required => 1 ); +has 'public_key_file' => ( is => 'lazy' ); +has 'is_valid' => ( is => 'lazy' ); +has 'command' => ( is => 'ro', default => 'tct' ); + +sub _build_public_key_file { + my ($self) = @_; + my $fh = File::Temp->new(SUFFIX => '.pubkey'); + $fh->print($self->public_key); + $fh->close; + return $fh; +} + +sub _build_is_valid { + my ($self) = @_; + + my $loop = IO::Async::Loop->new; + my $exit_f = $loop->new_future; + my ($stderr, $stdout); + my $process = IO::Async::Process->new( + command => [$self->command, 'check', '-k', $self->public_key_file ], + stderr => { + into => \$stderr, + }, + stdout => { + into => \$stdout, + }, + on_finish => on_finish($exit_f), + on_exception => on_exception($self->command, $exit_f), + ); + $loop->add($process); + + return $exit_f->then( + sub { + my ($rv) = @_; + Future->wrap($rv == 0); + } + ); +} + +sub encrypt { + my ($self, $input, $comment) = @_; + $self->is_valid->then( + sub { + my ($is_valid) = @_; + call { + die 'invalid public key!' unless $is_valid; + + my $output; + my $loop = IO::Async::Loop->new; + my $exit_f = $loop->new_future; + my @command = ( $self->command, 'encrypt', '-k', $self->public_key_file ); + push @command, '--comment', $comment if $comment; + my $process = IO::Async::Process->new( + command => \@command, + stdin => { + from => $input, + }, + stdout => { + into => \$output, + }, + on_finish => on_finish($exit_f), + on_exception => on_exception($self->command, $exit_f), + ); + $loop->add($process); + + return $exit_f->then(sub { Future->wrap($output) }); + } + } + ); +} + +1; + +__END__ + +=head1 NAME + +Bugzilla::Extension::SecureMail::TCT - An interface to the tct program + +=head1 SYNOPSIS + + my $key = <<'PUBLIC_KEY'; + -----BEGIN PGP PUBLIC KEY BLOCK----- + + mQINBFakJSsBEACbDwHztgZaVhIb6f4PN0KbXv5BEciqKNbdVLgWQJyqgEMIwTF7 + ... + o858gRM= + =t9lA + -----END PGP PUBLIC KEY BLOCK----- + PUBLIC_KEY + + my $tct = Bugzilla::Extension::SecureMail::TCT->new(public_key => $key); + my $encrypted = $tct->encrypt("message", "comment goes here")->get; + diff --git a/template/en/default/setup/strings.txt.pl b/template/en/default/setup/strings.txt.pl index 5fc860519f..8726a8b138 100644 --- a/template/en/default/setup/strings.txt.pl +++ b/template/en/default/setup/strings.txt.pl @@ -181,6 +181,7 @@ END what directory the "diff" bin is in. (You only need to set this if you are using that feature of the Patch Viewer.) END + localconfig_tct_bin => 'Path to tct (tocotrienol) a gpg replacement.', localconfig_inbound_proxies => <<'END', This is a list of IP addresses that we expect proxies to come from. This can be '*' if only the load balancer can connect. @@ -270,7 +271,7 @@ END before Apache::SizeLimit kills it. This is only applicable when run under mod_perl. EOT localconfig_shadowdb_user => < < Date: Tue, 8 May 2018 10:13:27 -0400 Subject: [PATCH 044/119] bump version to 20180508.1 --- Bugzilla.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Bugzilla.pm b/Bugzilla.pm index efe1580a2e..825ef46c85 100644 --- a/Bugzilla.pm +++ b/Bugzilla.pm @@ -22,7 +22,7 @@ BEGIN { } } -our $VERSION = '20180426.4'; +our $VERSION = '20180508.1'; use Bugzilla::Auth; use Bugzilla::Auth::Persist::Cookie; From 2eea01b5c298e24127d26ba1e99bf01772579f2a Mon Sep 17 00:00:00 2001 From: dklawren Date: Tue, 8 May 2018 10:15:20 -0400 Subject: [PATCH 045/119] Bug 1459336 - feed daemon skips setting r+ for accepted revision if the same user already has a flag set even if flag is status of ? --- extensions/PhabBugz/lib/Feed.pm | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/extensions/PhabBugz/lib/Feed.pm b/extensions/PhabBugz/lib/Feed.pm index 3517d44fde..821ec65270 100644 --- a/extensions/PhabBugz/lib/Feed.pm +++ b/extensions/PhabBugz/lib/Feed.pm @@ -430,19 +430,22 @@ sub process_revision_change { my ($attach_revision_id) = ($attachment->filename =~ PHAB_ATTACHMENT_PATTERN); next if $revision->id != $attach_revision_id; - # Clear old flags if no longer accepted + # Clear old accepted review flags if no longer accepted my (@denied_flags, @new_flags, @removed_flags, %accepted_done, $flag_type); foreach my $flag (@{ $attachment->flags }) { next if $flag->type->name ne 'review'; $flag_type = $flag->type if $flag->type->is_active; + next if $flag->status ne '+'; if (any { $flag->setter->id == $_ } @denied_user_ids) { + INFO('Denying review flag set by ' . $flag->setter->name); push(@denied_flags, { id => $flag->id, setter => $flag->setter, status => 'X' }); } if (any { $flag->setter->id == $_ } @accepted_user_ids) { + INFO('Skipping as review+ already set by ' . $flag->setter->name); $accepted_done{$flag->setter->id}++; } - if ($flag->status eq '+' - && !any { $flag->setter->id == $_ } (@accepted_user_ids, @denied_user_ids)) { + if (!any { $flag->setter->id == $_ } (@accepted_user_ids, @denied_user_ids)) { + INFO('Clearing review+ flag set by ' . $flag->setter->name); push(@removed_flags, { id => $flag->id, setter => $flag->setter, status => 'X' }); } } @@ -453,6 +456,7 @@ sub process_revision_change { foreach my $user_id (@accepted_user_ids) { next if $accepted_done{$user_id}; my $user = Bugzilla::User->check({ id => $user_id, cache => 1 }); + INFO('Setting new review+ flag for ' . $user->name); push(@new_flags, { type_id => $flag_type->id, setter => $user, status => '+' }); } From 739676cf4b122cdec12981c2bc3a79c3f54aa7e4 Mon Sep 17 00:00:00 2001 From: dklawren Date: Tue, 8 May 2018 10:15:48 -0400 Subject: [PATCH 046/119] Bug 1440086 - Refactor PhabBugz extension code to use new User.pm module for better type checking --- extensions/PhabBugz/Extension.pm | 7 +- extensions/PhabBugz/lib/Feed.pm | 67 +++++++++++------ extensions/PhabBugz/lib/Project.pm | 28 +++---- extensions/PhabBugz/lib/Revision.pm | 63 +++++++--------- extensions/PhabBugz/lib/User.pm | 5 +- extensions/PhabBugz/lib/Util.pm | 103 ++++---------------------- extensions/PhabBugz/lib/WebService.pm | 10 +-- 7 files changed, 107 insertions(+), 176 deletions(-) diff --git a/extensions/PhabBugz/Extension.pm b/extensions/PhabBugz/Extension.pm index ee96901a29..c857c60abc 100644 --- a/extensions/PhabBugz/Extension.pm +++ b/extensions/PhabBugz/Extension.pm @@ -18,11 +18,6 @@ use Bugzilla::Extension::PhabBugz::Feed; our $VERSION = '0.01'; -BEGIN { - *Bugzilla::User::phab_phid = sub { return $_[0]->{phab_phid}; }; - *Bugzilla::User::phab_review_status = sub { return $_[0]->{phab_review_status}; }; -} - sub config_add_panels { my ($self, $args) = @_; my $modules = $args->{panel_modules}; @@ -85,7 +80,7 @@ sub install_filesystem { my $files = $args->{'files'}; my $extensionsdir = bz_locations()->{'extensionsdir'}; - my $scriptname = $extensionsdir . "/PhabBugz/bin/phabbugzd.pl"; + my $scriptname = $extensionsdir . "/PhabBugz/bin/phabbugz_feed.pl"; $files->{$scriptname} = { perms => Bugzilla::Install::Filesystem::WS_EXECUTE diff --git a/extensions/PhabBugz/lib/Feed.pm b/extensions/PhabBugz/lib/Feed.pm index 821ec65270..648844055f 100644 --- a/extensions/PhabBugz/lib/Feed.pm +++ b/extensions/PhabBugz/lib/Feed.pm @@ -31,7 +31,6 @@ use Bugzilla::Extension::PhabBugz::Util qw( add_security_sync_comments create_revision_attachment get_bug_role_phids - get_phab_bmo_ids get_project_phid get_security_sync_groups is_attachment_phab_revision @@ -146,10 +145,14 @@ sub feed_query { } # Skip changes done by phab-bot user - my $phab_users = get_phab_bmo_ids({ phids => [$author_phid] }); - if (@$phab_users) { - my $user = Bugzilla::User->new({ id => $phab_users->[0]->{id}, cache => 1 }); - if ($user->login eq PHAB_AUTOMATION_USER) { + my $phab_user = Bugzilla::Extension::PhabBugz::User->new_from_query( + { + phids => [ $author_phid ] + } + ); + + if ($phab_user && $phab_user->bugzilla_id) { + if ($phab_user->bugzilla_user->login eq PHAB_AUTOMATION_USER) { INFO("SKIPPING: Change made by phabricator user"); $self->save_last_id($story_id, 'feed'); next; @@ -256,11 +259,10 @@ sub group_query { ); } - if ( my @group_members = get_group_members($group) ) { - INFO("Setting group members for " . $project->name); - $project->set_members( \@group_members ); - $project->update(); - } + INFO("Setting group members for " . $project->name); + my @group_members = get_group_members($group); + $project->set_members( \@group_members ); + $project->update(); } } @@ -414,15 +416,28 @@ sub process_revision_change { my (@accepted_phids, @denied_phids, @accepted_user_ids, @denied_user_ids); unless ($revision->status eq 'changes-planned' || $revision->status eq 'needs-review') { foreach my $reviewer (@{ $revision->reviewers }) { - push(@accepted_phids, $reviewer->phab_phid) if $reviewer->phab_review_status eq 'accepted'; - push(@denied_phids, $reviewer->phab_phid) if $reviewer->phab_review_status eq 'rejected'; + push(@accepted_phids, $reviewer->phid) if $reviewer->{phab_review_status} eq 'accepted'; + push(@denied_phids, $reviewer->phid) if $reviewer->{phab_review_status} eq 'rejected'; } } - my $phab_users = get_phab_bmo_ids({ phids => \@accepted_phids }); - @accepted_user_ids = map { $_->{id} } @$phab_users; - $phab_users = get_phab_bmo_ids({ phids => \@denied_phids }); - @denied_user_ids = map { $_->{id} } @$phab_users; + if ( @accepted_phids ) { + my $phab_users = Bugzilla::Extension::PhabBugz::User->match( + { + phids => \@accepted_phids + } + ); + @accepted_user_ids = map { $_->bugzilla_user->id } @$phab_users; + } + + if ( @denied_phids ) { + my $phab_users = Bugzilla::Extension::PhabBugz::User->match( + { + phids => \@denied_phids + } + ); + @denied_user_ids = map { $_->bugzilla_user->id } @$phab_users; + } my %reviewers_hash = map { $_->name => 1 } @{ $revision->reviewers }; @@ -712,24 +727,28 @@ sub save_last_id { sub get_group_members { my ($group) = @_; + my $group_obj = ref $group ? $group : Bugzilla::Group->check( { name => $group, cache => 1 } ); my $members_all = $group_obj->members_complete(); - my %users; + + my @userids; foreach my $name ( keys %$members_all ) { foreach my $user ( @{ $members_all->{$name} } ) { - $users{ $user->id } = $user; + push @userids, $user->id; } } + return if !@userids; + # Look up the phab ids for these users - my $phab_users = get_phab_bmo_ids( { ids => [ keys %users ] } ); - foreach my $phab_user ( @{$phab_users} ) { - $users{ $phab_user->{id} }->{phab_phid} = $phab_user->{phid}; - } + my $phab_users = Bugzilla::Extension::PhabBugz::User->match( + { + ids => \@userids + } + ); - # We only need users who have accounts in phabricator - return grep { $_->phab_phid } values %users; + return map { $_->phid } @$phab_users; } 1; diff --git a/extensions/PhabBugz/lib/Project.pm b/extensions/PhabBugz/lib/Project.pm index cbf1bdcafd..c52e1a6614 100644 --- a/extensions/PhabBugz/lib/Project.pm +++ b/extensions/PhabBugz/lib/Project.pm @@ -9,15 +9,14 @@ package Bugzilla::Extension::PhabBugz::Project; use 5.10.1; use Moo; +use Scalar::Util qw(blessed); use Types::Standard -all; use Type::Utils; use Bugzilla::Error; use Bugzilla::Util qw(trim); -use Bugzilla::Extension::PhabBugz::Util qw( - request - get_phab_bmo_ids -); +use Bugzilla::Extension::PhabBugz::User; +use Bugzilla::Extension::PhabBugz::Util qw(request); ######################### # Initialization # @@ -281,20 +280,20 @@ sub set_description { sub add_member { my ( $self, $member ) = @_; $self->{add_members} ||= []; - my $member_phid = blessed $member ? $member->phab_phid : $member; + my $member_phid = blessed $member ? $member->phid : $member; push( @{ $self->{add_members} }, $member_phid ); } sub remove_member { my ( $self, $member ) = @_; $self->{remove_members} ||= []; - my $member_phid = blessed $member ? $member->phab_phid : $member; + my $member_phid = blessed $member ? $member->phid : $member; push( @{ $self->{remove_members} }, $member_phid ); } sub set_members { my ( $self, $members ) = @_; - $self->{set_members} = [ map { $_->phab_phid } @$members ]; + $self->{set_members} = [ map { blessed $_ ? $_->phid : $_ } @$members ]; } sub set_policy { @@ -318,16 +317,13 @@ sub _build_members { return [] if !@phids; - my $users = get_phab_bmo_ids( { phids => \@phids } ); + my $users = Bugzilla::Extension::PhabBugz::User->match( + { + phids => \@phids + } + ); - my @members; - foreach my $user (@$users) { - my $member = Bugzilla::User->new( { id => $user->{id}, cache => 1 } ); - $member->{phab_phid} = $user->{phid}; - push( @members, $member ); - } - - return \@members; + return [ map { $_->bugzilla_user } @$users ]; } 1; diff --git a/extensions/PhabBugz/lib/Revision.pm b/extensions/PhabBugz/lib/Revision.pm index 98c3196c2f..950779f0d6 100644 --- a/extensions/PhabBugz/lib/Revision.pm +++ b/extensions/PhabBugz/lib/Revision.pm @@ -17,10 +17,8 @@ use Type::Utils; use Bugzilla::Bug; use Bugzilla::Error; use Bugzilla::Util qw(trim); -use Bugzilla::Extension::PhabBugz::Util qw( - get_phab_bmo_ids - request -); +use Bugzilla::Extension::PhabBugz::User; +use Bugzilla::Extension::PhabBugz::Util qw(request); ######################### # Initialization # @@ -284,12 +282,13 @@ sub _build_bug { sub _build_author { my ($self) = @_; return $self->{author} if $self->{author}; - my $users = get_phab_bmo_ids( { phids => [ $self->author_phid ] } ); - if (@$users) { - $self->{author} = - new Bugzilla::User( { id => $users->[0]->{id}, cache => 1 } ); - $self->{author}->{phab_phid} = $self->author_phid; - return $self->{author}; + my $phab_user = Bugzilla::Extension::PhabBugz::User->new_from_query( + { + phids => [ $self->author_phid ] + } + ); + if ($phab_user) { + return $self->{author} = $phab_user->bugzilla_user; } } @@ -306,22 +305,22 @@ sub _build_reviewers { return [] unless @phids; - my $users = get_phab_bmo_ids( { phids => \@phids } ); + my $users = Bugzilla::Extension::PhabBugz::User->match( + { + phids => \@phids + } + ); - my @reviewers; foreach my $user (@$users) { - my $reviewer = Bugzilla::User->new( { id => $user->{id}, cache => 1 } ); - $reviewer->{phab_phid} = $user->{phid}; foreach my $reviewer_data ( @{ $self->reviewers_raw } ) { - if ( $reviewer_data->{reviewerPHID} eq $user->{phid} ) { - $reviewer->{phab_review_status} = $reviewer_data->{status}; + if ( $reviewer_data->{reviewerPHID} eq $user->phid ) { + $user->{phab_review_status} = $reviewer_data->{status}; last; } } - push @reviewers, $reviewer; } - return \@reviewers; + return $self->{reviewers} = $users; } sub _build_subscribers { @@ -335,19 +334,13 @@ sub _build_subscribers { push @phids, $phid; } - my $users = get_phab_bmo_ids( { phids => \@phids } ); - - return [] unless @phids; - - my @subscribers; - foreach my $user (@$users) { - my $subscriber = - Bugzilla::User->new( { id => $user->{id}, cache => 1 } ); - $subscriber->{phab_phid} = $user->{phid}; - push @subscribers, $subscriber; - } + my $users = Bugzilla::Extension::PhabBugz::User->math( + { + phids => \@phids + } + ); - return \@subscribers; + return $self->{subscribers} = $users; } ######################### @@ -364,27 +357,27 @@ sub add_comment { sub add_reviewer { my ( $self, $reviewer ) = @_; $self->{add_reviewers} ||= []; - my $reviewer_phid = blessed $reviewer ? $reviewer->phab_phid : $reviewer; + my $reviewer_phid = blessed $reviewer ? $reviewer->phid : $reviewer; push @{ $self->{add_reviewers} }, $reviewer_phid; } sub remove_reviewer { my ( $self, $reviewer ) = @_; $self->{remove_reviewers} ||= []; - my $reviewer_phid = blessed $reviewer ? $reviewer->phab_phid : $reviewer; + my $reviewer_phid = blessed $reviewer ? $reviewer->phid : $reviewer; push @{ $self->{remove_reviewers} }, $reviewer_phid; } sub set_reviewers { my ( $self, $reviewers ) = @_; - $self->{set_reviewers} = [ map { $_->phab_phid } @$reviewers ]; + $self->{set_reviewers} = [ map { $_->phid } @$reviewers ]; } sub add_subscriber { my ( $self, $subscriber ) = @_; $self->{add_subscribers} ||= []; my $subscriber_phid = - blessed $subscriber ? $subscriber->phab_phid : $subscriber; + blessed $subscriber ? $subscriber->phid : $subscriber; push @{ $self->{add_subscribers} }, $subscriber_phid; } @@ -392,7 +385,7 @@ sub remove_subscriber { my ( $self, $subscriber ) = @_; $self->{remove_subscribers} ||= []; my $subscriber_phid = - blessed $subscriber ? $subscriber->phab_phid : $subscriber; + blessed $subscriber ? $subscriber->phid : $subscriber; push @{ $self->{remove_subscribers} }, $subscriber_phid; } diff --git a/extensions/PhabBugz/lib/User.pm b/extensions/PhabBugz/lib/User.pm index 3b2d87e606..9d4e9eef49 100644 --- a/extensions/PhabBugz/lib/User.pm +++ b/extensions/PhabBugz/lib/User.pm @@ -116,14 +116,14 @@ sub match { my ( $class, $params ) = @_; # BMO id search takes precedence if bugzilla_ids is used. - my $bugzilla_ids = delete $params->{bugzilla_ids}; + my $bugzilla_ids = delete $params->{ids}; if ($bugzilla_ids) { my $bugzilla_data = $class->get_phab_bugzilla_ids( { ids => $bugzilla_ids } ); $params->{phids} = [ map { $_->{phid} } @$bugzilla_data ]; } - return [] if !$params->{phids}; + return [] if !@{ $params->{phids} }; # Look for BMO external user id in external-accounts attachment my $data = { @@ -177,6 +177,7 @@ sub get_phab_bugzilla_ids { # Store new values in memcache for later retrieval foreach my $user ( @{ $result->{result} } ) { + next if !$user->{phid}; $memcache->set( { key => "phab_user_bugzilla_id_" . $user->{id}, diff --git a/extensions/PhabBugz/lib/Util.pm b/extensions/PhabBugz/lib/Util.pm index 42da79c39d..916455e242 100644 --- a/extensions/PhabBugz/lib/Util.pm +++ b/extensions/PhabBugz/lib/Util.pm @@ -34,10 +34,7 @@ our @EXPORT_OK = qw( edit_revision_policy get_attachment_revisions get_bug_role_phids - get_members_by_bmo_id - get_members_by_phid get_needs_review - get_phab_bmo_ids get_project_phid get_revisions_by_ids get_revisions_by_phids @@ -134,7 +131,13 @@ sub get_bug_role_phids { push(@bug_users, $bug->qa_contact) if $bug->qa_contact; push(@bug_users, @{ $bug->cc_users }) if @{ $bug->cc_users }; - return get_members_by_bmo_id(\@bug_users); + my $phab_users = Bugzilla::Extension::PhabBugz::User->match( + { + ids => [ map { $_->id } @bug_users ] + } + ); + + return [ map { $_->phid } @$phab_users ]; } sub create_private_revision_policy { @@ -354,84 +357,6 @@ sub set_project_members { return $result->{result}{object}{phid}; } -sub get_members_by_bmo_id { - my $users = shift; - - my $result = get_phab_bmo_ids({ ids => [ map { $_->id } @$users ] }); - - my @phab_ids; - foreach my $user (@$result) { - push(@phab_ids, $user->{phid}) - if ($user->{phid} && $user->{phid} =~ /^PHID-USER/); - } - - return \@phab_ids; -} - -sub get_members_by_phid { - my $phids = shift; - - my $result = get_phab_bmo_ids({ phids => $phids }); - - my @bmo_ids; - foreach my $user (@$result) { - push(@bmo_ids, $user->{id}) - if ($user->{phid} && $user->{phid} =~ /^PHID-USER/); - } - - return \@bmo_ids; -} - -sub get_phab_bmo_ids { - my ($params) = @_; - my $memcache = Bugzilla->memcached; - - # Try to find the values in memcache first - my @results; - if ($params->{ids}) { - my @bmo_ids = @{ $params->{ids} }; - for (my $i = 0; $i < @bmo_ids; $i++) { - my $phid = $memcache->get({ key => "phab_user_bmo_id_" . $bmo_ids[$i] }); - if ($phid) { - push(@results, { - id => $bmo_ids[$i], - phid => $phid - }); - splice(@bmo_ids, $i, 1); - } - } - $params->{ids} = \@bmo_ids; - } - - if ($params->{phids}) { - my @phids = @{ $params->{phids} }; - for (my $i = 0; $i < @phids; $i++) { - my $bmo_id = $memcache->get({ key => "phab_user_phid_" . $phids[$i] }); - if ($bmo_id) { - push(@results, { - id => $bmo_id, - phid => $phids[$i] - }); - splice(@phids, $i, 1); - } - } - $params->{phids} = \@phids; - } - - my $result = request('bugzilla.account.search', $params); - - # Store new values in memcache for later retrieval - foreach my $user (@{ $result->{result} }) { - $memcache->set({ key => "phab_user_bmo_id_" . $user->{id}, - value => $user->{phid} }); - $memcache->set({ key => "phab_user_phid_" . $user->{phid}, - value => $user->{id} }); - push(@results, $user); - } - - return \@results; -} - sub is_attachment_phab_revision { my ($attachment) = @_; return ($attachment->contenttype eq PHAB_CONTENT_TYPE @@ -559,9 +484,13 @@ sub get_needs_review { $user //= Bugzilla->user; return unless $user->id; - my $ids = get_members_by_bmo_id([$user]); - return [] unless @$ids; - my $phid_user = $ids->[0]; + my $phab_user = Bugzilla::Extension::PhabBugz::User->new_from_query( + { + ids => [ $user->id ] + } + ); + + return [] unless $phab_user; my $diffs = request( 'differential.revision.search', @@ -570,7 +499,7 @@ sub get_needs_review { reviewers => 1, }, constraints => { - reviewerPHIDs => [$phid_user], + reviewerPHIDs => [$phab_user->phid], statuses => [qw( needs-review )], }, order => 'newest', @@ -584,7 +513,7 @@ sub get_needs_review { foreach my $diff (@{ $diffs->{result}{data} }) { my $attachments = delete $diff->{attachments}; my $reviewers = $attachments->{reviewers}{reviewers}; - my $review = first { $_->{reviewerPHID} eq $phid_user } @$reviewers; + my $review = first { $_->{reviewerPHID} eq $phab_user->phid } @$reviewers; $diff->{fields}{review_status} = $review->{status}; push @result, $diff; } diff --git a/extensions/PhabBugz/lib/WebService.pm b/extensions/PhabBugz/lib/WebService.pm index 32cea1b510..f018ba702e 100644 --- a/extensions/PhabBugz/lib/WebService.pm +++ b/extensions/PhabBugz/lib/WebService.pm @@ -30,7 +30,6 @@ use Bugzilla::Extension::PhabBugz::Util qw( create_revision_attachment edit_revision_policy get_bug_role_phids - get_phab_bmo_ids get_needs_review get_project_phid get_revisions_by_ids @@ -307,8 +306,7 @@ sub needs_review { my $reviews = get_needs_review(); - # map author phids to bugzilla users - my $author_id_map = get_phab_bmo_ids({ + my $authors = Bugzilla::Extension::PhabBugz::User->match({ phids => [ uniq grep { defined } @@ -316,9 +314,9 @@ sub needs_review { @$reviews ] }); - my %author_phab_to_id = map { $_->{phid} => $_->{id} } @$author_id_map; - my $author_users = Bugzilla::User->new_from_list([ map { $_->{id} } @$author_id_map ]); - my %author_id_to_user = map { $_->id => $_ } @$author_users; + + my %author_phab_to_id = map { $_->phid => $_->bugzilla_user->id } @$authors; + my %author_id_to_user = map { $_->bugzilla_user->id => $_->bugzilla_user } @$authors; # bug data my $visible_bugs = $user->visible_bugs([ From 2c2b5a5644c17e2a9cc2d56fdbea76a25a24280f Mon Sep 17 00:00:00 2001 From: Dylan William Hardison Date: Wed, 9 May 2018 10:28:26 -0400 Subject: [PATCH 047/119] Revert "Bug 1440086 - Refactor PhabBugz extension code to use new User.pm module for better type checking" This reverts commit 739676cf4b122cdec12981c2bc3a79c3f54aa7e4. --- extensions/PhabBugz/Extension.pm | 7 +- extensions/PhabBugz/lib/Feed.pm | 67 ++++++----------- extensions/PhabBugz/lib/Project.pm | 28 ++++--- extensions/PhabBugz/lib/Revision.pm | 63 +++++++++------- extensions/PhabBugz/lib/User.pm | 5 +- extensions/PhabBugz/lib/Util.pm | 103 ++++++++++++++++++++++---- extensions/PhabBugz/lib/WebService.pm | 10 ++- 7 files changed, 176 insertions(+), 107 deletions(-) diff --git a/extensions/PhabBugz/Extension.pm b/extensions/PhabBugz/Extension.pm index c857c60abc..ee96901a29 100644 --- a/extensions/PhabBugz/Extension.pm +++ b/extensions/PhabBugz/Extension.pm @@ -18,6 +18,11 @@ use Bugzilla::Extension::PhabBugz::Feed; our $VERSION = '0.01'; +BEGIN { + *Bugzilla::User::phab_phid = sub { return $_[0]->{phab_phid}; }; + *Bugzilla::User::phab_review_status = sub { return $_[0]->{phab_review_status}; }; +} + sub config_add_panels { my ($self, $args) = @_; my $modules = $args->{panel_modules}; @@ -80,7 +85,7 @@ sub install_filesystem { my $files = $args->{'files'}; my $extensionsdir = bz_locations()->{'extensionsdir'}; - my $scriptname = $extensionsdir . "/PhabBugz/bin/phabbugz_feed.pl"; + my $scriptname = $extensionsdir . "/PhabBugz/bin/phabbugzd.pl"; $files->{$scriptname} = { perms => Bugzilla::Install::Filesystem::WS_EXECUTE diff --git a/extensions/PhabBugz/lib/Feed.pm b/extensions/PhabBugz/lib/Feed.pm index 648844055f..821ec65270 100644 --- a/extensions/PhabBugz/lib/Feed.pm +++ b/extensions/PhabBugz/lib/Feed.pm @@ -31,6 +31,7 @@ use Bugzilla::Extension::PhabBugz::Util qw( add_security_sync_comments create_revision_attachment get_bug_role_phids + get_phab_bmo_ids get_project_phid get_security_sync_groups is_attachment_phab_revision @@ -145,14 +146,10 @@ sub feed_query { } # Skip changes done by phab-bot user - my $phab_user = Bugzilla::Extension::PhabBugz::User->new_from_query( - { - phids => [ $author_phid ] - } - ); - - if ($phab_user && $phab_user->bugzilla_id) { - if ($phab_user->bugzilla_user->login eq PHAB_AUTOMATION_USER) { + my $phab_users = get_phab_bmo_ids({ phids => [$author_phid] }); + if (@$phab_users) { + my $user = Bugzilla::User->new({ id => $phab_users->[0]->{id}, cache => 1 }); + if ($user->login eq PHAB_AUTOMATION_USER) { INFO("SKIPPING: Change made by phabricator user"); $self->save_last_id($story_id, 'feed'); next; @@ -259,10 +256,11 @@ sub group_query { ); } - INFO("Setting group members for " . $project->name); - my @group_members = get_group_members($group); - $project->set_members( \@group_members ); - $project->update(); + if ( my @group_members = get_group_members($group) ) { + INFO("Setting group members for " . $project->name); + $project->set_members( \@group_members ); + $project->update(); + } } } @@ -416,28 +414,15 @@ sub process_revision_change { my (@accepted_phids, @denied_phids, @accepted_user_ids, @denied_user_ids); unless ($revision->status eq 'changes-planned' || $revision->status eq 'needs-review') { foreach my $reviewer (@{ $revision->reviewers }) { - push(@accepted_phids, $reviewer->phid) if $reviewer->{phab_review_status} eq 'accepted'; - push(@denied_phids, $reviewer->phid) if $reviewer->{phab_review_status} eq 'rejected'; + push(@accepted_phids, $reviewer->phab_phid) if $reviewer->phab_review_status eq 'accepted'; + push(@denied_phids, $reviewer->phab_phid) if $reviewer->phab_review_status eq 'rejected'; } } - if ( @accepted_phids ) { - my $phab_users = Bugzilla::Extension::PhabBugz::User->match( - { - phids => \@accepted_phids - } - ); - @accepted_user_ids = map { $_->bugzilla_user->id } @$phab_users; - } - - if ( @denied_phids ) { - my $phab_users = Bugzilla::Extension::PhabBugz::User->match( - { - phids => \@denied_phids - } - ); - @denied_user_ids = map { $_->bugzilla_user->id } @$phab_users; - } + my $phab_users = get_phab_bmo_ids({ phids => \@accepted_phids }); + @accepted_user_ids = map { $_->{id} } @$phab_users; + $phab_users = get_phab_bmo_ids({ phids => \@denied_phids }); + @denied_user_ids = map { $_->{id} } @$phab_users; my %reviewers_hash = map { $_->name => 1 } @{ $revision->reviewers }; @@ -727,28 +712,24 @@ sub save_last_id { sub get_group_members { my ($group) = @_; - my $group_obj = ref $group ? $group : Bugzilla::Group->check( { name => $group, cache => 1 } ); my $members_all = $group_obj->members_complete(); - - my @userids; + my %users; foreach my $name ( keys %$members_all ) { foreach my $user ( @{ $members_all->{$name} } ) { - push @userids, $user->id; + $users{ $user->id } = $user; } } - return if !@userids; - # Look up the phab ids for these users - my $phab_users = Bugzilla::Extension::PhabBugz::User->match( - { - ids => \@userids - } - ); + my $phab_users = get_phab_bmo_ids( { ids => [ keys %users ] } ); + foreach my $phab_user ( @{$phab_users} ) { + $users{ $phab_user->{id} }->{phab_phid} = $phab_user->{phid}; + } - return map { $_->phid } @$phab_users; + # We only need users who have accounts in phabricator + return grep { $_->phab_phid } values %users; } 1; diff --git a/extensions/PhabBugz/lib/Project.pm b/extensions/PhabBugz/lib/Project.pm index c52e1a6614..cbf1bdcafd 100644 --- a/extensions/PhabBugz/lib/Project.pm +++ b/extensions/PhabBugz/lib/Project.pm @@ -9,14 +9,15 @@ package Bugzilla::Extension::PhabBugz::Project; use 5.10.1; use Moo; -use Scalar::Util qw(blessed); use Types::Standard -all; use Type::Utils; use Bugzilla::Error; use Bugzilla::Util qw(trim); -use Bugzilla::Extension::PhabBugz::User; -use Bugzilla::Extension::PhabBugz::Util qw(request); +use Bugzilla::Extension::PhabBugz::Util qw( + request + get_phab_bmo_ids +); ######################### # Initialization # @@ -280,20 +281,20 @@ sub set_description { sub add_member { my ( $self, $member ) = @_; $self->{add_members} ||= []; - my $member_phid = blessed $member ? $member->phid : $member; + my $member_phid = blessed $member ? $member->phab_phid : $member; push( @{ $self->{add_members} }, $member_phid ); } sub remove_member { my ( $self, $member ) = @_; $self->{remove_members} ||= []; - my $member_phid = blessed $member ? $member->phid : $member; + my $member_phid = blessed $member ? $member->phab_phid : $member; push( @{ $self->{remove_members} }, $member_phid ); } sub set_members { my ( $self, $members ) = @_; - $self->{set_members} = [ map { blessed $_ ? $_->phid : $_ } @$members ]; + $self->{set_members} = [ map { $_->phab_phid } @$members ]; } sub set_policy { @@ -317,13 +318,16 @@ sub _build_members { return [] if !@phids; - my $users = Bugzilla::Extension::PhabBugz::User->match( - { - phids => \@phids - } - ); + my $users = get_phab_bmo_ids( { phids => \@phids } ); - return [ map { $_->bugzilla_user } @$users ]; + my @members; + foreach my $user (@$users) { + my $member = Bugzilla::User->new( { id => $user->{id}, cache => 1 } ); + $member->{phab_phid} = $user->{phid}; + push( @members, $member ); + } + + return \@members; } 1; diff --git a/extensions/PhabBugz/lib/Revision.pm b/extensions/PhabBugz/lib/Revision.pm index 950779f0d6..98c3196c2f 100644 --- a/extensions/PhabBugz/lib/Revision.pm +++ b/extensions/PhabBugz/lib/Revision.pm @@ -17,8 +17,10 @@ use Type::Utils; use Bugzilla::Bug; use Bugzilla::Error; use Bugzilla::Util qw(trim); -use Bugzilla::Extension::PhabBugz::User; -use Bugzilla::Extension::PhabBugz::Util qw(request); +use Bugzilla::Extension::PhabBugz::Util qw( + get_phab_bmo_ids + request +); ######################### # Initialization # @@ -282,13 +284,12 @@ sub _build_bug { sub _build_author { my ($self) = @_; return $self->{author} if $self->{author}; - my $phab_user = Bugzilla::Extension::PhabBugz::User->new_from_query( - { - phids => [ $self->author_phid ] - } - ); - if ($phab_user) { - return $self->{author} = $phab_user->bugzilla_user; + my $users = get_phab_bmo_ids( { phids => [ $self->author_phid ] } ); + if (@$users) { + $self->{author} = + new Bugzilla::User( { id => $users->[0]->{id}, cache => 1 } ); + $self->{author}->{phab_phid} = $self->author_phid; + return $self->{author}; } } @@ -305,22 +306,22 @@ sub _build_reviewers { return [] unless @phids; - my $users = Bugzilla::Extension::PhabBugz::User->match( - { - phids => \@phids - } - ); + my $users = get_phab_bmo_ids( { phids => \@phids } ); + my @reviewers; foreach my $user (@$users) { + my $reviewer = Bugzilla::User->new( { id => $user->{id}, cache => 1 } ); + $reviewer->{phab_phid} = $user->{phid}; foreach my $reviewer_data ( @{ $self->reviewers_raw } ) { - if ( $reviewer_data->{reviewerPHID} eq $user->phid ) { - $user->{phab_review_status} = $reviewer_data->{status}; + if ( $reviewer_data->{reviewerPHID} eq $user->{phid} ) { + $reviewer->{phab_review_status} = $reviewer_data->{status}; last; } } + push @reviewers, $reviewer; } - return $self->{reviewers} = $users; + return \@reviewers; } sub _build_subscribers { @@ -334,13 +335,19 @@ sub _build_subscribers { push @phids, $phid; } - my $users = Bugzilla::Extension::PhabBugz::User->math( - { - phids => \@phids - } - ); + my $users = get_phab_bmo_ids( { phids => \@phids } ); + + return [] unless @phids; + + my @subscribers; + foreach my $user (@$users) { + my $subscriber = + Bugzilla::User->new( { id => $user->{id}, cache => 1 } ); + $subscriber->{phab_phid} = $user->{phid}; + push @subscribers, $subscriber; + } - return $self->{subscribers} = $users; + return \@subscribers; } ######################### @@ -357,27 +364,27 @@ sub add_comment { sub add_reviewer { my ( $self, $reviewer ) = @_; $self->{add_reviewers} ||= []; - my $reviewer_phid = blessed $reviewer ? $reviewer->phid : $reviewer; + my $reviewer_phid = blessed $reviewer ? $reviewer->phab_phid : $reviewer; push @{ $self->{add_reviewers} }, $reviewer_phid; } sub remove_reviewer { my ( $self, $reviewer ) = @_; $self->{remove_reviewers} ||= []; - my $reviewer_phid = blessed $reviewer ? $reviewer->phid : $reviewer; + my $reviewer_phid = blessed $reviewer ? $reviewer->phab_phid : $reviewer; push @{ $self->{remove_reviewers} }, $reviewer_phid; } sub set_reviewers { my ( $self, $reviewers ) = @_; - $self->{set_reviewers} = [ map { $_->phid } @$reviewers ]; + $self->{set_reviewers} = [ map { $_->phab_phid } @$reviewers ]; } sub add_subscriber { my ( $self, $subscriber ) = @_; $self->{add_subscribers} ||= []; my $subscriber_phid = - blessed $subscriber ? $subscriber->phid : $subscriber; + blessed $subscriber ? $subscriber->phab_phid : $subscriber; push @{ $self->{add_subscribers} }, $subscriber_phid; } @@ -385,7 +392,7 @@ sub remove_subscriber { my ( $self, $subscriber ) = @_; $self->{remove_subscribers} ||= []; my $subscriber_phid = - blessed $subscriber ? $subscriber->phid : $subscriber; + blessed $subscriber ? $subscriber->phab_phid : $subscriber; push @{ $self->{remove_subscribers} }, $subscriber_phid; } diff --git a/extensions/PhabBugz/lib/User.pm b/extensions/PhabBugz/lib/User.pm index 9d4e9eef49..3b2d87e606 100644 --- a/extensions/PhabBugz/lib/User.pm +++ b/extensions/PhabBugz/lib/User.pm @@ -116,14 +116,14 @@ sub match { my ( $class, $params ) = @_; # BMO id search takes precedence if bugzilla_ids is used. - my $bugzilla_ids = delete $params->{ids}; + my $bugzilla_ids = delete $params->{bugzilla_ids}; if ($bugzilla_ids) { my $bugzilla_data = $class->get_phab_bugzilla_ids( { ids => $bugzilla_ids } ); $params->{phids} = [ map { $_->{phid} } @$bugzilla_data ]; } - return [] if !@{ $params->{phids} }; + return [] if !$params->{phids}; # Look for BMO external user id in external-accounts attachment my $data = { @@ -177,7 +177,6 @@ sub get_phab_bugzilla_ids { # Store new values in memcache for later retrieval foreach my $user ( @{ $result->{result} } ) { - next if !$user->{phid}; $memcache->set( { key => "phab_user_bugzilla_id_" . $user->{id}, diff --git a/extensions/PhabBugz/lib/Util.pm b/extensions/PhabBugz/lib/Util.pm index 916455e242..42da79c39d 100644 --- a/extensions/PhabBugz/lib/Util.pm +++ b/extensions/PhabBugz/lib/Util.pm @@ -34,7 +34,10 @@ our @EXPORT_OK = qw( edit_revision_policy get_attachment_revisions get_bug_role_phids + get_members_by_bmo_id + get_members_by_phid get_needs_review + get_phab_bmo_ids get_project_phid get_revisions_by_ids get_revisions_by_phids @@ -131,13 +134,7 @@ sub get_bug_role_phids { push(@bug_users, $bug->qa_contact) if $bug->qa_contact; push(@bug_users, @{ $bug->cc_users }) if @{ $bug->cc_users }; - my $phab_users = Bugzilla::Extension::PhabBugz::User->match( - { - ids => [ map { $_->id } @bug_users ] - } - ); - - return [ map { $_->phid } @$phab_users ]; + return get_members_by_bmo_id(\@bug_users); } sub create_private_revision_policy { @@ -357,6 +354,84 @@ sub set_project_members { return $result->{result}{object}{phid}; } +sub get_members_by_bmo_id { + my $users = shift; + + my $result = get_phab_bmo_ids({ ids => [ map { $_->id } @$users ] }); + + my @phab_ids; + foreach my $user (@$result) { + push(@phab_ids, $user->{phid}) + if ($user->{phid} && $user->{phid} =~ /^PHID-USER/); + } + + return \@phab_ids; +} + +sub get_members_by_phid { + my $phids = shift; + + my $result = get_phab_bmo_ids({ phids => $phids }); + + my @bmo_ids; + foreach my $user (@$result) { + push(@bmo_ids, $user->{id}) + if ($user->{phid} && $user->{phid} =~ /^PHID-USER/); + } + + return \@bmo_ids; +} + +sub get_phab_bmo_ids { + my ($params) = @_; + my $memcache = Bugzilla->memcached; + + # Try to find the values in memcache first + my @results; + if ($params->{ids}) { + my @bmo_ids = @{ $params->{ids} }; + for (my $i = 0; $i < @bmo_ids; $i++) { + my $phid = $memcache->get({ key => "phab_user_bmo_id_" . $bmo_ids[$i] }); + if ($phid) { + push(@results, { + id => $bmo_ids[$i], + phid => $phid + }); + splice(@bmo_ids, $i, 1); + } + } + $params->{ids} = \@bmo_ids; + } + + if ($params->{phids}) { + my @phids = @{ $params->{phids} }; + for (my $i = 0; $i < @phids; $i++) { + my $bmo_id = $memcache->get({ key => "phab_user_phid_" . $phids[$i] }); + if ($bmo_id) { + push(@results, { + id => $bmo_id, + phid => $phids[$i] + }); + splice(@phids, $i, 1); + } + } + $params->{phids} = \@phids; + } + + my $result = request('bugzilla.account.search', $params); + + # Store new values in memcache for later retrieval + foreach my $user (@{ $result->{result} }) { + $memcache->set({ key => "phab_user_bmo_id_" . $user->{id}, + value => $user->{phid} }); + $memcache->set({ key => "phab_user_phid_" . $user->{phid}, + value => $user->{id} }); + push(@results, $user); + } + + return \@results; +} + sub is_attachment_phab_revision { my ($attachment) = @_; return ($attachment->contenttype eq PHAB_CONTENT_TYPE @@ -484,13 +559,9 @@ sub get_needs_review { $user //= Bugzilla->user; return unless $user->id; - my $phab_user = Bugzilla::Extension::PhabBugz::User->new_from_query( - { - ids => [ $user->id ] - } - ); - - return [] unless $phab_user; + my $ids = get_members_by_bmo_id([$user]); + return [] unless @$ids; + my $phid_user = $ids->[0]; my $diffs = request( 'differential.revision.search', @@ -499,7 +570,7 @@ sub get_needs_review { reviewers => 1, }, constraints => { - reviewerPHIDs => [$phab_user->phid], + reviewerPHIDs => [$phid_user], statuses => [qw( needs-review )], }, order => 'newest', @@ -513,7 +584,7 @@ sub get_needs_review { foreach my $diff (@{ $diffs->{result}{data} }) { my $attachments = delete $diff->{attachments}; my $reviewers = $attachments->{reviewers}{reviewers}; - my $review = first { $_->{reviewerPHID} eq $phab_user->phid } @$reviewers; + my $review = first { $_->{reviewerPHID} eq $phid_user } @$reviewers; $diff->{fields}{review_status} = $review->{status}; push @result, $diff; } diff --git a/extensions/PhabBugz/lib/WebService.pm b/extensions/PhabBugz/lib/WebService.pm index f018ba702e..32cea1b510 100644 --- a/extensions/PhabBugz/lib/WebService.pm +++ b/extensions/PhabBugz/lib/WebService.pm @@ -30,6 +30,7 @@ use Bugzilla::Extension::PhabBugz::Util qw( create_revision_attachment edit_revision_policy get_bug_role_phids + get_phab_bmo_ids get_needs_review get_project_phid get_revisions_by_ids @@ -306,7 +307,8 @@ sub needs_review { my $reviews = get_needs_review(); - my $authors = Bugzilla::Extension::PhabBugz::User->match({ + # map author phids to bugzilla users + my $author_id_map = get_phab_bmo_ids({ phids => [ uniq grep { defined } @@ -314,9 +316,9 @@ sub needs_review { @$reviews ] }); - - my %author_phab_to_id = map { $_->phid => $_->bugzilla_user->id } @$authors; - my %author_id_to_user = map { $_->bugzilla_user->id => $_->bugzilla_user } @$authors; + my %author_phab_to_id = map { $_->{phid} => $_->{id} } @$author_id_map; + my $author_users = Bugzilla::User->new_from_list([ map { $_->{id} } @$author_id_map ]); + my %author_id_to_user = map { $_->id => $_ } @$author_users; # bug data my $visible_bugs = $user->visible_bugs([ From a5b780358d0e8c9cd611a644b3d6168daac711e3 Mon Sep 17 00:00:00 2001 From: dklawren Date: Fri, 11 May 2018 12:13:35 -0400 Subject: [PATCH 048/119] Bug 1460466 - Phab bot does not create r+ for acceptance when there are still blocking reviewers --- extensions/PhabBugz/lib/Feed.pm | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/extensions/PhabBugz/lib/Feed.pm b/extensions/PhabBugz/lib/Feed.pm index 821ec65270..a0012cc107 100644 --- a/extensions/PhabBugz/lib/Feed.pm +++ b/extensions/PhabBugz/lib/Feed.pm @@ -412,11 +412,9 @@ sub process_revision_change { # REVIEWER STATUSES my (@accepted_phids, @denied_phids, @accepted_user_ids, @denied_user_ids); - unless ($revision->status eq 'changes-planned' || $revision->status eq 'needs-review') { - foreach my $reviewer (@{ $revision->reviewers }) { - push(@accepted_phids, $reviewer->phab_phid) if $reviewer->phab_review_status eq 'accepted'; - push(@denied_phids, $reviewer->phab_phid) if $reviewer->phab_review_status eq 'rejected'; - } + foreach my $reviewer (@{ $revision->reviewers }) { + push(@accepted_phids, $reviewer->phab_phid) if $reviewer->phab_review_status eq 'accepted'; + push(@denied_phids, $reviewer->phab_phid) if $reviewer->phab_review_status eq 'rejected'; } my $phab_users = get_phab_bmo_ids({ phids => \@accepted_phids }); From a1d7038494ee5fb327b21b48c7d1e5fb75b39e72 Mon Sep 17 00:00:00 2001 From: dklawren Date: Fri, 11 May 2018 13:10:09 -0400 Subject: [PATCH 049/119] Bug 1440086 - Refactor PhabBugz extension code to use new User.pm module for better type checking --- extensions/PhabBugz/Extension.pm | 7 +- extensions/PhabBugz/lib/Feed.pm | 67 +++++++++++------ extensions/PhabBugz/lib/Project.pm | 28 +++---- extensions/PhabBugz/lib/Revision.pm | 63 +++++++--------- extensions/PhabBugz/lib/User.pm | 5 +- extensions/PhabBugz/lib/Util.pm | 103 ++++---------------------- extensions/PhabBugz/lib/WebService.pm | 10 +-- 7 files changed, 107 insertions(+), 176 deletions(-) diff --git a/extensions/PhabBugz/Extension.pm b/extensions/PhabBugz/Extension.pm index ee96901a29..c857c60abc 100644 --- a/extensions/PhabBugz/Extension.pm +++ b/extensions/PhabBugz/Extension.pm @@ -18,11 +18,6 @@ use Bugzilla::Extension::PhabBugz::Feed; our $VERSION = '0.01'; -BEGIN { - *Bugzilla::User::phab_phid = sub { return $_[0]->{phab_phid}; }; - *Bugzilla::User::phab_review_status = sub { return $_[0]->{phab_review_status}; }; -} - sub config_add_panels { my ($self, $args) = @_; my $modules = $args->{panel_modules}; @@ -85,7 +80,7 @@ sub install_filesystem { my $files = $args->{'files'}; my $extensionsdir = bz_locations()->{'extensionsdir'}; - my $scriptname = $extensionsdir . "/PhabBugz/bin/phabbugzd.pl"; + my $scriptname = $extensionsdir . "/PhabBugz/bin/phabbugz_feed.pl"; $files->{$scriptname} = { perms => Bugzilla::Install::Filesystem::WS_EXECUTE diff --git a/extensions/PhabBugz/lib/Feed.pm b/extensions/PhabBugz/lib/Feed.pm index a0012cc107..a7bb75148d 100644 --- a/extensions/PhabBugz/lib/Feed.pm +++ b/extensions/PhabBugz/lib/Feed.pm @@ -31,7 +31,6 @@ use Bugzilla::Extension::PhabBugz::Util qw( add_security_sync_comments create_revision_attachment get_bug_role_phids - get_phab_bmo_ids get_project_phid get_security_sync_groups is_attachment_phab_revision @@ -146,10 +145,14 @@ sub feed_query { } # Skip changes done by phab-bot user - my $phab_users = get_phab_bmo_ids({ phids => [$author_phid] }); - if (@$phab_users) { - my $user = Bugzilla::User->new({ id => $phab_users->[0]->{id}, cache => 1 }); - if ($user->login eq PHAB_AUTOMATION_USER) { + my $phab_user = Bugzilla::Extension::PhabBugz::User->new_from_query( + { + phids => [ $author_phid ] + } + ); + + if ($phab_user && $phab_user->bugzilla_id) { + if ($phab_user->bugzilla_user->login eq PHAB_AUTOMATION_USER) { INFO("SKIPPING: Change made by phabricator user"); $self->save_last_id($story_id, 'feed'); next; @@ -256,11 +259,10 @@ sub group_query { ); } - if ( my @group_members = get_group_members($group) ) { - INFO("Setting group members for " . $project->name); - $project->set_members( \@group_members ); - $project->update(); - } + INFO("Setting group members for " . $project->name); + my @group_members = get_group_members($group); + $project->set_members( \@group_members ); + $project->update(); } } @@ -413,14 +415,27 @@ sub process_revision_change { my (@accepted_phids, @denied_phids, @accepted_user_ids, @denied_user_ids); foreach my $reviewer (@{ $revision->reviewers }) { - push(@accepted_phids, $reviewer->phab_phid) if $reviewer->phab_review_status eq 'accepted'; - push(@denied_phids, $reviewer->phab_phid) if $reviewer->phab_review_status eq 'rejected'; + push(@accepted_phids, $reviewer->phid) if $reviewer->{phab_review_status} eq 'accepted'; + push(@denied_phids, $reviewer->phid) if $reviewer->{phab_review_status} eq 'rejected'; } - my $phab_users = get_phab_bmo_ids({ phids => \@accepted_phids }); - @accepted_user_ids = map { $_->{id} } @$phab_users; - $phab_users = get_phab_bmo_ids({ phids => \@denied_phids }); - @denied_user_ids = map { $_->{id} } @$phab_users; + if ( @accepted_phids ) { + my $phab_users = Bugzilla::Extension::PhabBugz::User->match( + { + phids => \@accepted_phids + } + ); + @accepted_user_ids = map { $_->bugzilla_user->id } @$phab_users; + } + + if ( @denied_phids ) { + my $phab_users = Bugzilla::Extension::PhabBugz::User->match( + { + phids => \@denied_phids + } + ); + @denied_user_ids = map { $_->bugzilla_user->id } @$phab_users; + } my %reviewers_hash = map { $_->name => 1 } @{ $revision->reviewers }; @@ -710,24 +725,28 @@ sub save_last_id { sub get_group_members { my ($group) = @_; + my $group_obj = ref $group ? $group : Bugzilla::Group->check( { name => $group, cache => 1 } ); my $members_all = $group_obj->members_complete(); - my %users; + + my @userids; foreach my $name ( keys %$members_all ) { foreach my $user ( @{ $members_all->{$name} } ) { - $users{ $user->id } = $user; + push @userids, $user->id; } } + return if !@userids; + # Look up the phab ids for these users - my $phab_users = get_phab_bmo_ids( { ids => [ keys %users ] } ); - foreach my $phab_user ( @{$phab_users} ) { - $users{ $phab_user->{id} }->{phab_phid} = $phab_user->{phid}; - } + my $phab_users = Bugzilla::Extension::PhabBugz::User->match( + { + ids => \@userids + } + ); - # We only need users who have accounts in phabricator - return grep { $_->phab_phid } values %users; + return map { $_->phid } @$phab_users; } 1; diff --git a/extensions/PhabBugz/lib/Project.pm b/extensions/PhabBugz/lib/Project.pm index cbf1bdcafd..c52e1a6614 100644 --- a/extensions/PhabBugz/lib/Project.pm +++ b/extensions/PhabBugz/lib/Project.pm @@ -9,15 +9,14 @@ package Bugzilla::Extension::PhabBugz::Project; use 5.10.1; use Moo; +use Scalar::Util qw(blessed); use Types::Standard -all; use Type::Utils; use Bugzilla::Error; use Bugzilla::Util qw(trim); -use Bugzilla::Extension::PhabBugz::Util qw( - request - get_phab_bmo_ids -); +use Bugzilla::Extension::PhabBugz::User; +use Bugzilla::Extension::PhabBugz::Util qw(request); ######################### # Initialization # @@ -281,20 +280,20 @@ sub set_description { sub add_member { my ( $self, $member ) = @_; $self->{add_members} ||= []; - my $member_phid = blessed $member ? $member->phab_phid : $member; + my $member_phid = blessed $member ? $member->phid : $member; push( @{ $self->{add_members} }, $member_phid ); } sub remove_member { my ( $self, $member ) = @_; $self->{remove_members} ||= []; - my $member_phid = blessed $member ? $member->phab_phid : $member; + my $member_phid = blessed $member ? $member->phid : $member; push( @{ $self->{remove_members} }, $member_phid ); } sub set_members { my ( $self, $members ) = @_; - $self->{set_members} = [ map { $_->phab_phid } @$members ]; + $self->{set_members} = [ map { blessed $_ ? $_->phid : $_ } @$members ]; } sub set_policy { @@ -318,16 +317,13 @@ sub _build_members { return [] if !@phids; - my $users = get_phab_bmo_ids( { phids => \@phids } ); + my $users = Bugzilla::Extension::PhabBugz::User->match( + { + phids => \@phids + } + ); - my @members; - foreach my $user (@$users) { - my $member = Bugzilla::User->new( { id => $user->{id}, cache => 1 } ); - $member->{phab_phid} = $user->{phid}; - push( @members, $member ); - } - - return \@members; + return [ map { $_->bugzilla_user } @$users ]; } 1; diff --git a/extensions/PhabBugz/lib/Revision.pm b/extensions/PhabBugz/lib/Revision.pm index 98c3196c2f..d87ca8bd2a 100644 --- a/extensions/PhabBugz/lib/Revision.pm +++ b/extensions/PhabBugz/lib/Revision.pm @@ -17,10 +17,8 @@ use Type::Utils; use Bugzilla::Bug; use Bugzilla::Error; use Bugzilla::Util qw(trim); -use Bugzilla::Extension::PhabBugz::Util qw( - get_phab_bmo_ids - request -); +use Bugzilla::Extension::PhabBugz::User; +use Bugzilla::Extension::PhabBugz::Util qw(request); ######################### # Initialization # @@ -284,12 +282,13 @@ sub _build_bug { sub _build_author { my ($self) = @_; return $self->{author} if $self->{author}; - my $users = get_phab_bmo_ids( { phids => [ $self->author_phid ] } ); - if (@$users) { - $self->{author} = - new Bugzilla::User( { id => $users->[0]->{id}, cache => 1 } ); - $self->{author}->{phab_phid} = $self->author_phid; - return $self->{author}; + my $phab_user = Bugzilla::Extension::PhabBugz::User->new_from_query( + { + phids => [ $self->author_phid ] + } + ); + if ($phab_user) { + return $self->{author} = $phab_user->bugzilla_user; } } @@ -306,22 +305,22 @@ sub _build_reviewers { return [] unless @phids; - my $users = get_phab_bmo_ids( { phids => \@phids } ); + my $users = Bugzilla::Extension::PhabBugz::User->match( + { + phids => \@phids + } + ); - my @reviewers; foreach my $user (@$users) { - my $reviewer = Bugzilla::User->new( { id => $user->{id}, cache => 1 } ); - $reviewer->{phab_phid} = $user->{phid}; foreach my $reviewer_data ( @{ $self->reviewers_raw } ) { - if ( $reviewer_data->{reviewerPHID} eq $user->{phid} ) { - $reviewer->{phab_review_status} = $reviewer_data->{status}; + if ( $reviewer_data->{reviewerPHID} eq $user->phid ) { + $user->{phab_review_status} = $reviewer_data->{status}; last; } } - push @reviewers, $reviewer; } - return \@reviewers; + return $self->{reviewers} = $users; } sub _build_subscribers { @@ -335,19 +334,13 @@ sub _build_subscribers { push @phids, $phid; } - my $users = get_phab_bmo_ids( { phids => \@phids } ); - - return [] unless @phids; - - my @subscribers; - foreach my $user (@$users) { - my $subscriber = - Bugzilla::User->new( { id => $user->{id}, cache => 1 } ); - $subscriber->{phab_phid} = $user->{phid}; - push @subscribers, $subscriber; - } + my $users = Bugzilla::Extension::PhabBugz::User->match( + { + phids => \@phids + } + ); - return \@subscribers; + return $self->{subscribers} = $users; } ######################### @@ -364,27 +357,27 @@ sub add_comment { sub add_reviewer { my ( $self, $reviewer ) = @_; $self->{add_reviewers} ||= []; - my $reviewer_phid = blessed $reviewer ? $reviewer->phab_phid : $reviewer; + my $reviewer_phid = blessed $reviewer ? $reviewer->phid : $reviewer; push @{ $self->{add_reviewers} }, $reviewer_phid; } sub remove_reviewer { my ( $self, $reviewer ) = @_; $self->{remove_reviewers} ||= []; - my $reviewer_phid = blessed $reviewer ? $reviewer->phab_phid : $reviewer; + my $reviewer_phid = blessed $reviewer ? $reviewer->phid : $reviewer; push @{ $self->{remove_reviewers} }, $reviewer_phid; } sub set_reviewers { my ( $self, $reviewers ) = @_; - $self->{set_reviewers} = [ map { $_->phab_phid } @$reviewers ]; + $self->{set_reviewers} = [ map { $_->phid } @$reviewers ]; } sub add_subscriber { my ( $self, $subscriber ) = @_; $self->{add_subscribers} ||= []; my $subscriber_phid = - blessed $subscriber ? $subscriber->phab_phid : $subscriber; + blessed $subscriber ? $subscriber->phid : $subscriber; push @{ $self->{add_subscribers} }, $subscriber_phid; } @@ -392,7 +385,7 @@ sub remove_subscriber { my ( $self, $subscriber ) = @_; $self->{remove_subscribers} ||= []; my $subscriber_phid = - blessed $subscriber ? $subscriber->phab_phid : $subscriber; + blessed $subscriber ? $subscriber->phid : $subscriber; push @{ $self->{remove_subscribers} }, $subscriber_phid; } diff --git a/extensions/PhabBugz/lib/User.pm b/extensions/PhabBugz/lib/User.pm index 3b2d87e606..9d4e9eef49 100644 --- a/extensions/PhabBugz/lib/User.pm +++ b/extensions/PhabBugz/lib/User.pm @@ -116,14 +116,14 @@ sub match { my ( $class, $params ) = @_; # BMO id search takes precedence if bugzilla_ids is used. - my $bugzilla_ids = delete $params->{bugzilla_ids}; + my $bugzilla_ids = delete $params->{ids}; if ($bugzilla_ids) { my $bugzilla_data = $class->get_phab_bugzilla_ids( { ids => $bugzilla_ids } ); $params->{phids} = [ map { $_->{phid} } @$bugzilla_data ]; } - return [] if !$params->{phids}; + return [] if !@{ $params->{phids} }; # Look for BMO external user id in external-accounts attachment my $data = { @@ -177,6 +177,7 @@ sub get_phab_bugzilla_ids { # Store new values in memcache for later retrieval foreach my $user ( @{ $result->{result} } ) { + next if !$user->{phid}; $memcache->set( { key => "phab_user_bugzilla_id_" . $user->{id}, diff --git a/extensions/PhabBugz/lib/Util.pm b/extensions/PhabBugz/lib/Util.pm index 42da79c39d..916455e242 100644 --- a/extensions/PhabBugz/lib/Util.pm +++ b/extensions/PhabBugz/lib/Util.pm @@ -34,10 +34,7 @@ our @EXPORT_OK = qw( edit_revision_policy get_attachment_revisions get_bug_role_phids - get_members_by_bmo_id - get_members_by_phid get_needs_review - get_phab_bmo_ids get_project_phid get_revisions_by_ids get_revisions_by_phids @@ -134,7 +131,13 @@ sub get_bug_role_phids { push(@bug_users, $bug->qa_contact) if $bug->qa_contact; push(@bug_users, @{ $bug->cc_users }) if @{ $bug->cc_users }; - return get_members_by_bmo_id(\@bug_users); + my $phab_users = Bugzilla::Extension::PhabBugz::User->match( + { + ids => [ map { $_->id } @bug_users ] + } + ); + + return [ map { $_->phid } @$phab_users ]; } sub create_private_revision_policy { @@ -354,84 +357,6 @@ sub set_project_members { return $result->{result}{object}{phid}; } -sub get_members_by_bmo_id { - my $users = shift; - - my $result = get_phab_bmo_ids({ ids => [ map { $_->id } @$users ] }); - - my @phab_ids; - foreach my $user (@$result) { - push(@phab_ids, $user->{phid}) - if ($user->{phid} && $user->{phid} =~ /^PHID-USER/); - } - - return \@phab_ids; -} - -sub get_members_by_phid { - my $phids = shift; - - my $result = get_phab_bmo_ids({ phids => $phids }); - - my @bmo_ids; - foreach my $user (@$result) { - push(@bmo_ids, $user->{id}) - if ($user->{phid} && $user->{phid} =~ /^PHID-USER/); - } - - return \@bmo_ids; -} - -sub get_phab_bmo_ids { - my ($params) = @_; - my $memcache = Bugzilla->memcached; - - # Try to find the values in memcache first - my @results; - if ($params->{ids}) { - my @bmo_ids = @{ $params->{ids} }; - for (my $i = 0; $i < @bmo_ids; $i++) { - my $phid = $memcache->get({ key => "phab_user_bmo_id_" . $bmo_ids[$i] }); - if ($phid) { - push(@results, { - id => $bmo_ids[$i], - phid => $phid - }); - splice(@bmo_ids, $i, 1); - } - } - $params->{ids} = \@bmo_ids; - } - - if ($params->{phids}) { - my @phids = @{ $params->{phids} }; - for (my $i = 0; $i < @phids; $i++) { - my $bmo_id = $memcache->get({ key => "phab_user_phid_" . $phids[$i] }); - if ($bmo_id) { - push(@results, { - id => $bmo_id, - phid => $phids[$i] - }); - splice(@phids, $i, 1); - } - } - $params->{phids} = \@phids; - } - - my $result = request('bugzilla.account.search', $params); - - # Store new values in memcache for later retrieval - foreach my $user (@{ $result->{result} }) { - $memcache->set({ key => "phab_user_bmo_id_" . $user->{id}, - value => $user->{phid} }); - $memcache->set({ key => "phab_user_phid_" . $user->{phid}, - value => $user->{id} }); - push(@results, $user); - } - - return \@results; -} - sub is_attachment_phab_revision { my ($attachment) = @_; return ($attachment->contenttype eq PHAB_CONTENT_TYPE @@ -559,9 +484,13 @@ sub get_needs_review { $user //= Bugzilla->user; return unless $user->id; - my $ids = get_members_by_bmo_id([$user]); - return [] unless @$ids; - my $phid_user = $ids->[0]; + my $phab_user = Bugzilla::Extension::PhabBugz::User->new_from_query( + { + ids => [ $user->id ] + } + ); + + return [] unless $phab_user; my $diffs = request( 'differential.revision.search', @@ -570,7 +499,7 @@ sub get_needs_review { reviewers => 1, }, constraints => { - reviewerPHIDs => [$phid_user], + reviewerPHIDs => [$phab_user->phid], statuses => [qw( needs-review )], }, order => 'newest', @@ -584,7 +513,7 @@ sub get_needs_review { foreach my $diff (@{ $diffs->{result}{data} }) { my $attachments = delete $diff->{attachments}; my $reviewers = $attachments->{reviewers}{reviewers}; - my $review = first { $_->{reviewerPHID} eq $phid_user } @$reviewers; + my $review = first { $_->{reviewerPHID} eq $phab_user->phid } @$reviewers; $diff->{fields}{review_status} = $review->{status}; push @result, $diff; } diff --git a/extensions/PhabBugz/lib/WebService.pm b/extensions/PhabBugz/lib/WebService.pm index 32cea1b510..f018ba702e 100644 --- a/extensions/PhabBugz/lib/WebService.pm +++ b/extensions/PhabBugz/lib/WebService.pm @@ -30,7 +30,6 @@ use Bugzilla::Extension::PhabBugz::Util qw( create_revision_attachment edit_revision_policy get_bug_role_phids - get_phab_bmo_ids get_needs_review get_project_phid get_revisions_by_ids @@ -307,8 +306,7 @@ sub needs_review { my $reviews = get_needs_review(); - # map author phids to bugzilla users - my $author_id_map = get_phab_bmo_ids({ + my $authors = Bugzilla::Extension::PhabBugz::User->match({ phids => [ uniq grep { defined } @@ -316,9 +314,9 @@ sub needs_review { @$reviews ] }); - my %author_phab_to_id = map { $_->{phid} => $_->{id} } @$author_id_map; - my $author_users = Bugzilla::User->new_from_list([ map { $_->{id} } @$author_id_map ]); - my %author_id_to_user = map { $_->id => $_ } @$author_users; + + my %author_phab_to_id = map { $_->phid => $_->bugzilla_user->id } @$authors; + my %author_id_to_user = map { $_->bugzilla_user->id => $_->bugzilla_user } @$authors; # bug data my $visible_bugs = $user->visible_bugs([ From 2ef89c7fecfbc3e8f44320489e4033440782862b Mon Sep 17 00:00:00 2001 From: Dylan William Hardison Date: Fri, 11 May 2018 14:34:45 -0400 Subject: [PATCH 050/119] no bug - make sure log4perl config is set --- vagrant_support/apache.j2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vagrant_support/apache.j2 b/vagrant_support/apache.j2 index 8316f1aa26..3135d8c258 100644 --- a/vagrant_support/apache.j2 +++ b/vagrant_support/apache.j2 @@ -1,7 +1,7 @@ PerlSwitches -wT PerlSetEnv USE_NYTPROF 0 PerlSetEnv BUGZILLA_UNSAFE_AUTH_DELEGATION 1 -PerlPassEnv LOG4PERL_CONFIG_FILE +PerlSetEnv LOG4PERL_CONFIG_FILE log4perl-vagrant.conf PerlSetEnv LOG4PERL_STDERR_DISABLE 1 PerlConfigRequire /vagrant/mod_perl.pl From fb8fb9b2c69e4d20c5a39a0595e65af8bdcc3161 Mon Sep 17 00:00:00 2001 From: dklawren Date: Fri, 11 May 2018 14:48:46 -0400 Subject: [PATCH 051/119] Bug 1458664 - Feed daemon when adding or updating a new project in Phabricator, it should fix permissions --- extensions/PhabBugz/lib/Feed.pm | 65 ++++++++++++++++++++++----------- 1 file changed, 44 insertions(+), 21 deletions(-) diff --git a/extensions/PhabBugz/lib/Feed.pm b/extensions/PhabBugz/lib/Feed.pm index a7bb75148d..8f02d8c3fa 100644 --- a/extensions/PhabBugz/lib/Feed.pm +++ b/extensions/PhabBugz/lib/Feed.pm @@ -18,6 +18,7 @@ use Try::Tiny; use Bugzilla::Constants; use Bugzilla::Error; +use Bugzilla::Field; use Bugzilla::Logging; use Bugzilla::Mailer; use Bugzilla::Search; @@ -221,49 +222,71 @@ sub group_query { INFO("Updating group memberships"); + # Pre setup before making changes + my $old_user = set_phab_user(); + # Loop through each group and perform the following: # # 1. Load flattened list of group members # 2. Check to see if Phab project exists for 'bmo-' # 3. Create if does not exist with locked down policy. - # 4. Set project members to exact list + # 4. Set project members to exact list including phab-bot user # 5. Profit my $sync_groups = Bugzilla::Group->match( { isactive => 1, isbuggroup => 1 } ); - foreach my $group (@$sync_groups) { + # Load phab-bot Phabricator user to add as a member of each project group later + my $phab_ids = get_phab_bmo_ids( { ids => [ Bugzilla->user->id ] } ); + my $phab_user = Bugzilla::User->new( { id => $phab_ids->[0]->{id}, cache => 1 } ); + $phab_user->{phab_phid} = $phab_ids->[0]->{phid}; + # secure-revision project that will be used for bmo group projects + my $secure_revision = + Bugzilla::Extension::PhabBugz::Project->new_from_query( + { + name => 'secure-revision' + } + ); + + foreach my $group (@$sync_groups) { # Create group project if one does not yet exist my $phab_project_name = 'bmo-' . $group->name; - my $project = Bugzilla::Extension::PhabBugz::Project->new_from_query( + my $project = + Bugzilla::Extension::PhabBugz::Project->new_from_query( { - name => $phab_project_name + name => $phab_project_name } ); + if ( !$project ) { - INFO("Project $project not found. Creating."); - my $secure_revision = - Bugzilla::Extension::PhabBugz::Project->new_from_query( - { - name => 'secure-revision' - } - ); + INFO("Project $phab_project_name not found. Creating."); $project = Bugzilla::Extension::PhabBugz::Project->create( - { - name => $phab_project_name, - description => 'BMO Security Group for ' . $group->name, - view_policy => $secure_revision->phid, - edit_policy => $secure_revision->phid, - join_policy => $secure_revision->phid - } + { + name => $phab_project_name, + description => 'BMO Security Group for ' . $group->name, + view_policy => $secure_revision->phid, + edit_policy => $secure_revision->phid, + join_policy => $secure_revision->phid + } ); } + else { + # Make sure that the group project permissions are set properly + INFO("Updating permissions on $phab_project_name"); + $project->set_policy( 'view', $secure_revision->phid ); + $project->set_policy( 'edit', $secure_revision->phid ); + $project->set_policy( 'join', $secure_revision->phid ); + } + # Make sure phab-bot also a member of the new project group so that it can + # make policy changes to the private revisions INFO("Setting group members for " . $project->name); - my @group_members = get_group_members($group); - $project->set_members( \@group_members ); + my @group_members = $self->get_group_members( $group ); + $project->set_members( [ ($phab_user, @group_members) ] ); $project->update(); } + + Bugzilla->set_user($old_user); } sub process_revision_change { @@ -724,7 +747,7 @@ sub save_last_id { } sub get_group_members { - my ($group) = @_; + my ( $self, $group ) = @_; my $group_obj = ref $group ? $group : Bugzilla::Group->check( { name => $group, cache => 1 } ); From b6f1553569efeaa0682ff9247c7fc0e93552609d Mon Sep 17 00:00:00 2001 From: Dylan William Hardison Date: Mon, 21 May 2018 09:04:50 -0400 Subject: [PATCH 052/119] no bug - update dependencies --- .circleci/config.yml | 2 +- Dockerfile | 2 +- Makefile.PL | 15 +++++++++++++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index cef28d2be0..341b48062c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -7,7 +7,7 @@ version: 2 defaults: bmo_slim_image: &bmo_slim_image - image: mozillabteam/bmo-slim:20180503.1 + image: mozillabteam/bmo-slim:20180518.1 user: app mysql_image: &mysql_image diff --git a/Dockerfile b/Dockerfile index bd16516da0..3ac3209375 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM mozillabteam/bmo-slim:20180503.1 +FROM mozillabteam/bmo-slim:20180518.1 ARG CI ARG CIRCLE_SHA1 diff --git a/Makefile.PL b/Makefile.PL index 96e340b4f7..cda7eefa14 100755 --- a/Makefile.PL +++ b/Makefile.PL @@ -33,8 +33,11 @@ BEGIN { # PREREQ_PM my %requires = ( + 'Alien::libcmark_gfm' => 0, 'Algorithm::BloomFilter' => '0.02', 'CGI' => '4.31', + 'CGI::Compile' => 0, + 'CGI::Emulate::PSGI' => 0, 'CPAN::Meta::Prereqs' => '2.132830', 'CPAN::Meta::Requirements' => '2.121', 'Class::XSAccessor' => '1.18', @@ -47,6 +50,7 @@ my %requires = ( 'Digest::SHA' => '5.47', 'Email::MIME' => '1.904', 'Email::Send' => '1.911', + 'FFI::Platypus' => 0, 'File::Slurp' => '9999.13', 'File::Slurper' => '0.012', 'Future' => '0.34', @@ -63,6 +67,7 @@ my %requires = ( 'Math::Random::ISAAC' => '1.0.1', 'Module::Metadata' => '1.000033', 'Module::Runtime' => '0.014', + 'Mojolicious' => '7.71', 'Moo' => '2.002004', 'MooX::StrictConstructor' => '0.008', 'Mozilla::CA' => '20160104', @@ -108,6 +113,16 @@ if ( $OSNAME eq 'linux' && -f '/etc/debian_version' ) { } my %optional_features = ( + argon2 => { + description => 'Support hashing passwords with Argon2', + prereqs => { + runtime => { + requires => { + 'Crypt::Argon2' => '0.004', + }, + }, + }, + }, smtp_auth => { description => 'SMTP Authentication', prereqs => { runtime => { requires => { 'Authen::SASL' => 0 } } }, From 8c039ca6047d0dd761e561def6618071dd955544 Mon Sep 17 00:00:00 2001 From: Bob Micheletto Date: Mon, 21 May 2018 17:44:49 -0400 Subject: [PATCH 053/119] Removes branching logic for bounce types and disables e-mail for any bounce. --- ses/index.cgi | 67 +++++++++++++++++++-------------------------------- 1 file changed, 25 insertions(+), 42 deletions(-) diff --git a/ses/index.cgi b/ses/index.cgi index 8abd98e24a..e36956b1d1 100755 --- a/ses/index.cgi +++ b/ses/index.cgi @@ -104,57 +104,40 @@ sub handle_notification { sub process_bounce { my ($notification) = @_; - my $type = $notification->{bounce}->{bounceType}; - if ( $type eq 'Transient' ) { + # disable each account that is bouncing + foreach my $recipient ( @{ $notification->{bounce}->{bouncedRecipients} } ) { + my $address = $recipient->{emailAddress}; + my $reason = sprintf '(%s) %s', $recipient->{action} // 'error', $recipient->{diagnosticCode} // 'unknown'; - # just log transient bounces - foreach my $recipient ( @{ $notification->{bounce}->{bouncedRecipients} } ) { - my $address = $recipient->{emailAddress}; - Bugzilla->audit("transient bounce for <$address>"); - } - } + my $user = Bugzilla::User->new( { name => $address, cache => 1 } ); + if ($user) { - elsif ( $type eq 'Permanent' ) { - - # disable each account that is permanently bouncing - foreach my $recipient ( @{ $notification->{bounce}->{bouncedRecipients} } ) { - my $address = $recipient->{emailAddress}; - my $reason = sprintf '(%s) %s', $recipient->{action} // 'error', $recipient->{diagnosticCode} // 'unknown'; - - my $user = Bugzilla::User->new( { name => $address, cache => 1 } ); - if ($user) { - - # never auto-disable admin accounts - if ( $user->in_group('admin') ) { - Bugzilla->audit("ignoring permanent bounce for admin <$address>: $reason"); - } - - else { - my $template = Bugzilla->template_inner(); - my $vars = { - mta => $notification->{bounce}->{reportingMTA} // 'unknown', - reason => $reason, - }; - my $disable_text; - $template->process( 'admin/users/bounce-disabled.txt.tmpl', $vars, \$disable_text ) - || die $template->error(); - - $user->set_disabledtext($disable_text); - $user->set_disable_mail(1); - $user->update(); - Bugzilla->audit( "permanent bounce for <$address> disabled userid-" . $user->id . ": $reason" ); - } + # never auto-disable admin accounts + if ( $user->in_group('admin') ) { + Bugzilla->audit("ignoring bounce for admin <$address>: $reason"); } else { - Bugzilla->audit("permanent bounce for <$address> has no user: $reason"); + my $template = Bugzilla->template_inner(); + my $vars = { + mta => $notification->{bounce}->{reportingMTA} // 'unknown', + reason => $reason, + }; + my $disable_text; + $template->process( 'admin/users/bounce-disabled.txt.tmpl', $vars, \$disable_text ) + || die $template->error(); + + $user->set_disabledtext($disable_text); + $user->set_disable_mail(1); + $user->update(); + Bugzilla->audit( "bounce for <$address> disabled userid-" . $user->id . ": $reason" ); } } - } - else { - WARN("Unsupported bounce type: $type\n"); + else { + Bugzilla->audit("bounce for <$address> has no user: $reason"); + } } respond( 200 => 'OK' ); From e3961fddcc9520e6b77a1de02ca8e1828e6925a9 Mon Sep 17 00:00:00 2001 From: Dylan William Hardison Date: Tue, 22 May 2018 10:42:46 -0400 Subject: [PATCH 054/119] no bug - fix deps on non-linux (#580) --- Makefile.PL | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.PL b/Makefile.PL index cda7eefa14..484e66d3d0 100755 --- a/Makefile.PL +++ b/Makefile.PL @@ -443,7 +443,7 @@ MAKE sub is_bmo_feature { local $_ = shift; - return 1 if $OSNAME eq 'linux' && /^linux/; + return $OSNAME eq 'linux' if /^linux/; return !m{ ^ (?: pg From 50a43f2b2147cc7fba37c45860afd27a8f256b17 Mon Sep 17 00:00:00 2001 From: dklawren Date: Tue, 22 May 2018 11:20:23 -0400 Subject: [PATCH 055/119] Bug 1462686 - Current phabbugz in bmo master still refers to get_phab_bmo_ids() which is no longer part of the code --- extensions/PhabBugz/lib/Feed.pm | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/extensions/PhabBugz/lib/Feed.pm b/extensions/PhabBugz/lib/Feed.pm index 8f02d8c3fa..96c0830a2b 100644 --- a/extensions/PhabBugz/lib/Feed.pm +++ b/extensions/PhabBugz/lib/Feed.pm @@ -236,9 +236,13 @@ sub group_query { my $sync_groups = Bugzilla::Group->match( { isactive => 1, isbuggroup => 1 } ); # Load phab-bot Phabricator user to add as a member of each project group later - my $phab_ids = get_phab_bmo_ids( { ids => [ Bugzilla->user->id ] } ); - my $phab_user = Bugzilla::User->new( { id => $phab_ids->[0]->{id}, cache => 1 } ); - $phab_user->{phab_phid} = $phab_ids->[0]->{phid}; + my $phab_bmo_user = Bugzilla::User->new( { name => PHAB_AUTOMATION_USER, cache => 1 } ); + my $phab_user = + Bugzilla::Extension::PhabBugz::User->new_from_query( + { + ids => [ $phab_bmo_user->id ] + } + ); # secure-revision project that will be used for bmo group projects my $secure_revision = From 8b54dc1e1ada823d5e6c655f295e52602681226b Mon Sep 17 00:00:00 2001 From: Dylan William Hardison Date: Tue, 22 May 2018 14:43:07 -0400 Subject: [PATCH 056/119] Bug 1461819 - Plack::Handler::Apache2 accidentally unsets $ENV{MOD_PERL} --- .htaccess | 5 +++-- Bugzilla/ModPerl.pm | 6 ------ helper.psgi | 35 ----------------------------------- 3 files changed, 3 insertions(+), 43 deletions(-) delete mode 100644 helper.psgi diff --git a/.htaccess b/.htaccess index 4c7e3bd206..ee7d296b04 100644 --- a/.htaccess +++ b/.htaccess @@ -12,6 +12,8 @@ Redirect permanent /bug_status.html https://bugzilla.mozilla.org/page.cgi?id=fie Redirect permanent /bugwritinghelp.html https://bugzilla.mozilla.org/page.cgi?id=bug-writing.html Redirect permanent /etiquette.html https://bugzilla.mozilla.org/page.cgi?id=etiquette.html Redirect permanent /duplicates.html https://bugzilla.mozilla.org/duplicates.cgi +Redirect permanent /quicksearch.html https://bugzilla.mozilla.org/page.cgi?id=quicksearch.html +Redirect permanent /bugwritinghelp.html https://bugzilla.mozilla.org/page.cgi?id=bug-writing.html RewriteEngine On # This rewrite rule skips over the rest, which is good because the load balancers @@ -27,8 +29,6 @@ RewriteRule ^__version__$ version.json [L] # heartbeat.cgi returns 200 if the DB and memcached are both working, and 500 otherwise. RewriteRule ^__heartbeat__$ heartbeat.cgi [L] -RewriteRule ^(\d+|quicksearch\.html|bugwritinghelp\.html)$ /helper/$1 [L] - RewriteRule ^static/v\d{4}\d{2}\d{2}\.\d+/(.+\.(?:js|css|woff2?|png|jpe?g|gif|ico|svg))$ $1 [NC,E=IMMUTABLE:1,L] Header set Cache-Control "public, max-age=31536000" env=REDIRECT_IMMUTABLE @@ -42,6 +42,7 @@ RewriteRule ^template_cache.deleteme/ - [F,L,NC] RewriteRule ^review$ page.cgi?id=splinter.html$1 [QSA] RewriteRule ^user_?profile$ page.cgi?id=user_profile.html$1 [QSA] RewriteRule ^request_defer$ page.cgi?id=request_defer.html$1 [QSA] +RewriteRule ^([0-9]+)$ show_bug.cgi?id=$1 [QSA] RewriteRule ^favicon\.ico$ extensions/BMO/web/images/favicon.ico RewriteRule ^form[\.:]itrequest$ enter_bug.cgi?product=Infrastructure+\%26+Operations&format=itrequest [QSA] RewriteRule ^form[\.:](mozlist|poweredby|presentation|trademark|recoverykey)$ enter_bug.cgi?product=mozilla.org&format=$1 [QSA] diff --git a/Bugzilla/ModPerl.pm b/Bugzilla/ModPerl.pm index 120dd82102..89eca95853 100644 --- a/Bugzilla/ModPerl.pm +++ b/Bugzilla/ModPerl.pm @@ -86,12 +86,6 @@ ErrorDocument 403 /errors/403.html ErrorDocument 404 /errors/404.html ErrorDocument 500 /errors/500.html - - SetHandler perl-script - PerlResponseHandler Plack::Handler::Apache2 - PerlSetVar psgi_app [% cgi_path %]/helper.psgi - - AddHandler perl-script .cgi # No need to PerlModule these because they're already defined in mod_perl.pl diff --git a/helper.psgi b/helper.psgi deleted file mode 100644 index cc8c648a81..0000000000 --- a/helper.psgi +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/perl -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. -# -# This Source Code Form is "Incompatible With Secondary Licenses", as -# defined by the Mozilla Public License, v. 2.0. - -use 5.10.1; -use strict; -use warnings; -use Plack::Request; -use Plack::Response; - -my $app = sub { - my $env = shift; - my $req = Plack::Request->new($env); - my $res = Plack::Response->new(404); - my $urlbase = Bugzilla->localconfig->{urlbase}; - my $path = $req->path; - - if ( $path eq '/quicksearch.html' ) { - $res->redirect( $urlbase . 'page.cgi?id=quicksearch.html', 301 ); - } - elsif ( $path eq '/bugwritinghelp.html') { - $res->redirect( $urlbase . 'page.cgi?id=bug-writing.html', 301 ); - } - elsif ( $path =~ m{^/(\d+)$}s ) { - $res->redirect( $urlbase . "show_bug.cgi?id=$1", 301 ); - } - else { - $res->body('not found'); - } - return $res->finalize; -}; \ No newline at end of file From ff60b109bcd1a83f79b45296a9bc679ad9fd1f6b Mon Sep 17 00:00:00 2001 From: Dylan William Hardison Date: Wed, 23 May 2018 09:10:55 -0400 Subject: [PATCH 057/119] Bug 1461400 - Log errors in webservices when undef values are passed to $self->type() --- Bugzilla/WebService/Server/XMLRPC.pm | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Bugzilla/WebService/Server/XMLRPC.pm b/Bugzilla/WebService/Server/XMLRPC.pm index fce865e888..6bb73af010 100644 --- a/Bugzilla/WebService/Server/XMLRPC.pm +++ b/Bugzilla/WebService/Server/XMLRPC.pm @@ -11,6 +11,7 @@ use 5.10.1; use strict; use warnings; +use Bugzilla::Logging; use XMLRPC::Transport::HTTP; use Bugzilla::WebService::Server; if ($ENV{MOD_PERL}) { @@ -32,8 +33,15 @@ BEGIN { if ($type eq 'dateTime') { # This is the XML-RPC implementation, see the README in Bugzilla/WebService/. # Our "base" implementation is in Bugzilla::WebService::Server. - $value = Bugzilla::WebService::Server->datetime_format_outbound($value); - $value =~ s/-//g; + if (defined $value) { + $value = Bugzilla::WebService::Server->datetime_format_outbound($value); + $value =~ s/-//g; + } + else { + my ($pkg, $file, $line) = caller; + my $class = ref $self; + ERROR("$class->type($type, undef) called from $pkg ($file line $line)"); + } } elsif ($type eq 'email') { $type = 'string'; From b0b55b7251c2e0bc1521010cfb2a42814e1247e4 Mon Sep 17 00:00:00 2001 From: Dylan William Hardison Date: Wed, 23 May 2018 10:10:12 -0400 Subject: [PATCH 058/119] no bug - fix-remove-non-public-data.pl should use use FindBin --- scripts/remove-non-public-data.pl | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/scripts/remove-non-public-data.pl b/scripts/remove-non-public-data.pl index fd379af79a..61c761c270 100755 --- a/scripts/remove-non-public-data.pl +++ b/scripts/remove-non-public-data.pl @@ -9,8 +9,17 @@ use 5.10.1; use strict; use warnings; -use lib qw(. lib local/lib/perl5); +use File::Basename qw(dirname); +use File::Spec::Functions qw(catfile catdir rel2abs); +use Cwd qw(realpath); +BEGIN { + require lib; + my $dir = rel2abs(catdir(dirname(__FILE__), '..')); + lib->import($dir, catdir($dir, 'lib'), catdir($dir, qw(local lib perl5))); +} + +use autodie; use Bugzilla; use Bugzilla::Constants; use List::MoreUtils qw(any); @@ -130,7 +139,8 @@ # run sanitiseme.pl print "running sanitizeme.pl\n"; -system "'$RealBin/sanitizeme.pl' --execute"; +my $sanitizeme = catfile(realpath(dirname(__FILE__)), 'sanitizeme.pl'); +system $sanitizeme, '--execute'; if ($dbh->selectrow_array("SELECT COUNT(*) FROM bug_group_map")) { die "sanitization failed\n"; From 077878d97b727daeb09fd61162099a458242cd7f Mon Sep 17 00:00:00 2001 From: Dylan William Hardison Date: Wed, 23 May 2018 10:18:17 -0400 Subject: [PATCH 059/119] bump version to 20180523.1 (#584) --- Bugzilla.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Bugzilla.pm b/Bugzilla.pm index 825ef46c85..897433e478 100644 --- a/Bugzilla.pm +++ b/Bugzilla.pm @@ -22,7 +22,7 @@ BEGIN { } } -our $VERSION = '20180508.1'; +our $VERSION = '20180523.1'; use Bugzilla::Auth; use Bugzilla::Auth::Persist::Cookie; From dbed2a62025d93c1c40eea35f44f5044ece36008 Mon Sep 17 00:00:00 2001 From: dklawren Date: Thu, 24 May 2018 16:33:28 -0400 Subject: [PATCH 060/119] Bug 1462685 - Use Phabricators Draft functionality to allow sending of initial revision email after BMO has updated the policies --- extensions/PhabBugz/lib/Feed.pm | 51 +++++++++++++++- extensions/PhabBugz/lib/WebService.pm | 59 ++++++++++++++++++- .../hook/global/user-error-errors.html.tmpl | 4 ++ 3 files changed, 112 insertions(+), 2 deletions(-) diff --git a/extensions/PhabBugz/lib/Feed.pm b/extensions/PhabBugz/lib/Feed.pm index 96c0830a2b..5d9d894f1f 100644 --- a/extensions/PhabBugz/lib/Feed.pm +++ b/extensions/PhabBugz/lib/Feed.pm @@ -14,6 +14,7 @@ use IO::Async::Loop; use List::Util qw(first); use List::MoreUtils qw(any); use Moo; +use Scalar::Util qw(blessed); use Try::Tiny; use Bugzilla::Constants; @@ -165,6 +166,51 @@ sub feed_query { }; $self->save_last_id($story_id, 'feed'); } + + # Process any build targets as well. + my $dbh = Bugzilla->dbh; + + INFO("Checking for revisions in draft mode"); + my $build_targets = $dbh->selectall_arrayref( + "SELECT name, value FROM phabbugz WHERE name LIKE 'build_target_%'", + { Slice => {} } + ); + + my $delete_build_target = $dbh->prepare( + "DELETE FROM phabbugz WHERE name = ? AND VALUE = ?" + ); + + foreach my $target (@$build_targets) { + my ($revision_id) = ($target->{name} =~ /^build_target_(\d+)$/); + my $build_target = $target->{value}; + + # FIXME: Remove debugging + use Data::Dumper; print STDERR Dumper $target; + + next unless $revision_id && $build_target; + + INFO("Processing revision $revision_id with build target $build_target"); + + my $revision = + Bugzilla::Extension::PhabBugz::Revision->new_from_query( + { + ids => [ int($revision_id) ] + } + ); + + with_writable_database { + $self->process_revision_change($revision); + }; + + # Set the build target to a passing status to + # allow the revision to exit draft state + request( 'harbormaster.sendmessage', { + buildTargetPHID => $build_target, + type => 'pass' + } ); + + $delete_build_target->execute($target->{name}, $target->{value}); + } } sub user_query { @@ -297,7 +343,10 @@ sub process_revision_change { my ($self, $revision_phid, $story_text) = @_; # Load the revision from Phabricator - my $revision = Bugzilla::Extension::PhabBugz::Revision->new_from_query({ phids => [ $revision_phid ] }); + my $revision = + blessed $revision_phid + ? $revision_phid + : Bugzilla::Extension::PhabBugz::Revision->new_from_query({ phids => [ $revision_phid ] }); my $secure_revision = Bugzilla::Extension::PhabBugz::Project->new_from_query( diff --git a/extensions/PhabBugz/lib/WebService.pm b/extensions/PhabBugz/lib/WebService.pm index f018ba702e..56afc93fed 100644 --- a/extensions/PhabBugz/lib/WebService.pm +++ b/extensions/PhabBugz/lib/WebService.pm @@ -20,7 +20,7 @@ use Bugzilla::Constants; use Bugzilla::Error; use Bugzilla::Extension::Push::Util qw(is_public); use Bugzilla::User; -use Bugzilla::Util qw(detaint_natural datetime_from time_ago); +use Bugzilla::Util qw(detaint_natural datetime_from time_ago trick_taint); use Bugzilla::WebService::Constants; use Bugzilla::Extension::PhabBugz::Constants; @@ -54,6 +54,7 @@ use constant PUBLIC_METHODS => qw( revision update_reviewer_statuses needs_review + set_build_target ); sub revision { @@ -398,8 +399,64 @@ sub _phabricator_precheck { unless $user->login eq PHAB_AUTOMATION_USER; } +sub set_build_target { + my ( $self, $params ) = @_; + + # Phabricator only supports sending credentials via HTTP Basic Auth + # so we exploit that function to pass in an API key as the password + # of basic auth. BMO does not support basic auth but does support + # use of API keys. + my $http_auth = Bugzilla->cgi->http('Authorization'); + $http_auth =~ s/^Basic\s+//; + $http_auth = decode_base64($http_auth); + my ($login, $api_key) = split(':', $http_auth); + $params->{'Bugzilla_login'} = $login; + $params->{'Bugzilla_api_key'} = $api_key; + + my $user = Bugzilla->login(LOGIN_REQUIRED); + + # Ensure PhabBugz is on + ThrowUserError('phabricator_not_enabled') + unless Bugzilla->params->{phabricator_enabled}; + + # Validate that the requesting user's email matches phab-bot + ThrowUserError('phabricator_unauthorized_user') + unless $user->login eq PHAB_AUTOMATION_USER; + + my $revision_id = $params->{revision_id}; + my $build_target = $params->{build_target}; + + ThrowUserError('invalid_phabricator_revision_id') + unless detaint_natural($revision_id); + + ThrowUserError('invalid_phabricator_build_target') + unless $build_target =~ /^PHID-HMBT-[a-zA-Z0-9]+$/; + trick_taint($build_target); + + Bugzilla->dbh->do( + "INSERT INTO phabbugz (name, value) VALUES (?, ?)", + undef, + 'build_target_' . $revision_id, + $build_target + ); + + return { result => 1 }; +} + sub rest_resources { return [ + # Set build target in Phabricator + qr{^/phabbugz/build_target/(\d+)/(PHID-HMBT-.*)$}, { + POST => { + method => 'set_build_target', + params => sub { + return { + revision_id => $_[0], + build_target => $_[1] + }; + } + } + }, # Revision creation qr{^/phabbugz/revision/([^/]+)$}, { POST => { diff --git a/extensions/PhabBugz/template/en/default/hook/global/user-error-errors.html.tmpl b/extensions/PhabBugz/template/en/default/hook/global/user-error-errors.html.tmpl index f1366e7b6e..0274f72ceb 100644 --- a/extensions/PhabBugz/template/en/default/hook/global/user-error-errors.html.tmpl +++ b/extensions/PhabBugz/template/en/default/hook/global/user-error-errors.html.tmpl @@ -22,6 +22,10 @@ [% title = "Invalid Phabricator Revision ID" %] You must provide a valid Phabricator revision ID. +[% ELSIF error == "invalid_phabricator_build_target" %] + [% title = "Invalid Phabricator Build Target" %] + You must provide a valid Phabricator Build Target PHID. + [% ELSIF error == "phabricator_not_enabled" %] [% title = "Phabricator Support Not Enabled" %] The Phabricator to Bugzilla library, PhabBugz, From 2f51d1b91da4f5bd3fdea589a14dfe7ef5536f8f Mon Sep 17 00:00:00 2001 From: dklawren Date: Thu, 24 May 2018 18:02:30 -0400 Subject: [PATCH 061/119] no bug - removed debugging code introduced by bug 1462685 --- extensions/PhabBugz/lib/Feed.pm | 3 --- 1 file changed, 3 deletions(-) diff --git a/extensions/PhabBugz/lib/Feed.pm b/extensions/PhabBugz/lib/Feed.pm index 5d9d894f1f..bd6f8e32c4 100644 --- a/extensions/PhabBugz/lib/Feed.pm +++ b/extensions/PhabBugz/lib/Feed.pm @@ -184,9 +184,6 @@ sub feed_query { my ($revision_id) = ($target->{name} =~ /^build_target_(\d+)$/); my $build_target = $target->{value}; - # FIXME: Remove debugging - use Data::Dumper; print STDERR Dumper $target; - next unless $revision_id && $build_target; INFO("Processing revision $revision_id with build target $build_target"); From 8e23b3e337e4f9508ea8e518ad75decd4f3bb2f8 Mon Sep 17 00:00:00 2001 From: Dylan William Hardison Date: Thu, 24 May 2018 21:18:14 -0400 Subject: [PATCH 062/119] Bug 1464226 - quicksearch can't search for "Resolution:---" --- Bugzilla/Search/Quicksearch.pm | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Bugzilla/Search/Quicksearch.pm b/Bugzilla/Search/Quicksearch.pm index 6897d22194..6c0253e27d 100644 --- a/Bugzilla/Search/Quicksearch.pm +++ b/Bugzilla/Search/Quicksearch.pm @@ -469,6 +469,11 @@ sub _handle_field_names { $value = $2; $value =~ s/\\(["'])/$1/g; } + # If the value is a pair of matching quotes, the person wanted the empty string + elsif ($value =~ /^(["'])\1$/ || $translated eq 'resolution' && $value eq '---') { + $value = ""; + $operator = "isempty"; + } addChart($translated, $operator, $value, $negate); } } @@ -659,6 +664,9 @@ sub negateComparisonType { if ($comparisonType eq 'anywords') { return 'nowords'; } + elsif ($comparisonType eq 'isempty') { + return 'isnotempty'; + } return "not$comparisonType"; } From 42d7fa7b22e40f97fb8f1b809719e22a86dc8ebe Mon Sep 17 00:00:00 2001 From: Dylan William Hardison Date: Tue, 29 May 2018 09:39:34 -0700 Subject: [PATCH 063/119] Bug 1464312 - Write script to undo the INACTIVE changes on bugs --- scripts/undo.pl | 203 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 203 insertions(+) create mode 100644 scripts/undo.pl diff --git a/scripts/undo.pl b/scripts/undo.pl new file mode 100644 index 0000000000..852c786fd3 --- /dev/null +++ b/scripts/undo.pl @@ -0,0 +1,203 @@ +#!/usr/bin/perl +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# This Source Code Form is "Incompatible With Secondary Licenses", as +# defined by the Mozilla Public License, v. 2.0. + +use 5.10.1; +use strict; +use warnings; + +use File::Basename qw(dirname); +use File::Spec::Functions qw(catfile catdir rel2abs); +use Cwd qw(realpath); +BEGIN { + require lib; + my $dir = rel2abs(catdir(dirname(__FILE__), '..')); + lib->import($dir, catdir($dir, 'lib'), catdir($dir, qw(local lib perl5))); +} + +use Bugzilla; +BEGIN { Bugzilla->extensions }; +use Bugzilla::Extension::UserProfile::Util qw(tag_for_recount_from_bug); + +use Try::Tiny; + +# not involved with kmag +my $query = q{ + resolution = 'INACTIVE' + AND NOT ( + triage_owner.login_name = 'mak77@bonardo.net' + AND bugs.priority IN ('P4', 'P5', '--') + ) + AND NOT ( + product.name = 'Toolkit' AND component.name = 'Add-ons Manager' + ) + AND NOT ( + product.name = 'Toolkit' AND component.name LIKE 'WebExtensions%' + ) + AND NOT ( + product.name = 'Core' AND component.name = 'XPConnect' + ) +}; +undo($query); + +sub undo { + my @args = @_; + my $changes = get_changes(@args); + my $comments = get_comments(@args); + + my %action; + while ($_ = $changes->()) { + push @{ $action{$_->{bug_id}}{$_->{bug_when}}{remove_activities} }, { id => $_->{change_id} }; + $action{ $_->{bug_id} }{ $_->{bug_when} }{change}{ $_->{field_name} } = { + replace => $_->{added}, + with => $_->{removed}, + }; + } + + while ($_ = $comments->()) { + push @{ $action{ $_->{bug_id} }{$_->{bug_when}}{remove_comments} }, { + id => $_->{comment_id}, + }; + } + + my $dbh = Bugzilla->dbh; + my @bug_ids = sort { $b <=> $a } keys %action; + say 'Found ', 0 + @bug_ids, ' bugs'; + foreach my $bug_id (@bug_ids) { + $dbh->bz_start_transaction; + say "Fix $bug_id"; + try { + my ($delta_ts) = $dbh->selectrow_array( + 'SELECT delta_ts FROM bugs WHERE bug_id = ?', + undef, + $bug_id); + my ($previous_last_ts) = $dbh->selectrow_array( + q{ + SELECT MAX(bug_when) FROM ( + SELECT bug_when FROM bugs_activity WHERE bug_id = ? AND bug_when < ? + UNION + SELECT bug_when FROM longdescs WHERE bug_id = ? AND bug_when < ? + UNION + SELECT creation_ts AS bug_when FROM bugs WHERE bug_id = ? + ) as changes ORDER BY bug_when DESC + }, + undef, + $bug_id, $delta_ts, + $bug_id, $delta_ts, + $bug_id, + ); + die 'cannot find previous last updated time' unless $previous_last_ts; + my $action = delete $action{$bug_id}{$delta_ts}; + if (keys %{ $action{$bug_id}{$delta_ts}}) { + die "skipping because more than one change\n"; + } + elsif (!$action) { + die "skipping because most recent change newer than automation change\n"; + } + foreach my $field (keys %{ $action->{change} }) { + my $change = $action->{change}{$field}; + if ($field eq 'cf_last_resolved' && !$change->{with}) { + $change->{with} = undef; + } + my $did = $dbh->do( + "UPDATE bugs SET $field = ? WHERE bug_id = ? AND $field = ?", + undef, $change->{with}, $bug_id, $change->{replace}, + ); + die "Failed to set $field to $change->{with}" unless $did; + } + $dbh->do('UPDATE bugs SET delta_ts = ?, lastdiffed = ? WHERE bug_id = ?', undef, + $previous_last_ts, $previous_last_ts, $bug_id ); + my $del_comments = $dbh->prepare('DELETE FROM longdescs WHERE comment_id = ?'); + my $del_activity = $dbh->prepare('DELETE FROM bugs_activity WHERE id = ?'); + foreach my $c (@{ $action->{remove_comments}}) { + $del_comments->execute($c->{id}) or die "failed to delete comment $c->{id}"; + } + foreach my $a (@{ $action->{remove_activities}}) { + $del_activity->execute($a->{id}) or die "failed to delete comment $a->{id}"; + } + tag_for_recount_from_bug($bug_id); + $dbh->bz_commit_transaction; + sleep 1; + } catch { + chomp $_; + say "Error updating $bug_id: $_"; + $dbh->bz_rollback_transaction; + }; + } + say 'Done.'; +} + +sub get_changes { + my ($where, @bind) = @_; + + my $sql = qq{ + SELECT + BA.id AS change_id, + BA.bug_id, + FD.name AS field_name, + BA.removed, + BA.added, + BA.bug_when + FROM + bugs_activity AS BA + JOIN + fielddefs AS FD ON BA.fieldid = FD.id + JOIN + profiles AS changer ON changer.userid = BA.who + JOIN + (SELECT + bug_id + FROM + bugs + JOIN products AS product ON product.id = product_id + JOIN components AS component ON component.id = component_id + LEFT JOIN profiles AS triage_owner ON triage_owner.userid = component.triage_owner_id + WHERE + $where + ) target_bugs ON BA.bug_id = target_bugs.bug_id + WHERE + changer.login_name = 'automation\@bmo.tld' + AND BA.bug_when BETWEEN '2018-05-22' AND '2018-05-26' + }; + my $sth = Bugzilla->dbh->prepare($sql); + $sth->execute(@bind); + + return sub { $sth->fetchrow_hashref }; +} + +sub get_comments { + my ($where, @bind) = @_; + + my $sql = qq{ + SELECT + C.comment_id AS comment_id, + C.bug_id AS bug_id, + C.bug_when + FROM + longdescs AS C + JOIN + profiles AS commenter ON commenter.userid = C.who + JOIN + (SELECT + bug_id + FROM + bugs + JOIN products AS product ON product.id = product_id + JOIN components AS component ON component.id = component_id + LEFT JOIN profiles AS triage_owner ON triage_owner.userid = component.triage_owner_id + WHERE + $where + ) target_bugs ON C.bug_id = target_bugs.bug_id + WHERE + commenter.login_name = 'automation\@bmo.tld' + AND C.bug_when BETWEEN '2018-05-22' AND '2018-05-26' + }; + my $sth = Bugzilla->dbh->prepare($sql); + $sth->execute(@bind); + + return sub { $sth->fetchrow_hashref }; +} From 283e9a03efc9185e1a86e2ea2de3b6693a42224b Mon Sep 17 00:00:00 2001 From: Dylan William Hardison Date: Tue, 29 May 2018 12:20:58 -0700 Subject: [PATCH 064/119] bump version to 20180529.1 (#589) --- .perlcriticrc | 1 + Bugzilla.pm | 2 +- scripts/undo.pl | 10 +++++----- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.perlcriticrc b/.perlcriticrc index 44254f64ee..0b8e4c862a 100644 --- a/.perlcriticrc +++ b/.perlcriticrc @@ -46,6 +46,7 @@ severity = 2 [-ValuesAndExpressions::ProhibitNoisyQuotes] [-ValuesAndExpressions::ProhibitNoisyQuotes] [-ValuesAndExpressions::ProhibitVersionStrings] +[-ValuesAndExpressions::ProhibitImplicitNewlines] [-Variables::ProhibitLocalVars] [-Variables::ProhibitPackageVars] diff --git a/Bugzilla.pm b/Bugzilla.pm index 897433e478..5928e2e3f1 100644 --- a/Bugzilla.pm +++ b/Bugzilla.pm @@ -22,7 +22,7 @@ BEGIN { } } -our $VERSION = '20180523.1'; +our $VERSION = '20180529.1'; use Bugzilla::Auth; use Bugzilla::Auth::Persist::Cookie; diff --git a/scripts/undo.pl b/scripts/undo.pl index 852c786fd3..5de3104fa0 100644 --- a/scripts/undo.pl +++ b/scripts/undo.pl @@ -65,7 +65,7 @@ sub undo { } my $dbh = Bugzilla->dbh; - my @bug_ids = sort { $b <=> $a } keys %action; + my @bug_ids = reverse sort { $a <=> $b } keys %action; say 'Found ', 0 + @bug_ids, ' bugs'; foreach my $bug_id (@bug_ids) { $dbh->bz_start_transaction; @@ -113,11 +113,11 @@ sub undo { $previous_last_ts, $previous_last_ts, $bug_id ); my $del_comments = $dbh->prepare('DELETE FROM longdescs WHERE comment_id = ?'); my $del_activity = $dbh->prepare('DELETE FROM bugs_activity WHERE id = ?'); - foreach my $c (@{ $action->{remove_comments}}) { - $del_comments->execute($c->{id}) or die "failed to delete comment $c->{id}"; + foreach my $comment (@{ $action->{remove_comments}}) { + $del_comments->execute($comment->{id}) or die "failed to delete comment $c->{id}"; } - foreach my $a (@{ $action->{remove_activities}}) { - $del_activity->execute($a->{id}) or die "failed to delete comment $a->{id}"; + foreach my $activity (@{ $action->{remove_activities}}) { + $del_activity->execute($activity->{id}) or die "failed to delete comment $a->{id}"; } tag_for_recount_from_bug($bug_id); $dbh->bz_commit_transaction; From bf3665f50d518aee6219b5f4143df01882661f0b Mon Sep 17 00:00:00 2001 From: Dylan William Hardison Date: Tue, 29 May 2018 13:41:18 -0700 Subject: [PATCH 065/119] bump version to 20180529.2 --- Bugzilla.pm | 2 +- scripts/undo.pl | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Bugzilla.pm b/Bugzilla.pm index 5928e2e3f1..6cffa66a1b 100644 --- a/Bugzilla.pm +++ b/Bugzilla.pm @@ -22,7 +22,7 @@ BEGIN { } } -our $VERSION = '20180529.1'; +our $VERSION = '20180529.2'; use Bugzilla::Auth; use Bugzilla::Auth::Persist::Cookie; diff --git a/scripts/undo.pl b/scripts/undo.pl index 5de3104fa0..24d6f594b2 100644 --- a/scripts/undo.pl +++ b/scripts/undo.pl @@ -114,10 +114,10 @@ sub undo { my $del_comments = $dbh->prepare('DELETE FROM longdescs WHERE comment_id = ?'); my $del_activity = $dbh->prepare('DELETE FROM bugs_activity WHERE id = ?'); foreach my $comment (@{ $action->{remove_comments}}) { - $del_comments->execute($comment->{id}) or die "failed to delete comment $c->{id}"; + $del_comments->execute($comment->{id}) or die "failed to delete comment $comment->{id}"; } foreach my $activity (@{ $action->{remove_activities}}) { - $del_activity->execute($activity->{id}) or die "failed to delete comment $a->{id}"; + $del_activity->execute($activity->{id}) or die "failed to delete comment $activity->{id}"; } tag_for_recount_from_bug($bug_id); $dbh->bz_commit_transaction; From 8bc6479753f26eba181d411387c315f7db082dc8 Mon Sep 17 00:00:00 2001 From: dklawren Date: Wed, 30 May 2018 09:54:59 -0400 Subject: [PATCH 066/119] Bug 1465225 - New changes for draft revisions can miss setting permissions on revisions without a bug id associated --- extensions/PhabBugz/lib/Feed.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/PhabBugz/lib/Feed.pm b/extensions/PhabBugz/lib/Feed.pm index bd6f8e32c4..a0d620246a 100644 --- a/extensions/PhabBugz/lib/Feed.pm +++ b/extensions/PhabBugz/lib/Feed.pm @@ -196,7 +196,7 @@ sub feed_query { ); with_writable_database { - $self->process_revision_change($revision); + $self->process_revision_change($revision, " created D" . $revision->id); }; # Set the build target to a passing status to From e2ff84039ad6976142fa9081e920593d94f2be8f Mon Sep 17 00:00:00 2001 From: dklawren Date: Wed, 30 May 2018 14:07:50 -0400 Subject: [PATCH 067/119] bump version to 20180530.1 --- Bugzilla.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Bugzilla.pm b/Bugzilla.pm index 6cffa66a1b..cb575433ea 100644 --- a/Bugzilla.pm +++ b/Bugzilla.pm @@ -22,7 +22,7 @@ BEGIN { } } -our $VERSION = '20180529.2'; +our $VERSION = '20180530.1'; use Bugzilla::Auth; use Bugzilla::Auth::Persist::Cookie; From 5460e9be3a80d353f7c49ca94833c08455440ce8 Mon Sep 17 00:00:00 2001 From: dklawren Date: Wed, 30 May 2018 22:12:59 -0400 Subject: [PATCH 068/119] Bug 1430905 - Remove legacy phabbugz code that is no longer needed --- extensions/PhabBugz/lib/Feed.pm | 13 +- extensions/PhabBugz/lib/Util.pm | 300 ++----------------- extensions/PhabBugz/lib/WebService.pm | 284 +----------------- extensions/Push/lib/Connector/Phabricator.pm | 97 +++--- 4 files changed, 81 insertions(+), 613 deletions(-) diff --git a/extensions/PhabBugz/lib/Feed.pm b/extensions/PhabBugz/lib/Feed.pm index a0d620246a..ff381ad262 100644 --- a/extensions/PhabBugz/lib/Feed.pm +++ b/extensions/PhabBugz/lib/Feed.pm @@ -33,10 +33,8 @@ use Bugzilla::Extension::PhabBugz::Util qw( add_security_sync_comments create_revision_attachment get_bug_role_phids - get_project_phid get_security_sync_groups is_attachment_phab_revision - make_revision_public request set_phab_user ); @@ -117,7 +115,7 @@ sub feed_query { # PROCESS NEW FEED TRANSACTIONS - INFO("Fetching new transactions"); + INFO("Fetching new stories"); my $story_last_id = $self->get_last_id('feed'); @@ -265,9 +263,6 @@ sub group_query { INFO("Updating group memberships"); - # Pre setup before making changes - my $old_user = set_phab_user(); - # Loop through each group and perform the following: # # 1. Load flattened list of group members @@ -332,8 +327,6 @@ sub group_query { $project->set_members( [ ($phab_user, @group_members) ] ); $project->update(); } - - Bugzilla->set_user($old_user); } sub process_revision_change { @@ -385,6 +378,10 @@ sub process_revision_change { # REVISION SECURITY POLICY + my $secure_revision = Bugzilla::Extension::PhabBugz::Project->new_from_query({ + name => 'secure-revision' + }); + # If bug is public then remove privacy policy if (!@{ $bug->groups_in }) { INFO('Bug is public so setting view/edit public'); diff --git a/extensions/PhabBugz/lib/Util.pm b/extensions/PhabBugz/lib/Util.pm index 916455e242..b24124edea 100644 --- a/extensions/PhabBugz/lib/Util.pm +++ b/extensions/PhabBugz/lib/Util.pm @@ -25,56 +25,19 @@ use Taint::Util qw(untaint); use base qw(Exporter); -our @EXPORT_OK = qw( - add_comment_to_revision +our @EXPORT = qw( add_security_sync_comments - create_private_revision_policy - create_project create_revision_attachment - edit_revision_policy get_attachment_revisions get_bug_role_phids get_needs_review - get_project_phid - get_revisions_by_ids - get_revisions_by_phids get_security_sync_groups intersect is_attachment_phab_revision - make_revision_private - make_revision_public request set_phab_user - set_project_members - set_revision_subscribers ); -sub get_revisions_by_ids { - my ($ids) = @_; - return _get_revisions({ ids => $ids }); -} - -sub get_revisions_by_phids { - my ($phids) = @_; - return _get_revisions({ phids => $phids }); -} - -sub _get_revisions { - my ($constraints) = @_; - - my $data = { - queryKey => 'all', - constraints => $constraints - }; - - my $result = request('differential.revision.search', $data); - - ThrowUserError('invalid_phabricator_revision_id') - unless (exists $result->{result}{data} && @{ $result->{result}{data} }); - - return $result->{result}{data}; -} - sub create_revision_attachment { my ( $bug, $revision, $timestamp ) = @_; @@ -131,230 +94,14 @@ sub get_bug_role_phids { push(@bug_users, $bug->qa_contact) if $bug->qa_contact; push(@bug_users, @{ $bug->cc_users }) if @{ $bug->cc_users }; - my $phab_users = Bugzilla::Extension::PhabBugz::User->match( - { - ids => [ map { $_->id } @bug_users ] - } - ); - - return [ map { $_->phid } @$phab_users ]; -} - -sub create_private_revision_policy { - my ( $groups ) = @_; - - my $data = { - objectType => 'DREV', - default => 'deny', - policy => [ - { - action => 'allow', - rule => 'PhabricatorSubscriptionsSubscribersPolicyRule' - }, - { - action => 'allow', - rule => 'PhabricatorDifferentialReviewersPolicyRule' - } - ] - }; - - if(scalar @$groups gt 0) { - my $project_phids = []; - foreach my $group (@$groups) { - my $phid = get_project_phid('bmo-' . $group); - push(@$project_phids, $phid) if $phid; - } - - ThrowUserError('invalid_phabricator_projects') unless @$project_phids; - - push(@{ $data->{policy} }, - { - action => 'allow', - rule => 'PhabricatorProjectsPolicyRule', - value => $project_phids, - } - ); - } - else { - my $secure_revision = Bugzilla::Extension::PhabBugz::Project->new_from_query({ - name => 'secure-revision' - }); - push(@{ $data->{policy} }, - { - action => 'allow', - value => $secure_revision->phid, - } - ); - } - - my $result = request('policy.create', $data); - return $result->{result}{phid}; -} - -sub make_revision_public { - my ($revision_phid) = @_; - return request('differential.revision.edit', { - transactions => [ - { - type => 'view', - value => 'public' - }, - { - type => 'edit', - value => 'users' - } - ], - objectIdentifier => $revision_phid - }); - -} - -sub make_revision_private { - my ($revision_phid) = @_; - - # When creating a private policy with no args it - # creates one with the secure-revision project. - my $private_policy = create_private_revision_policy(); - - return request('differential.revision.edit', { - transactions => [ - { - type => "view", - value => $private_policy->phid - }, - { - type => "edit", - value => $private_policy->phid - } - ], - objectIdentifier => $revision_phid - }); -} - -sub edit_revision_policy { - my ($revision_phid, $policy_phid, $subscribers) = @_; - - my $data = { - transactions => [ - { - type => 'view', - value => $policy_phid - }, - { - type => 'edit', - value => $policy_phid - } - ], - objectIdentifier => $revision_phid - }; - - if (@$subscribers) { - push(@{ $data->{transactions} }, { - type => 'subscribers.set', - value => $subscribers - }); - } - - return request('differential.revision.edit', $data); -} - -sub set_revision_subscribers { - my ($revision_phid, $subscribers) = @_; - - my $data = { - transactions => [ - { - type => 'subscribers.set', - value => $subscribers - } - ], - objectIdentifier => $revision_phid - }; - - return request('differential.revision.edit', $data); -} - -sub add_comment_to_revision { - my ($revision_phid, $comment) = @_; - - my $data = { - transactions => [ - { - type => 'comment', - value => $comment - } - ], - objectIdentifier => $revision_phid - }; - return request('differential.revision.edit', $data); -} - -sub get_project_phid { - my $project = shift; - my $memcache = Bugzilla->memcached; - - # Check memcache - my $project_phid = $memcache->get_config({ key => "phab_project_phid_" . $project }); - if (!$project_phid) { - my $data = { - queryKey => 'all', - constraints => { - name => $project - } - }; - - my $result = request('project.search', $data); - return undef - unless (exists $result->{result}{data} && @{ $result->{result}{data} }); - - # If name is used as a query param, we need to loop through and look - # for exact match as Conduit will tokenize the name instead of doing - # exact string match :( - foreach my $item ( @{ $result->{result}{data} } ) { - next if $item->{fields}{name} ne $project; - $project_phid = $item->{phid}; + my $phab_users = + Bugzilla::Extension::PhabBugz::User->match( + { + ids => [ map { $_->id } @bug_users ] } + ); - $memcache->set_config({ key => "phab_project_phid_" . $project, data => $project_phid }); - } - return $project_phid; -} - -sub create_project { - my ($project, $description, $members) = @_; - - my $secure_revision = Bugzilla::Extension::PhabBugz::Project->new_from_query({ - name => 'secure-revision' - }); - - my $data = { - transactions => [ - { type => 'name', value => $project }, - { type => 'description', value => $description }, - { type => 'edit', value => $secure_revision->phid }. - { type => 'join', value => $secure_revision->phid }, - { type => 'view', value => $secure_revision->phid }, - { type => 'icon', value => 'group' }, - { type => 'color', value => 'red' } - ] - }; - - my $result = request('project.edit', $data); - return $result->{result}{object}{phid}; -} - -sub set_project_members { - my ($project_id, $phab_user_ids) = @_; - - my $data = { - objectIdentifier => $project_id, - transactions => [ - { type => 'members.set', value => $phab_user_ids } - ] - }; - - my $result = request('project.edit', $data); - return $result->{result}{object}{phid}; + return [ map { $_->phid } @{ $phab_users } ]; } sub is_attachment_phab_revision { @@ -366,26 +113,29 @@ sub is_attachment_phab_revision { sub get_attachment_revisions { my $bug = shift; - my $revisions; - my @attachments = grep { is_attachment_phab_revision($_) } @{ $bug->attachments() }; - if (@attachments) { - my @revision_ids; - foreach my $attachment (@attachments) { - my ($revision_id) = - ( $attachment->filename =~ PHAB_ATTACHMENT_PATTERN ); - next if !$revision_id; - push( @revision_ids, int($revision_id) ); - } + return unless @attachments; - if (@revision_ids) { - $revisions = get_revisions_by_ids( \@revision_ids ); - } + my @revision_ids; + foreach my $attachment (@attachments) { + my ($revision_id) = + ( $attachment->filename =~ PHAB_ATTACHMENT_PATTERN ); + next if !$revision_id; + push( @revision_ids, int($revision_id) ); + } + + return unless @revision_ids; + + my @revisions; + foreach my $revision_id (@revision_ids) { + push @revisions, Bugzilla::Extension::PhabBugz::Revision->new_from_query({ + ids => [ $revision_id ] + }); } - return @$revisions; + return \@revisions; } sub request { @@ -462,7 +212,7 @@ sub add_security_sync_comments { my $phab_error_message = 'Revision is being made private due to unknown Bugzilla groups.'; foreach my $revision (@$revisions) { - add_comment_to_revision( $revision->{phid}, $phab_error_message ); + $revision->add_comment($phab_error_message); } my $num_revisions = scalar @$revisions; diff --git a/extensions/PhabBugz/lib/WebService.pm b/extensions/PhabBugz/lib/WebService.pm index 56afc93fed..5ca811d58f 100644 --- a/extensions/PhabBugz/lib/WebService.pm +++ b/extensions/PhabBugz/lib/WebService.pm @@ -13,30 +13,14 @@ use warnings; use base qw(Bugzilla::WebService); -use Bugzilla::Attachment; -use Bugzilla::Bug; -use Bugzilla::BugMail; use Bugzilla::Constants; -use Bugzilla::Error; -use Bugzilla::Extension::Push::Util qw(is_public); use Bugzilla::User; use Bugzilla::Util qw(detaint_natural datetime_from time_ago trick_taint); use Bugzilla::WebService::Constants; use Bugzilla::Extension::PhabBugz::Constants; use Bugzilla::Extension::PhabBugz::Util qw( - add_security_sync_comments - create_private_revision_policy - create_revision_attachment - edit_revision_policy - get_bug_role_phids get_needs_review - get_project_phid - get_revisions_by_ids - get_security_sync_groups - is_attachment_phab_revision - make_revision_public - request ); use DateTime (); @@ -45,90 +29,28 @@ use List::MoreUtils qw(any); use MIME::Base64 qw(decode_base64); use constant READ_ONLY => qw( + check_user_permission_for_bug needs_review ); use constant PUBLIC_METHODS => qw( check_user_permission_for_bug - obsolete_attachments - revision - update_reviewer_statuses needs_review set_build_target ); -sub revision { - my ($self, $params) = @_; - - # Phabricator only supports sending credentials via HTTP Basic Auth - # so we exploit that function to pass in an API key as the password - # of basic auth. BMO does not support basic auth but does support - # use of API keys. - my $http_auth = Bugzilla->cgi->http('Authorization'); - $http_auth =~ s/^Basic\s+//; - $http_auth = decode_base64($http_auth); - my ($login, $api_key) = split(':', $http_auth); - $params->{'Bugzilla_login'} = $login; - $params->{'Bugzilla_api_key'} = $api_key; - - my $user = Bugzilla->login(LOGIN_REQUIRED); - - # Prechecks - _phabricator_precheck($user); - - unless (defined $params->{revision} && detaint_natural($params->{revision})) { - ThrowCodeError('param_required', { param => 'revision' }) - } - - # Obtain more information about the revision from Phabricator - my $revision_id = $params->{revision}; - my $revisions = get_revisions_by_ids([$revision_id]); - my $revision = $revisions->[0]; - - my $revision_phid = $revision->{phid}; - my $revision_title = $revision->{fields}{title} || 'Unknown Description'; - my $bug_id = $revision->{fields}{'bugzilla.bug-id'}; - - my $bug = Bugzilla::Bug->new($bug_id); - - # If bug is public then remove privacy policy - my $result; - if (is_public($bug)) { - $result = make_revision_public($revision_id); - } - # else bug is private - else { - my @set_groups = get_security_sync_groups($bug); - - # If bug privacy groups do not have any matching synchronized groups, - # then leave revision private and it will have be dealt with manually. - if (!@set_groups) { - add_security_sync_comments($revisions, $bug); - } - - my $policy_phid = create_private_revision_policy($bug, \@set_groups); - my $subscribers = get_bug_role_phids($bug); - $result = edit_revision_policy($revision_phid, $policy_phid, $subscribers); - } - - my $attachment = create_revision_attachment($bug, $revision_id, $revision_title); - - Bugzilla::BugMail::Send($bug_id, { changer => $user }); - - return { - result => $result, - attachment_id => $attachment->id, - attachment_link => Bugzilla->localconfig->{urlbase} . "attachment.cgi?id=" . $attachment->id - }; -} - sub check_user_permission_for_bug { my ($self, $params) = @_; my $user = Bugzilla->login(LOGIN_REQUIRED); - # Prechecks - _phabricator_precheck($user); + # Ensure PhabBugz is on + ThrowUserError('phabricator_not_enabled') + unless Bugzilla->params->{phabricator_enabled}; + + # Validate that the requesting user's email matches phab-bot + ThrowUserError('phabricator_unauthorized_user') + unless $user->login eq PHAB_AUTOMATION_USER; # Validate that a bug id and user id are provided ThrowUserError('phabricator_invalid_request_params') @@ -143,161 +65,6 @@ sub check_user_permission_for_bug { }; } -sub update_reviewer_statuses { - my ($self, $params) = @_; - - my $user = Bugzilla->login(LOGIN_REQUIRED); - - # Prechecks - _phabricator_precheck($user); - - my $revision_id = $params->{revision_id}; - unless (defined $revision_id && detaint_natural($revision_id)) { - ThrowCodeError('param_required', { param => 'revision_id' }) - } - - my $bug_id = $params->{bug_id}; - unless (defined $bug_id && detaint_natural($bug_id)) { - ThrowCodeError('param_required', { param => 'bug_id' }) - } - - my $accepted_user_ids = $params->{accepted_users}; - defined $accepted_user_ids - || ThrowCodeError('param_required', { param => 'accepted_users' }); - $accepted_user_ids = [ split(':', $accepted_user_ids) ]; - - my $denied_user_ids = $params->{denied_users}; - defined $denied_user_ids - || ThrowCodeError('param_required', { param => 'denied_users' }); - $denied_user_ids = [ split(':', $denied_user_ids) ]; - - my $bug = Bugzilla::Bug->check($bug_id); - - my @attachments = - grep { is_attachment_phab_revision($_) } @{ $bug->attachments() }; - - return { result => [] } if !@attachments; - - my $dbh = Bugzilla->dbh; - my ($timestamp) = $dbh->selectrow_array("SELECT NOW()"); - - my @updated_attach_ids; - foreach my $attachment (@attachments) { - my ($curr_revision_id) = ($attachment->filename =~ PHAB_ATTACHMENT_PATTERN); - next if $revision_id != $curr_revision_id; - - # Clear old flags if no longer accepted - my (@denied_flags, @new_flags, @removed_flags, %accepted_done, $flag_type); - foreach my $flag (@{ $attachment->flags }) { - next if $flag->type->name ne 'review'; - $flag_type = $flag->type if $flag->type->is_active; - if (any { $flag->setter->id == $_ } @$denied_user_ids) { - push(@denied_flags, { id => $flag->id, setter => $flag->setter, status => 'X' }); - } - if (any { $flag->setter->id == $_ } @$accepted_user_ids) { - $accepted_done{$flag->setter->id}++; - } - if ($flag->status eq '+' - && !any { $flag->setter->id == $_ } (@$accepted_user_ids, @$denied_user_ids)) { - push(@removed_flags, { id => $flag->id, setter => $flag->setter, status => 'X' }); - } - } - - $flag_type ||= first { $_->name eq 'review' && $_->is_active } @{ $attachment->flag_types }; - - # Create new flags - foreach my $user_id (@$accepted_user_ids) { - next if $accepted_done{$user_id}; - my $user = Bugzilla::User->check({ id => $user_id, cache => 1 }); - push(@new_flags, { type_id => $flag_type->id, setter => $user, status => '+' }); - } - - # Also add comment to for attachment update showing the user's name - # that changed the revision. - my $comment; - foreach my $flag_data (@new_flags) { - $comment .= $flag_data->{setter}->name . " has approved the revision.\n"; - } - foreach my $flag_data (@denied_flags) { - $comment .= $flag_data->{setter}->name . " has requested changes to the revision.\n"; - } - foreach my $flag_data (@removed_flags) { - $comment .= $flag_data->{setter}->name . " has been removed from the revision.\n"; - } - - if ($comment) { - $comment .= "\n" . Bugzilla->params->{phabricator_base_uri} . "D" . $revision_id; - # Add transaction_id as anchor if one present - $comment .= "#" . $params->{transaction_id} if $params->{transaction_id}; - $bug->add_comment($comment, { - isprivate => $attachment->isprivate, - type => CMT_ATTACHMENT_UPDATED, - extra_data => $attachment->id - }); - } - - $attachment->set_flags([ @denied_flags, @removed_flags ], \@new_flags); - $attachment->update($timestamp); - $bug->update($timestamp) if $comment; - - push(@updated_attach_ids, $attachment->id); - } - - Bugzilla::BugMail::Send($bug_id, { changer => $user }) if @updated_attach_ids; - - return { result => \@updated_attach_ids }; -} - -sub obsolete_attachments { - my ($self, $params) = @_; - - my $user = Bugzilla->login(LOGIN_REQUIRED); - - # Prechecks - _phabricator_precheck($user); - - my $revision_id = $params->{revision_id}; - unless (defined $revision_id && detaint_natural($revision_id)) { - ThrowCodeError('param_required', { param => 'revision' }) - } - - my $bug_id= $params->{bug_id}; - unless (defined $bug_id && detaint_natural($bug_id)) { - ThrowCodeError('param_required', { param => 'bug_id' }) - } - - my $make_obsolete = $params->{make_obsolete}; - unless (defined $make_obsolete) { - ThrowCodeError('param_required', { param => 'make_obsolete' }) - } - $make_obsolete = $make_obsolete ? 1 : 0; - - my $bug = Bugzilla::Bug->check($bug_id); - - my @attachments = - grep { is_attachment_phab_revision($_) } @{ $bug->attachments() }; - - return { result => [] } if !@attachments; - - my $dbh = Bugzilla->dbh; - my ($timestamp) = $dbh->selectrow_array("SELECT NOW()"); - - my @updated_attach_ids; - foreach my $attachment (@attachments) { - my ($curr_revision_id) = ($attachment->filename =~ PHAB_ATTACHMENT_PATTERN); - next if $revision_id != $curr_revision_id; - - $attachment->set_is_obsolete($make_obsolete); - $attachment->update($timestamp); - - push(@updated_attach_ids, $attachment->id); - } - - Bugzilla::BugMail::Send($bug_id, { changer => $user }) if @updated_attach_ids; - - return { result => \@updated_attach_ids }; -} - sub needs_review { my ($self, $params) = @_; ThrowUserError('phabricator_not_enabled') @@ -387,18 +154,6 @@ sub needs_review { return { result => \@result }; } -sub _phabricator_precheck { - my ($user) = @_; - - # Ensure PhabBugz is on - ThrowUserError('phabricator_not_enabled') - unless Bugzilla->params->{phabricator_enabled}; - - # Validate that the requesting user's email matches phab-bot - ThrowUserError('phabricator_unauthorized_user') - unless $user->login eq PHAB_AUTOMATION_USER; -} - sub set_build_target { my ( $self, $params ) = @_; @@ -457,15 +212,6 @@ sub rest_resources { } } }, - # Revision creation - qr{^/phabbugz/revision/([^/]+)$}, { - POST => { - method => 'revision', - params => sub { - return { revision => $_[0] }; - } - } - }, # Bug permission checks qr{^/phabbugz/check_bug/(\d+)/(\d+)$}, { GET => { @@ -475,24 +221,12 @@ sub rest_resources { } } }, - # Update reviewer statuses - qr{^/phabbugz/update_reviewer_statuses$}, { - PUT => { - method => 'update_reviewer_statuses', - } - }, - # Obsolete attachments - qr{^/phabbugz/obsolete$}, { - PUT => { - method => 'obsolete_attachments', - } - }, # Review requests qw{^/phabbugz/needs_review$}, { GET => { method => 'needs_review', }, - }, + } ]; } diff --git a/extensions/Push/lib/Connector/Phabricator.pm b/extensions/Push/lib/Connector/Phabricator.pm index cea73f4104..aeef32ab4a 100644 --- a/extensions/Push/lib/Connector/Phabricator.pm +++ b/extensions/Push/lib/Connector/Phabricator.pm @@ -15,23 +15,18 @@ use base 'Bugzilla::Extension::Push::Connector::Base'; use Bugzilla::Bug; use Bugzilla::Constants; -use Bugzilla::Error; -use Bugzilla::User; use Bugzilla::Extension::PhabBugz::Constants; +use Bugzilla::Extension::PhabBugz::Policy; +use Bugzilla::Extension::PhabBugz::Project; +use Bugzilla::Extension::PhabBugz::Revision; use Bugzilla::Extension::PhabBugz::Util qw( - add_comment_to_revision add_security_sync_comments - create_private_revision_policy - edit_revision_policy get_attachment_revisions get_bug_role_phids - get_project_phid get_security_sync_groups - make_revision_public - make_revision_private - set_revision_subscribers ); + use Bugzilla::Extension::Push::Constants; use Bugzilla::Extension::Push::Util qw(is_public); @@ -75,72 +70,64 @@ sub send { my @set_groups = get_security_sync_groups($bug); - my @revisions = get_attachment_revisions($bug); - - if (!$is_public && !@set_groups) { - foreach my $revision (@revisions) { - Bugzilla->audit(sprintf( - 'Making revision %s for bug %s private due to unkown Bugzilla groups: %s', - $revision->{id}, - $bug->id, - join(', ', @set_groups) - )); - make_revision_private( $revision->{phid} ); - } - - add_security_sync_comments(\@revisions, $bug); - - return PUSH_RESULT_OK; - } + my $revisions = get_attachment_revisions($bug); my $group_change = ($message->routing_key =~ /^(?:attachment|bug)\.modify:.*\bbug_group\b/) ? 1 : 0; - my $subscribers; - if ( !$is_public ) { - $subscribers = get_bug_role_phids($bug); - } - - my $secure_project_phid = get_project_phid('secure-revision'); - - foreach my $revision (@revisions) { - my $revision_phid = $revision->{phid}; - - my $rev_obj = Bugzilla::Extension::PhabBugz::Revision->new_from_query({ phids => [ $revision_phid ] }); - my $revision_updated; + foreach my $revision (@$revisions) { + my $secure_revision = Bugzilla::Extension::PhabBugz::Project->new_from_query({ + name => 'secure-revision' + }); if ( $is_public && $group_change ) { Bugzilla->audit(sprintf( 'Making revision %s public for bug %s', - $revision->{id}, + $revision->id, $bug->id )); - make_revision_public($revision_phid); - $rev_obj->remove_project($secure_project_phid); - $revision_updated = 1; + $revision->set_policy('view', 'public'); + $revision->set_policy('edit', 'users'); + $revision->remove_project($secure_revision->phid); } - elsif ( !$is_public && $group_change ) { + elsif ( !$is_public && !@set_groups ) { Bugzilla->audit(sprintf( - 'Giving revision %s a custom policy for bug %s', - $revision->{id}, - $bug->id + 'Making revision %s for bug %s private due to unkown Bugzilla groups: %s', + $revision->id, + $bug->id, + join(', ', @set_groups) )); - my $policy_phid = create_private_revision_policy( \@set_groups ); - edit_revision_policy( $revision_phid, $policy_phid, $subscribers ); - $rev_obj->add_project($secure_project_phid); - $revision_updated = 1; + $revision->set_policy('view', $secure_revision->phid); + $revision->set_policy('edit', $secure_revision->phid); + $revision->add_project($secure_revision->phid); + add_security_sync_comments([$revision], $bug); } - elsif ( !$is_public && !$group_change ) { + elsif ( !$is_public && $group_change ) { Bugzilla->audit(sprintf( - 'Updating subscribers for %s for bug %s', - $revision->{id}, + 'Giving revision %s a custom policy for bug %s', + $revision->id, $bug->id )); - set_revision_subscribers( $revision_phid, $subscribers ); + my @set_projects = map { "bmo-" . $_ } @set_groups; + my $new_policy = Bugzilla::Extension::PhabBugz::Policy->create(\@set_projects); + $revision->set_policy('view', $new_policy->phid); + $revision->set_policy('edit', $new_policy->phid); + $revision->add_project($secure_revision->phid); } - $rev_obj->update() if $revision_updated; + + # Subscriber list of the private revision should always match + # the bug roles such as assignee, qa contact, and cc members. + Bugzilla->audit(sprintf( + 'Updating subscribers for %s for bug %s', + $revision->id, + $bug->id + )); + my $subscribers = get_bug_role_phids($bug); + $revision->set_subscribers($subscribers) if $subscribers; + + $revision->update(); } return PUSH_RESULT_OK; From 4c427904c8b0f654954f0f76c7c2c8401a48d088 Mon Sep 17 00:00:00 2001 From: Kohei Yoshino Date: Fri, 1 Jun 2018 13:45:23 -0400 Subject: [PATCH 069/119] Bug 1466159 - crash graph is wrong --- static/metricsgraphics/socorro-lens.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/metricsgraphics/socorro-lens.html b/static/metricsgraphics/socorro-lens.html index 9af0613237..c3664db373 100644 --- a/static/metricsgraphics/socorro-lens.html +++ b/static/metricsgraphics/socorro-lens.html @@ -96,7 +96,7 @@ function getSignaturesFromURL(search, match) { var index = search.indexOf("?s="); - search = search.substring(index + 3); + search = search.substring(index + 3).replace(/\+/g, '%20'); var signatures = []; if (search.indexOf("\\") !== -1) { signatures = search.split("\\"); From cb7ced68adc539b928198c92df21e30e51f41518 Mon Sep 17 00:00:00 2001 From: Kohei Yoshino Date: Mon, 4 Jun 2018 16:54:24 -0400 Subject: [PATCH 070/119] no bug - Fix SyntaxError in global.js --- js/global.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/global.js b/js/global.js index 68aceb03fa..d0396d6a85 100644 --- a/js/global.js +++ b/js/global.js @@ -219,7 +219,7 @@ const detect_blocked_gravatars = () => { */ const adjust_scroll_onload = () => { if (location.hash) { - const $target = document.querySelector(location.hash); + const $target = document.querySelector(CSS.escape(location.hash)); if ($target) { window.setTimeout(() => scroll_element_into_view($target), 50); From 1eac46993cd9ddb2e6ec6f5a443c002c5544a790 Mon Sep 17 00:00:00 2001 From: dklawren Date: Tue, 5 Jun 2018 10:33:51 -0400 Subject: [PATCH 071/119] Bug 1466122 - Change "Reviews Requested of You" to show results are from Phabricator and not from BMO --- .../template/en/default/pages/mydashboard.html.tmpl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/extensions/MyDashboard/template/en/default/pages/mydashboard.html.tmpl b/extensions/MyDashboard/template/en/default/pages/mydashboard.html.tmpl index a81a5903b7..5c372db3c3 100644 --- a/extensions/MyDashboard/template/en/default/pages/mydashboard.html.tmpl +++ b/extensions/MyDashboard/template/en/default/pages/mydashboard.html.tmpl @@ -129,7 +129,7 @@ [% BLOCK requests_table %]
    -
    [% title FILTER html %]
    +
    [% title FILTER html_light %]
    Loading... 0 reviews found @@ -143,7 +143,8 @@ [% ## no-008filter # requires PhabBugz extension IF Param('phabricator_enabled'); - PROCESS requests_table name='reviews' title='Reviews Requested of You'; + title = 'Phabricator Reviews Requested of You'; + PROCESS requests_table name='reviews' title=title; END; PROCESS requests_table name='requestee' title='Flags Requested of You'; From 7dc3c771ef4f0fc3dc7ff5671b0b433f2d71d11c Mon Sep 17 00:00:00 2001 From: Kohei Yoshino Date: Tue, 5 Jun 2018 11:59:55 -0400 Subject: [PATCH 072/119] Bug 1465889 - form.dev-engagement-event field should be red instead of black --- .../en/default/bug/create/create-dev-engagement-event.html.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/BMO/template/en/default/bug/create/create-dev-engagement-event.html.tmpl b/extensions/BMO/template/en/default/bug/create/create-dev-engagement-event.html.tmpl index 1197952fe8..5478de00fb 100644 --- a/extensions/BMO/template/en/default/bug/create/create-dev-engagement-event.html.tmpl +++ b/extensions/BMO/template/en/default/bug/create/create-dev-engagement-event.html.tmpl @@ -142,7 +142,7 @@ -
    +
    The Developer Events Team only participates in developer events. Form submission has been disabled.
    From 35038ef2b5f6f1b783931797f34c5ce68dc3b8a9 Mon Sep 17 00:00:00 2001 From: dklawren Date: Wed, 6 Jun 2018 07:52:35 -0400 Subject: [PATCH 073/119] Bump version to 20180605.1 --- Bugzilla.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Bugzilla.pm b/Bugzilla.pm index cb575433ea..7ab7031e71 100644 --- a/Bugzilla.pm +++ b/Bugzilla.pm @@ -22,7 +22,7 @@ BEGIN { } } -our $VERSION = '20180530.1'; +our $VERSION = '20180605.1'; use Bugzilla::Auth; use Bugzilla::Auth::Persist::Cookie; From 4413e39bdec98460efc9851f243d4555b9d24e26 Mon Sep 17 00:00:00 2001 From: dklawren Date: Thu, 7 Jun 2018 09:31:47 -0400 Subject: [PATCH 074/119] Bug 1467297 - variable masks earlier declaration in Feed.pm in Phabbugz extension --- extensions/PhabBugz/lib/Feed.pm | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/extensions/PhabBugz/lib/Feed.pm b/extensions/PhabBugz/lib/Feed.pm index ff381ad262..b58c1ea4b0 100644 --- a/extensions/PhabBugz/lib/Feed.pm +++ b/extensions/PhabBugz/lib/Feed.pm @@ -188,7 +188,7 @@ sub feed_query { my $revision = Bugzilla::Extension::PhabBugz::Revision->new_from_query( - { + { ids => [ int($revision_id) ] } ); @@ -337,7 +337,7 @@ sub process_revision_change { blessed $revision_phid ? $revision_phid : Bugzilla::Extension::PhabBugz::Revision->new_from_query({ phids => [ $revision_phid ] }); - + my $secure_revision = Bugzilla::Extension::PhabBugz::Project->new_from_query( { @@ -378,10 +378,6 @@ sub process_revision_change { # REVISION SECURITY POLICY - my $secure_revision = Bugzilla::Extension::PhabBugz::Project->new_from_query({ - name => 'secure-revision' - }); - # If bug is public then remove privacy policy if (!@{ $bug->groups_in }) { INFO('Bug is public so setting view/edit public'); @@ -808,7 +804,7 @@ sub get_group_members { } return if !@userids; - + # Look up the phab ids for these users my $phab_users = Bugzilla::Extension::PhabBugz::User->match( { From 9af546bb69ad8d0d61d7575b284c28825a0056fe Mon Sep 17 00:00:00 2001 From: dklawren Date: Fri, 8 Jun 2018 11:22:56 -0400 Subject: [PATCH 075/119] Bug 1467271 - When making a revision public, make the revision editable only by the bmo-editbugs-team project (editbugs) --- extensions/PhabBugz/lib/Feed.pm | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/extensions/PhabBugz/lib/Feed.pm b/extensions/PhabBugz/lib/Feed.pm index b58c1ea4b0..981c95fb3a 100644 --- a/extensions/PhabBugz/lib/Feed.pm +++ b/extensions/PhabBugz/lib/Feed.pm @@ -337,13 +337,20 @@ sub process_revision_change { blessed $revision_phid ? $revision_phid : Bugzilla::Extension::PhabBugz::Revision->new_from_query({ phids => [ $revision_phid ] }); - + + # Project tags/groups that will be used later for policies, etc. my $secure_revision = Bugzilla::Extension::PhabBugz::Project->new_from_query( { name => 'secure-revision' } ); + my $edit_bugs = + Bugzilla::Extension::PhabBugz::Project->new_from_query( + { + name => 'bmo-editbugs-team' + } + ); # NO BUG ID @@ -352,7 +359,7 @@ sub process_revision_change { # If new revision and bug id was omitted, make revision public INFO("No bug associated with new revision. Marking public."); $revision->set_policy('view', 'public'); - $revision->set_policy('edit', 'users'); + $revision->set_policy('edit', ($edit_bugs ? $edit_bugs->phid : 'users')); $revision->remove_project($secure_revision->phid); $revision->update(); INFO("SUCCESS"); @@ -382,7 +389,7 @@ sub process_revision_change { if (!@{ $bug->groups_in }) { INFO('Bug is public so setting view/edit public'); $revision->set_policy('view', 'public'); - $revision->set_policy('edit', 'users'); + $revision->set_policy('edit', ($edit_bugs ? $edit_bugs->phid : 'users')); $revision->remove_project($secure_revision->phid); } # else bug is private. From 69e02e0576fc834de982ab6fa3d5b77ce7fe472a Mon Sep 17 00:00:00 2001 From: Dylan William Hardison Date: Thu, 14 Jun 2018 15:54:24 -0700 Subject: [PATCH 076/119] Bug 1468818 - Re-introduce is_markdown to the Bugzilla::Comment model --- Bugzilla/Comment.pm | 8 ++++++++ Bugzilla/DB/Schema.pm | 3 ++- Bugzilla/Install/DB.pm | 5 +++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/Bugzilla/Comment.pm b/Bugzilla/Comment.pm index f9a6f7d3a9..00c7115e43 100644 --- a/Bugzilla/Comment.pm +++ b/Bugzilla/Comment.pm @@ -45,6 +45,7 @@ use constant DB_COLUMNS => qw( already_wrapped type extra_data + is_markdown ); use constant UPDATE_COLUMNS => qw( @@ -67,6 +68,7 @@ use constant VALIDATORS => { work_time => \&_check_work_time, thetext => \&_check_thetext, isprivate => \&_check_isprivate, + is_markdown => \&Bugzilla::Object::check_boolean, extra_data => \&_check_extra_data, type => \&_check_type, }; @@ -233,6 +235,7 @@ sub body { return $_[0]->{'thetext'}; } sub bug_id { return $_[0]->{'bug_id'}; } sub creation_ts { return $_[0]->{'bug_when'}; } sub is_private { return $_[0]->{'isprivate'}; } +sub is_markdown { return $_[0]->{'is_markdown'}; } sub work_time { # Work time is returned as a string (see bug 607909) return 0 if $_[0]->{'work_time'} + 0 == 0; @@ -336,6 +339,7 @@ sub body_full { sub set_is_private { $_[0]->set('isprivate', $_[1]); } sub set_type { $_[0]->set('type', $_[1]); } sub set_extra_data { $_[0]->set('extra_data', $_[1]); } +sub set_is_markdown { $_[0]->set('is_markdown', $_[1]); } sub add_tag { my ($self, $tag) = @_; @@ -576,6 +580,10 @@ C Time spent as related to this comment. C Comment is marked as private. +=item C + +C Whether this comment needs Markdown rendering to be applied. + =item C If this comment is stored in the database word-wrapped, this will be C<1>. diff --git a/Bugzilla/DB/Schema.pm b/Bugzilla/DB/Schema.pm index 3307464dbc..19275206d7 100644 --- a/Bugzilla/DB/Schema.pm +++ b/Bugzilla/DB/Schema.pm @@ -398,7 +398,8 @@ use constant ABSTRACT_SCHEMA => { DEFAULT => 'FALSE'}, type => {TYPE => 'INT2', NOTNULL => 1, DEFAULT => '0'}, - extra_data => {TYPE => 'varchar(255)'} + extra_data => {TYPE => 'varchar(255)'}, + is_markdown => {TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 'FALSE'} ], INDEXES => [ longdescs_bug_id_idx => ['bug_id'], diff --git a/Bugzilla/Install/DB.pm b/Bugzilla/Install/DB.pm index e6a7a3be0d..1729a134bf 100644 --- a/Bugzilla/Install/DB.pm +++ b/Bugzilla/Install/DB.pm @@ -710,6 +710,11 @@ sub update_table_definitions { # 2014-07-27 LpSolit@gmail.com - Bug 1044561 _fix_user_api_keys_indexes(); + + # 2018-06-14 dylan@mozilla.com - Bug 1468818 + $dbh->bz_add_column('longdescs', 'is_markdown', + {TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 'FALSE'}); + # 2014-10-?? dkl@mozilla.com - Bug 1062940 $dbh->bz_alter_column('bugs', 'alias', { TYPE => 'varchar(40)' }); From 404dc5496967203c5f99755340f43d712420446a Mon Sep 17 00:00:00 2001 From: Dylan William Hardison Date: Thu, 14 Jun 2018 17:50:08 -0700 Subject: [PATCH 077/119] Bug 1468848 - Change presentation of show_bug.cgi urls to be /bug/ID or /bug/ALIAS. --- .htaccess | 3 ++- Bugzilla/Install/Localconfig.pm | 13 ++++++++++--- README.rst | 5 ++++- .../global/header-additional_header.html.tmpl | 2 +- .../en/default/bug_modal/header.html.tmpl | 1 + extensions/BugModal/web/bug_modal.js | 18 +++++++++++------- .../default/hook/global/header-start.html.tmpl | 2 +- template/en/default/global/header.html.tmpl | 1 + template/en/default/setup/strings.txt.pl | 4 ++++ 9 files changed, 35 insertions(+), 14 deletions(-) diff --git a/.htaccess b/.htaccess index ee7d296b04..ac0e40d888 100644 --- a/.htaccess +++ b/.htaccess @@ -42,7 +42,6 @@ RewriteRule ^template_cache.deleteme/ - [F,L,NC] RewriteRule ^review$ page.cgi?id=splinter.html$1 [QSA] RewriteRule ^user_?profile$ page.cgi?id=user_profile.html$1 [QSA] RewriteRule ^request_defer$ page.cgi?id=request_defer.html$1 [QSA] -RewriteRule ^([0-9]+)$ show_bug.cgi?id=$1 [QSA] RewriteRule ^favicon\.ico$ extensions/BMO/web/images/favicon.ico RewriteRule ^form[\.:]itrequest$ enter_bug.cgi?product=Infrastructure+\%26+Operations&format=itrequest [QSA] RewriteRule ^form[\.:](mozlist|poweredby|presentation|trademark|recoverykey)$ enter_bug.cgi?product=mozilla.org&format=$1 [QSA] @@ -84,6 +83,8 @@ RewriteRule ^form[\.:]shield[\.:]studies$ enter_bug.cgi?product=Shield&format=sh RewriteRule ^form[\.:]client[\.:]bounty$ enter_bug.cgi?product=Firefox&format=client-bounty [QSA] RewriteRule ^rest - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] RewriteRule ^rest/(.*)$ rest.cgi/$1 [NE] +RewriteRule ^bug/([^/]+)$ show_bug.cgi?id=$1 [NE] +RewriteRule ^([0-9]+)$ show_bug.cgi?id=$1 [QSA] RewriteRule ^(?:latest|1\.2|1\.3)/(.*)$ extensions/BzAPI/bin/rest.cgi/$1 [NE] RewriteRule ^bzapi/(.*)$ extensions/BzAPI/bin/rest.cgi/$1 [NE] RewriteRule ^login$ index.cgi?GoAheadAndLogIn=1 [NE] diff --git a/Bugzilla/Install/Localconfig.pm b/Bugzilla/Install/Localconfig.pm index e1a8e09094..f6333f218b 100644 --- a/Bugzilla/Install/Localconfig.pm +++ b/Bugzilla/Install/Localconfig.pm @@ -163,6 +163,10 @@ use constant LOCALCONFIG_VARS => ( name => 'urlbase', default => _migrate_param( "urlbase", "" ), }, + { + name => 'canonical_urlbase', + default => '', + }, { name => 'attachment_base', default => _migrate_param( "attachment_base", '' ), @@ -286,13 +290,16 @@ sub _read_localconfig_from_file { sub read_localconfig { my ($include_deprecated) = @_; - + my $lc; if ($ENV{LOCALCONFIG_ENV}) { - return _read_localconfig_from_env(); + $lc = _read_localconfig_from_env(); } else { - return _read_localconfig_from_file($include_deprecated); + $lc = _read_localconfig_from_file($include_deprecated); } + $lc->{canonical_urlbase} //= $lc->{urlbase}; + + return $lc; } # diff --git a/README.rst b/README.rst index d8986fc734..bbc91bff29 100644 --- a/README.rst +++ b/README.rst @@ -246,9 +246,12 @@ BUGZILLA_UNSAFE_AUTH_DELEGATION BMO_urlbase The public url for this instance. Note that if this begins with https:// - abd BMO_inbound_proxies is set to '*' Bugzilla will believe the connection to it + and BMO_inbound_proxies is set to '*' Bugzilla will believe the connection to it is using SSL. +BMO_canonical_urlbase + The public url for the production instance, if different from urlbase above. + BMO_attachment_base This is the url for attachments. When the allow_attachment_display parameter is on, it is possible for a diff --git a/extensions/BMO/template/en/default/hook/global/header-additional_header.html.tmpl b/extensions/BMO/template/en/default/hook/global/header-additional_header.html.tmpl index f1896dccc7..d7a4cc2a5f 100644 --- a/extensions/BMO/template/en/default/hook/global/header-additional_header.html.tmpl +++ b/extensions/BMO/template/en/default/hook/global/header-additional_header.html.tmpl @@ -21,7 +21,7 @@ [% IF bug %] - + [% END %] [%# *** Bug List Navigation *** %] diff --git a/extensions/BugModal/template/en/default/bug_modal/header.html.tmpl b/extensions/BugModal/template/en/default/bug_modal/header.html.tmpl index 38b6b572fe..a21e9c2685 100644 --- a/extensions/BugModal/template/en/default/bug_modal/header.html.tmpl +++ b/extensions/BugModal/template/en/default/bug_modal/header.html.tmpl @@ -94,6 +94,7 @@ [%# expose useful data to js %] BUGZILLA.bug_id = [% bug.id FILTER none %]; BUGZILLA.bug_title = '[% unfiltered_title FILTER js %]'; + BUGZILLA.bug_alias = '[% bug.alias FILTER js %]'; BUGZILLA.user = { id: [% user.id FILTER none %], login: '[% user.login FILTER js %]', diff --git a/extensions/BugModal/web/bug_modal.js b/extensions/BugModal/web/bug_modal.js index bf5c300e14..9d2701e51a 100644 --- a/extensions/BugModal/web/bug_modal.js +++ b/extensions/BugModal/web/bug_modal.js @@ -1344,15 +1344,19 @@ function confirmUnsafeURL(url) { // fix url after bug creation/update if (history && history.replaceState) { - var href = document.location.href; - if (!href.match(/show_bug\.cgi/)) { - history.replaceState(null, BUGZILLA.bug_title, 'show_bug.cgi?id=' + BUGZILLA.bug_id); + let bug_id = BUGZILLA.bug_id; + let bug_alias = BUGZILLA.bug_alias; + let bug_slug = bug_alias || bug_id; + let url = new URL(document.location.href); + if (!url.pathname.match(/^bug\/[0-9]+/)) { + url.searchParams.delete("id"); + let new_url = url.search ? `/bug/${bug_slug}${url.search}` : `/bug/${bug_slug}`; + if (url.hash) { + new_url += url.hash; + } + history.replaceState(null, BUGZILLA.bug_title, new_url); document.title = BUGZILLA.bug_title; } - if (href.match(/show_bug\.cgi\?.*list_id=/)) { - href = href.replace(/[\?&]+list_id=(\d+|cookie)/, ''); - history.replaceState(null, BUGZILLA.bug_title, href); - } } // ajax wrapper, to simplify error handling and auth diff --git a/extensions/OpenGraph/template/en/default/hook/global/header-start.html.tmpl b/extensions/OpenGraph/template/en/default/hook/global/header-start.html.tmpl index 51c388d427..247e44ea91 100644 --- a/extensions/OpenGraph/template/en/default/hook/global/header-start.html.tmpl +++ b/extensions/OpenGraph/template/en/default/hook/global/header-start.html.tmpl @@ -9,7 +9,7 @@ [% USE Bugzilla %] - + [% IF bug %] diff --git a/template/en/default/global/header.html.tmpl b/template/en/default/global/header.html.tmpl index 6b56a5d30b..771fa801bd 100644 --- a/template/en/default/global/header.html.tmpl +++ b/template/en/default/global/header.html.tmpl @@ -136,6 +136,7 @@ + [% title %] diff --git a/template/en/default/setup/strings.txt.pl b/template/en/default/setup/strings.txt.pl index 8726a8b138..fe375400d7 100644 --- a/template/en/default/setup/strings.txt.pl +++ b/template/en/default/setup/strings.txt.pl @@ -230,6 +230,10 @@ END END localconfig_urlbase => <<'END', The URL that is the common initial leading part of all URLs. +END + localconfig_canonical_urlbase => <<'END', +The URL that is the canonical initial leading part of all URLs. +This will be the production url for a dev site, for instance. END localconfig_use_suexec => <<'END', Set this to 1 if Bugzilla runs in an Apache SuexecUserGroup environment. From 170ec08234e29050c5d78d52e4100207625897d2 Mon Sep 17 00:00:00 2001 From: Israel Madueme Date: Fri, 15 Jun 2018 14:42:19 -0700 Subject: [PATCH 078/119] Bug 1456877 - Add a wrapper around libcmark_gfm to Bugzilla --- Bugzilla.pm | 12 ++ Bugzilla/Markdown/GFM.pm | 92 ++++++++++++++++ Bugzilla/Markdown/GFM/Node.pm | 33 ++++++ Bugzilla/Markdown/GFM/Parser.pm | 109 +++++++++++++++++++ Bugzilla/Markdown/GFM/SyntaxExtension.pm | 31 ++++++ Bugzilla/Markdown/GFM/SyntaxExtensionList.pm | 23 ++++ t/markdown.t | 73 +++++++++++++ 7 files changed, 373 insertions(+) create mode 100644 Bugzilla/Markdown/GFM.pm create mode 100644 Bugzilla/Markdown/GFM/Node.pm create mode 100644 Bugzilla/Markdown/GFM/Parser.pm create mode 100644 Bugzilla/Markdown/GFM/SyntaxExtension.pm create mode 100644 Bugzilla/Markdown/GFM/SyntaxExtensionList.pm create mode 100644 t/markdown.t diff --git a/Bugzilla.pm b/Bugzilla.pm index 7ab7031e71..9df38138dd 100644 --- a/Bugzilla.pm +++ b/Bugzilla.pm @@ -38,6 +38,8 @@ use Bugzilla::Flag; use Bugzilla::Hook; use Bugzilla::Install::Localconfig qw(read_localconfig); use Bugzilla::Install::Util qw(init_console include_languages); +use Bugzilla::Markdown::GFM; +use Bugzilla::Markdown::GFM::Parser; use Bugzilla::Memcached; use Bugzilla::Template; use Bugzilla::Token; @@ -865,6 +867,11 @@ sub check_rate_limit { } } +sub markdown_parser { + return request_cache->{markdown_parser} + ||= Bugzilla::Markdown::GFM::Parser->new( {extensions => [qw( autolink tagfilter table strikethrough)] } ); +} + # Private methods # Per-process cleanup. Note that this is a plain subroutine, not a method, @@ -1190,6 +1197,11 @@ of features, see C in C. Feeds the provided message into our centralised auditing system. +=item C + +Returns a L with the default extensions +loaded (autolink, tagfilter, table, and strikethrough). + =back =head1 B diff --git a/Bugzilla/Markdown/GFM.pm b/Bugzilla/Markdown/GFM.pm new file mode 100644 index 0000000000..f3f24fc6ac --- /dev/null +++ b/Bugzilla/Markdown/GFM.pm @@ -0,0 +1,92 @@ +package Bugzilla::Markdown::GFM; + +use 5.10.1; +use strict; +use warnings; + +use Alien::libcmark_gfm; +use FFI::Platypus; +use FFI::Platypus::Buffer qw( scalar_to_buffer buffer_to_scalar ); +use Exporter qw(import); + +use Bugzilla::Markdown::GFM::SyntaxExtension; +use Bugzilla::Markdown::GFM::SyntaxExtensionList; +use Bugzilla::Markdown::GFM::Parser; +use Bugzilla::Markdown::GFM::Node; + +our @EXPORT_OK = qw(cmark_markdown_to_html); + +my %OPTIONS = ( + default => 0, + sourcepos => ( 1 << 1 ), + hardbreaks => ( 1 << 2 ), + safe => ( 1 << 3 ), + nobreaks => ( 1 << 4 ), + normalize => ( 1 << 8 ), + validate_utf8 => ( 1 << 9 ), + smart => ( 1 << 10 ), + github_pre_lang => ( 1 << 11 ), + liberal_html_tag => ( 1 << 12 ), + footnotes => ( 1 << 13 ), + strikethrough_double_tilde => ( 1 << 14 ), + table_prefer_style_attributes => ( 1 << 15 ), +); + +my $FFI = FFI::Platypus->new( + lib => [grep { not -l $_ } Alien::libcmark_gfm->dynamic_libs], +); + +$FFI->custom_type( + markdown_options_t => { + native_type => 'int', + native_to_perl => sub { + my ($options) = @_; + my $result = {}; + foreach my $key (keys %OPTIONS) { + $result->{$key} = ($options & $OPTIONS{$key}) != 0; + } + return $result; + }, + perl_to_native => sub { + my ($options) = @_; + my $result = 0; + foreach my $key (keys %OPTIONS) { + if ($options->{$key}) { + $result |= $OPTIONS{$key}; + } + } + return $result; + } + } +); + +$FFI->attach(cmark_markdown_to_html => ['opaque', 'int', 'markdown_options_t'] => 'string', + sub { + my $c_func = shift; + my($markdown, $markdown_length) = scalar_to_buffer $_[0]; + return $c_func->($markdown, $markdown_length, $_[1]); + } +); + +# This has to happen after something from the main lib is loaded +$FFI->attach('core_extensions_ensure_registered' => [] => 'void'); + +core_extensions_ensure_registered(); + +Bugzilla::Markdown::GFM::SyntaxExtension->SETUP($FFI); +Bugzilla::Markdown::GFM::SyntaxExtensionList->SETUP($FFI); +Bugzilla::Markdown::GFM::Node->SETUP($FFI); +Bugzilla::Markdown::GFM::Parser->SETUP($FFI); + +1; + +__END__ + +=head1 NAME + +Bugzilla::Markdown::GFM - Sets up the FFI to libcmark_gfm. + +=head1 DESCRIPTION + +This modules mainly just does setup work. See L +to actually render markdown to html. diff --git a/Bugzilla/Markdown/GFM/Node.pm b/Bugzilla/Markdown/GFM/Node.pm new file mode 100644 index 0000000000..da5af1a68e --- /dev/null +++ b/Bugzilla/Markdown/GFM/Node.pm @@ -0,0 +1,33 @@ +package Bugzilla::Markdown::GFM::Node; + +use 5.10.1; +use strict; +use warnings; + +sub SETUP { + my ($class, $FFI) = @_; + + $FFI->custom_type( + markdown_node_t => { + native_type => 'opaque', + native_to_perl => sub { + bless \$_[0], $class if $_[0]; + }, + perl_to_native => sub { ${ $_[0] } }, + } + ); + + $FFI->attach( + [ cmark_node_free => 'DESTROY' ], + [ 'markdown_node_t' ] => 'void' + ); + + $FFI->attach( + [ cmark_render_html => 'render_html' ], + [ 'markdown_node_t', 'markdown_options_t', 'markdown_syntax_extension_list_t'] => 'string', + ); +} + +1; + +__END__ diff --git a/Bugzilla/Markdown/GFM/Parser.pm b/Bugzilla/Markdown/GFM/Parser.pm new file mode 100644 index 0000000000..5307b49c18 --- /dev/null +++ b/Bugzilla/Markdown/GFM/Parser.pm @@ -0,0 +1,109 @@ +package Bugzilla::Markdown::GFM::Parser; + +use 5.10.1; +use strict; +use warnings; + +use FFI::Platypus::Buffer qw( scalar_to_buffer buffer_to_scalar ); + +sub new { + my ($class, $options) = @_; + my $extensions = delete $options->{extensions} // []; + my $parser = $class->_new($options); + $parser->{_options} = $options; + + eval { + foreach my $name (@$extensions) { + my $extension = Bugzilla::Markdown::GFM::SyntaxExtension->find($name) + or die "unknown extension: $name"; + $parser->attach_syntax_extension($extension); + } + }; + + return $parser; +} + +sub render_html { + my ($self, $markdown) = @_; + $self->feed($markdown); + my $node = $self->finish; + return $node->render_html($self->{_options}, $self->get_syntax_extensions); +} + +sub SETUP { + my ($class, $FFI) = @_; + + $FFI->custom_type( + markdown_parser_t => { + native_type => 'opaque', + native_to_perl => sub { + bless { _pointer => $_[0] }, $class; + }, + perl_to_native => sub { $_[0]->{_pointer} }, + } + ); + + $FFI->attach( + [ cmark_parser_new => '_new' ], + [ 'markdown_options_t' ] => 'markdown_parser_t', + sub { + my $c_func = shift; + return $c_func->($_[1]); + } + ); + + $FFI->attach( + [ cmark_parser_free => 'DESTROY' ], + [ 'markdown_parser_t' ] => 'void' + ); + + $FFI->attach( + [ cmark_parser_feed => 'feed'], + ['markdown_parser_t', 'opaque', 'int'] => 'void', + sub { + my $c_func = shift; + $c_func->($_[0], scalar_to_buffer $_[1]); + } + ); + + $FFI->attach( + [ cmark_parser_finish => 'finish' ], + [ 'markdown_parser_t' ] => 'markdown_node_t', + ); + + $FFI->attach( + [ cmark_parser_attach_syntax_extension => 'attach_syntax_extension' ], + [ 'markdown_parser_t', 'markdown_syntax_extension_t' ] => 'void', + ); + + $FFI->attach( + [ cmark_parser_get_syntax_extensions => 'get_syntax_extensions' ], + [ 'markdown_parser_t' ] => 'markdown_syntax_extension_list_t', + ); +} + +1; + +__END__ + +=head1 NAME + +Bugzilla::Markdown::GFM::Parser - Transforms markdown into HTML via libcmark_gfm. + +=head1 SYNOPSIS + + use Bugzilla::Markdown::GFM; + use Bugzilla::Markdown::GFM::Parser; + + my $parser = Bugzilla::Markdown::GFM::Parser->new({ + extensions => [qw( autolink tagfilter table strikethrough )] + }); + + say $parser->render_html(<<'MARKDOWN'); + # My header + + This is **markdown**! + + - list item 1 + - list item 2 + MARKDOWN diff --git a/Bugzilla/Markdown/GFM/SyntaxExtension.pm b/Bugzilla/Markdown/GFM/SyntaxExtension.pm new file mode 100644 index 0000000000..56efa177a9 --- /dev/null +++ b/Bugzilla/Markdown/GFM/SyntaxExtension.pm @@ -0,0 +1,31 @@ +package Bugzilla::Markdown::GFM::SyntaxExtension; + +use 5.10.1; +use strict; +use warnings; + +sub SETUP { + my ($class, $FFI) = @_; + + $FFI->custom_type( + markdown_syntax_extension_t => { + native_type => 'opaque', + native_to_perl => sub { + bless \$_[0], $class if $_[0]; + }, + perl_to_native => sub { $_[0] ? ${ $_[0] } : 0 }, + } + ); + $FFI->attach( + [ cmark_find_syntax_extension => 'find' ], + [ 'string' ] => 'markdown_syntax_extension_t', + sub { + my $c_func = shift; + return $c_func->($_[1]); + } + ); +} + +1; + +__END__ diff --git a/Bugzilla/Markdown/GFM/SyntaxExtensionList.pm b/Bugzilla/Markdown/GFM/SyntaxExtensionList.pm new file mode 100644 index 0000000000..06a9798c2b --- /dev/null +++ b/Bugzilla/Markdown/GFM/SyntaxExtensionList.pm @@ -0,0 +1,23 @@ +package Bugzilla::Markdown::GFM::SyntaxExtensionList; + +use 5.10.1; +use strict; +use warnings; + +sub SETUP { + my ($class, $FFI) = @_; + + $FFI->custom_type( + markdown_syntax_extension_list_t => { + native_type => 'opaque', + native_to_perl => sub { + bless \$_[0], $class if $_[0]; + }, + perl_to_native => sub { $_[0] ? ${ $_[0] } : 0 }, + } + ); +} + +1; + +__END__ diff --git a/t/markdown.t b/t/markdown.t new file mode 100644 index 0000000000..0344706c9d --- /dev/null +++ b/t/markdown.t @@ -0,0 +1,73 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# This Source Code Form is "Incompatible With Secondary Licenses", as +# defined by the Mozilla Public License, v. 2.0. +use 5.10.1; +use strict; +use warnings; +use lib qw( . lib local/lib/perl5 ); +use Bugzilla; +use Test::More; + +my $parser = Bugzilla->markdown_parser; + +is( + $parser->render_html('# header'), + "

    header

    \n", + 'Simple header' +); + +is( + $parser->render_html('`code snippet`'), + "

    code snippet

    \n", + 'Simple code snippet' +); + +is( + $parser->render_html('http://bmo-web.vm'), + "

    http://bmo-web.vm

    \n", + 'Autolink extension' +); + +is( + $parser->render_html(''), + "<script>hijack()</script>\n", + 'Tagfilter extension' +); + +is( + $parser->render_html('~~strikethrough~~'), + "

    strikethrough

    \n", + 'Strikethrough extension' +); + +my $table_markdown = <<'MARKDOWN'; +| Col1 | Col2 | +| ---- |:----:| +| val1 | val2 | +MARKDOWN + +my $table_html = <<'HTML'; + + + + + + + + + + + +
    Col1Col2
    val1val2
    +HTML + +is( + $parser->render_html($table_markdown), + $table_html, + 'Table extension' +); + +done_testing; From 4b59e0dce4ca88bc79fc65eeb7ec15b136e153e7 Mon Sep 17 00:00:00 2001 From: Dylan William Hardison Date: Wed, 20 Jun 2018 13:27:29 -0400 Subject: [PATCH 079/119] Bug 1468818 - Re-introduce is_markdown to the longdescs table (schema-only) --- Bugzilla/Comment.pm | 8 -------- 1 file changed, 8 deletions(-) diff --git a/Bugzilla/Comment.pm b/Bugzilla/Comment.pm index 00c7115e43..f9a6f7d3a9 100644 --- a/Bugzilla/Comment.pm +++ b/Bugzilla/Comment.pm @@ -45,7 +45,6 @@ use constant DB_COLUMNS => qw( already_wrapped type extra_data - is_markdown ); use constant UPDATE_COLUMNS => qw( @@ -68,7 +67,6 @@ use constant VALIDATORS => { work_time => \&_check_work_time, thetext => \&_check_thetext, isprivate => \&_check_isprivate, - is_markdown => \&Bugzilla::Object::check_boolean, extra_data => \&_check_extra_data, type => \&_check_type, }; @@ -235,7 +233,6 @@ sub body { return $_[0]->{'thetext'}; } sub bug_id { return $_[0]->{'bug_id'}; } sub creation_ts { return $_[0]->{'bug_when'}; } sub is_private { return $_[0]->{'isprivate'}; } -sub is_markdown { return $_[0]->{'is_markdown'}; } sub work_time { # Work time is returned as a string (see bug 607909) return 0 if $_[0]->{'work_time'} + 0 == 0; @@ -339,7 +336,6 @@ sub body_full { sub set_is_private { $_[0]->set('isprivate', $_[1]); } sub set_type { $_[0]->set('type', $_[1]); } sub set_extra_data { $_[0]->set('extra_data', $_[1]); } -sub set_is_markdown { $_[0]->set('is_markdown', $_[1]); } sub add_tag { my ($self, $tag) = @_; @@ -580,10 +576,6 @@ C Time spent as related to this comment. C Comment is marked as private. -=item C - -C Whether this comment needs Markdown rendering to be applied. - =item C If this comment is stored in the database word-wrapped, this will be C<1>. From 469d5c5f8705289997e50a3f94c700c0dcbfcc36 Mon Sep 17 00:00:00 2001 From: Kohei Yoshino Date: Wed, 20 Jun 2018 13:27:54 -0400 Subject: [PATCH 080/119] Bug 1469689 - Remove Bugzilla Helper and custom bug entry form links from Browse page --- .../BMO/template/en/default/global/choose-product.html.tmpl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/extensions/BMO/template/en/default/global/choose-product.html.tmpl b/extensions/BMO/template/en/default/global/choose-product.html.tmpl index 4329b716ad..11caca605f 100644 --- a/extensions/BMO/template/en/default/global/choose-product.html.tmpl +++ b/extensions/BMO/template/en/default/global/choose-product.html.tmpl @@ -173,12 +173,14 @@
    +[% IF NOT is_describe %] +[% END %] [% PROCESS global/footer.html.tmpl %] From 210068ac876bcf97966f6a593e00c8ac6bcb646c Mon Sep 17 00:00:00 2001 From: Kohei Yoshino Date: Wed, 20 Jun 2018 13:28:28 -0400 Subject: [PATCH 081/119] Bug 1419971 - Add new Developer Tools and WebExtensions products to easy product selector on Browse and Enter Bug pages --- .../en/default/global/choose-product.html.tmpl | 16 ++++++++++++---- extensions/BMO/web/producticons/devtools.png | Bin 0 -> 6807 bytes .../BMO/web/producticons/webextensions.png | Bin 0 -> 3711 bytes 3 files changed, 12 insertions(+), 4 deletions(-) create mode 100644 extensions/BMO/web/producticons/devtools.png create mode 100644 extensions/BMO/web/producticons/webextensions.png diff --git a/extensions/BMO/template/en/default/global/choose-product.html.tmpl b/extensions/BMO/template/en/default/global/choose-product.html.tmpl index 11caca605f..679d812e16 100644 --- a/extensions/BMO/template/en/default/global/choose-product.html.tmpl +++ b/extensions/BMO/template/en/default/global/choose-product.html.tmpl @@ -90,10 +90,22 @@ name="Firefox for iOS" icon="firefox_ios.png" %] + [% INCLUDE easyproduct + name="DevTools" + icon="devtools.png" + %] + [% INCLUDE easyproduct + name="WebExtensions" + icon="webextensions.png" + %] [% INCLUDE easyproduct name="Toolkit" icon="component.png" %] + [% INCLUDE easyproduct + name="Mozilla Localizations" + icon="localization.png" + %] [% INCLUDE easyproduct name="Thunderbird" icon="thunderbird.png" @@ -102,10 +114,6 @@ name="SeaMonkey" icon="seamonkey.png" %] - [% INCLUDE easyproduct - name="Mozilla Localizations" - icon="localization.png" - %] [% INCLUDE easyproduct name="Data Platform and Tools" icon="sync.png" diff --git a/extensions/BMO/web/producticons/devtools.png b/extensions/BMO/web/producticons/devtools.png new file mode 100644 index 0000000000000000000000000000000000000000..9800f654bc2f14aa975fab541ad52ce8058d3ca1 GIT binary patch literal 6807 zcmV;I8ffK-P)C0008_P)t-sM{rCU zN1p_G-znPrQ@-;wyX}3+_(;0(Osnf2i|`YQ;SGb~nb-URb>0Yl+fb(H6Mon+wderY z{~783FK(=K!21xL=@O^=09xJzd*10 zHmCn6c;*GZ@E3^a1WKw(CR~~btLHG0>I~rd41?lV z@BSc6j0$ePFrDfIWXUGj{5N{e0ASw$W8(m0-vo2<18wU8X5|HO?*eJ)0Bhd@XyycP z?gDG;26^=bb@Bjc-T-0Z0B+tBjQ;_7-2`v!1-$MEZ3BX+?<$^Qf}pIFoXUA_4; zl+gfL?G99-J7$t5Ua3ieuTrDePLsoc@c#qW{{rX#7eK89+y4a7|07tsA5pgvG?fPM z{{!LwA5W_Tm+c=<&mmT@1{FyWGK&i&Sr9OB001iojsFulgeG0V6E>Y2NvH!}y8?C1 zCc^v_IeRBz*&Ij51ZTV*PL?22*AhCI8AO=@1T+B*M-?}44lRThL5~N-@B_^MDs0#s zOP~mn{}w!;1wv>5209rtk03Husuo8mr2C)AwZ@mD2-4QQnEwujuhTAQo|0~n|FnQVug8v0M z%pR8i1+D)it^Wf>r2%R61;76TR>1-eKn;ig6NUB*A(b1W{{eCR15mmelK&Bh`~zCa zJ-Pn}oc}bN>ochT1;YOYxc>)x_y%_K3KDt$J#R$-002C6QchCKdD%*@PSW@hFv zW0{%z&^zPW+cbIDm1^yGXZQ8{-E+>IX*vyF5jQmy3Dw-#0Pj#28tT77Q8G~j{jX9K zOkHVc11K1#8aIX)UHyv?-w}bcdP71(7!!(Uj($iaYQhBF$U6ttX?EZ30vbM*g(#MH z_Jo3&OelskCdOn^;EI?lM&Ja;Ii9^op+X60o@#=(C3S0>B`+;|>)9^N$ma0yxIW}! z6n!QhV*)os%S#uR>~?O7qN{ouMUN7+F1GzIpA*Ts8BNhYvA#bNig{J*nOL9Em@twi zoIsGA+a8U&pU5D(s^<|BT#9$p8{iFA{K9;nvlaI@7i@aHD+Khx;Ce`5Opq}gFCl4! zu)E!M_Y?&JRP{9fYi??}9oMzi=kMOW_umRgdloh~=N2Yhdao*zS?9^bOe6!Yp9ak5 zmfUXcF$@A6+XX)a!Q!?07B9BcG>xFb@8Dy^~`x?YkRxhd3MxuKj{uB>opVgV7zXUs+Y4YA27mSyyIb>Ky!@kO z8w9WjQy}RYxtiBb?v45KfvaP-@%;sDa-$-EI{SOQ1i~js&ofbuD^1ZDc6|M+o`;@g z+4Hro0O01&=Nn)b$;XT8luvy>#rso}6AS+2YIw!^@vo15l7$PsDi40-H-Qo4a4XQg zOpiq(^dP%iV-wD-{e!uqNnX5~zp2%#mD>DF(eKyHDOXplJLTz}@&mLm-0vZS3IYN2 z<$&K|cno0?^xw6w5&9ntuO0K&83uuu(b^IJ7sG) zG^`>?Qsu>gKVh(QLI5l>c;c_Ru?P<|%U-UWtCc%HeE%T`F#QNk{*^E(7%$lyWN;%9wr7`!{b+w2b^Yxqlb% z1%XdLEkAfLBQSp9JVQt94hs}G)Pex2W`j}nb=sE>)#t+d(!TaB|6WPpqlt+#dnZeG z@5Ta{j|Ixh5g!D)LbtpTC!_aRuz{VlG&P^Yb!;>Qke+&g^OKj2hw@!maSq>`i0&;d z++UI{a0d%;0bIOC(=?+u!F_DB5E>IfXga?;2Qv^L%{}RK3Lb4u2-N#5XaAfIFk2GEe#io~y;PtAV#S#L-40f3> zmPY+9FrX}j;E@V5CPlM=o8^n93C6UmN|;6%gXi?AIKH}KA!*w%ywj62zC<25E;lqx$ww<&G}&3QMI!O2}qwpEO2`O1db}E#-Xa`=9HRl zBS~tYU`6zJC~NI~78#^M;?@gzNFbK$LSFSoe$ny*{r%N!6a@0}6xHL;D`#qIYWg%D zlJuY$pc@)uUUf2#C@FL!NaLY$M6kAOCr$ug133i72aX&*I)Vg{LFLTMR^=rlKL`d> zSZnsh5GdUv6K5~rH(EXmsW(NG4IO3PRYJQPGAc?Yg2fQK+D64+WB)|hw&wcG8n z&Z|Ok%#wkD6h5AiZ}$q0b}UytcXK)&Rw{0bJ3 zyokgL(ZD`*3~!H(DRpga{#LscD_CtLd>7!Ut~NAv5D^CEAc29b4Cox5=T{89xp)LO zZ5AhxYa{DQo;p0DNLCM@ZEIs95t>4LtU#GG2DQ4$6KLr0IK9eHTn@mvJjROYfQMSr zgSDHsOC+#lA;DMCzQ+os?t8$a&FC7609V*bjT!<0PlE?uYhmw+`w2NP;U@-Ri1CoX z;&ZTlosfXkJ|Ilt?Wx{q$s!Vk@ghhY8c zccle{W5D+ysr+H^te$2#1In@>VAqGOPQ9in(*=F-#PJcce8C_g#)H5%@&EtU6cl6x zitt91eG_p#-~`5$Z9$jLrjIZS5iA@8igH@5*09r#+7M7v-ofKH^2`84XfYo6vl~SM zunK@cJ`&i21^A7GpF-g#c<{}jKRBqnb}gg8*euup1%S$F)3^fxwLKF2s_qeFz$plH z;Df=cMPDKifC$73fiMIQA3iHTbZC02|1k*sUk4jm&;|cELycgr*sWXuxNsYqOtkmI zy17Sr$N-lZ-#hfu%dci%#09*7Fa+FHboFxYtbAZ-AePKiBo$-IyVn_)3n`qxh8n@x z&>Y$kTW`IB+dwrLnk+j1mv#RGgCQ{=@Td~&z5mJn6c&JiXbSlu`1xw@z?qho;o<0y z*9Y&lBo(^5*L6V`D%=Q~(b-`FsZ0alx0bNjT5D~z>)S8tDyyo%02MNrMf`=Tc+dN` z(1q9jqRZ?;+sLjszD>7DyqkpfVOvrt$wK;4D21{tedyMXt869LA2e|kMV4hp`LMzH zdhJ8uW@Z%Fw1R|WMl}?{u*0B-utLORO&t^)L5m$Tu9Rr3Eu*>!BZJ4TB7cLGhn{=y zj7E}^|4VQ%{C{)K|DHK>-F3+UutQKIJ=jkq|1mECW5UfJ2Rl}w(mzWv!TXC3Y8YCHyw!+2un z;nN$LOm#IF=OjjvIax!T0V%Kq2&u^US5NN!aeRC{6)_lODm*9W*vRxWx-&BR#n~%Y z{{@duWV?g@u=@6w&Te?f1po(A2n26p51+1Ns^Fgy1x8j(Q;}r_8315Qh_jDwMc5p} zNG8wo@GG(+8A2o{pgY$`L+8#8l+QO1B&p7?e)DrDg~z)ZdvMX21hw#=ssHMw6`o;4 z*#yb5Xw2FK=`?FF(dP^Y-l8c}iBb+Cs)=_;Mt9E!ejh5@0ATL`{K^6NvLKui|8ax*&vjIhB${=x*gej&sv*IHBSTQ5h<5;tTsp2X;?0ra z(YB6IAW+`1MeIR4)fW!HM<<~K%^{#YcwNY>K>4@k=7K}+Nltc3Am!4NMid^NhfJ8V zAqh-W;;j~#ax@OJ3K4+ej!@{ggXLr$AV##4sX73k9Y5}RXAfoq8QXtoCdl!M2#ppM z@L^J{Dj{$uQVd2Dw5V$REJ{%gTA*z zWGsKzz(9XSM~n($82IOT)a1;Dk|PZv!;nc3c$L-CaSAXzTyPf(eQlK()P5&P3Bq9j z`1P4%^$Hw0)MXt+3&1Kt{$Z1~>1pov$ll)epu4bMXdf)cngHB$iBFC?uj4~Q4uDev09eKTUEtqd+uzT=dMI-m z1u#@mWK_iII>+%MLjgz-6cNuUI0XWK_c{Pvb*}?KVL54u0t5zphZ~pDE*I%ROAOt? zEz7^N=AOxC*UtM{-6T#bBXYX9PXBd(u?G^!GZyn1$SP93l001lSdhlrDR@&YDt{&7}bJV{b{1YW|X7)DxDxAy>GX`QHTK1LPJf`2wYajNNdKMBAqMdy%jM#KuW=@o zqDhKkil>JwOE?}=v;v#{Wnxb&P?|X0a!CpRP73T<00i{jy0fE`eks}CDHLG?t5O^s zOTnoypa2vANusPk0cdYF{6qlY3lp_=fa>JsgC(#AG7z`}U9LbM*%SAHBC!eT@#88i zmzNi1o#S;Kb`pyKNs6qW-d-l@ocB_H0)P>GqyuLT+)zLX>M0;Oq{F}>*!K+01zq@^ z0D@u^)`|R;%A%?BLQX?4oehfsF^Z_)El*MT9RRQgh}I5Jebn2M0@?(Jz#Rxy=gtk0 z$w)Xtp>^-ZgB;i^QdH11hd@?k{pR)tc881-dysb{>m#BCIG|r#hGAqAI3=*xLytQ# z6Igk?6{H$+1>CiP2eUdS;FlpBdys74%@6<>{q!(|6KiT?(*o%6mZ(1l_~anKaasZx zhfWS{f_A(j*ItL`IPKZv6}%;_Rx^*kPMf-j$POCUh+s2RfI#5P;R>7&a!UL#xHbtu zPu2#Q@Y9c?)*f8wZfOGSr#OhT2GIG~n64lM8~JQbGkIRoScbzZpbF*=fuNt>t#~&q zS^jI;{(c*vL<`WWPcM)hw#L8;A|aRwuL!W|2nd-_^K^XS>lDMAA_EbyWU5hFH~(Jl z!QOaO^uUS5{+o%O{!*4|qV3&(F6gy$SU(93(|{0o^5K>AQheV{45F%AVb@{7cS@xJ8F(30h-tk#>P5(X$^8j-xmbEaVp8ngB?~9~qL0 zzYHbFfR6rMjICMb7D=U2 zY~)sIGJ?fQ@}v2Ruiw#Hmf9y0d9T%hQZo3R58k66zU;MM33NxNF`xuAiZloVnTVRp zf&Vi$7oTRIjFU?n!MN2{34>)_#4u69{`nqiUy2p}_g;V_FI$^HYtTyI&W6_>+@DOt ziPPX4G4D>6e@vr{Y>yIUUd9bvjhLs@7gJ!(_#6O$d`sT&|+qZw$w7xKo;|J+i zFUA;$Zw4t+N?Te2Ng!m58k6YNCefi4EeR=haPU9q-uwwIorIF5L#T@#OD`EpU4o#c zAaqEETtX5Qvt5^-`}j%Di6=F`OsBr5g2R3BJWsAm_52ZV$J@Xe^vpmN#I1Ujf3sSv zODjK(=SBX{Qg7nvyk)ZN6j$>{lN$SD(@C*}vx0eDoFWn8yqZzsZ?{s39#N;HCsPJh zYab9`{c!JS_a5uh{RRn^Q~?e_5MZcTmSsrQks-2b*7A;R%C5IITXj!Az<)_qz=J>@ z0thfbp+uk5I_(47R4pFo_vxFsPt!I)*#ey+)&PQ-0vVtX42$9)aH7?w?G!8dO19}_ z{hLq)XVySNz%}wQ009LUcytXPblO$Eky5s@m0CCNu?y1tieLr;3;_f#Pz-9^7ml|L zyM;9)+dOo!i}E!{u+$>}0hfR&$Oe_sp%Mvtemx$p)|;l2UEDMYUUX-WQ3OPg41^$s zT7nm|>NpRpW{$GUp6G5^c;7&E{r%0mDFn;-)aYN^>UonoRB zF1+$Ebom)1A;1t60vR}h6l&ZS>(C`6^umQ*Y-BHzkK|bZ|&v+ zY;d_8-lQPFkijlw$6{rxbvzS|nT1ClHqh=kyqJP)z(Gg_bv+%;dPcAft9uixXIuTI3PyWIc)002ovPDHLk FV1gM5j=KN= literal 0 HcmV?d00001 diff --git a/extensions/BMO/web/producticons/webextensions.png b/extensions/BMO/web/producticons/webextensions.png new file mode 100644 index 0000000000000000000000000000000000000000..7e5503d97a1ab63e28d4789abaa67a78be650d5e GIT binary patch literal 3711 zcmV-_4uJ8AP)C0008|P)t-sM{rF4 z>Dc9ZM3YuH{ol~#ctQW^;Q#;s|LWHN|Nri$g7C7F_}a|##jWLbKK9kf?3r-pd`In` zc<-x;=!RDEyPxQbTJpD<@seWgopbijyyksL>yv2o%eL^ZkL;an@2!gGfK2kgrRIJ~ z_}R(vx}WW(fb61t=Y2`*m1^dIO!eZ{+=*cI)4~1a*zBTu|L55A!KnAt!RnD^_ub6q zd`9JaM(?hT=6FKod`9%kw)4HD_twSfh*|Z(rvK#R_06{Nx}ET@j`qs2>5pRXv6Aw> zrSiC(=6OWrc|!5MqV>(W^~$sK#jf$YpZ46)>WyILiCgyA%q_gi`5^U-sbC z^vJU8pLgt=a_*;v_RF^DiCONZg7C4D=Yvu3tBCBFZ|$CV^~J63rh@C9ckii&@xrI< zpnCACi0Fh-_SwknopkDwW$mJU^TDa_w2|?(mh78y>zs4yk7MnogXMWc@UoNapLyz$ zVe-DD@2`&SpL*|&T5EW^XZx0~^}obRfK zWp3W$g}adnen-v@xrO`xS8pTTj`8l=!RDFn{VieS?iT*>zZxt zrhx9Ni0hec?W2I?cR%f)df$Fe?W2D4!Ks^FKjwQy>zHrgk!tgaRqms9?xlF{rFj|} z9PXre@1}X^hEeXIa_yXM4;32fkY4GESLlLF>y>2}7#r@RckZHf=!aA9c|Yx&Y!Vh5 z?VWDugHP>vKJA`w>32P+Su@>nJH25vJ18JSEFvf%9Wx~#mr^inKPn9p7fmf4(rr5H zb2i9fF2Z6nu~;k{8XO!O9Fb2igGns2UNzEgJ(W!=qEjp7bv@*9H{@_O<#RgabUWpA zJmhXO<#IXXZ#Cs}JLGaW<#jyfctPZII^=9I<#9LTZ8GM0LgjZq=YL7%c0T5NMC5Wh z=665ql40k4N9>qr>4{b9j9Tubcj}K^?3!xsp>r;s_Fn)10IPITPE!B^{P++17Xb$Z zoQNhj{TH?Ht&{UY{%FotM(M^Skp`IVJNhJRp{VS<<{G&z3&9Z-D#;-B^K~39`q!WqE^Ul4GH_02*AN>4$ z=bZD-oOj=Q7ygUhdFP#TH?N%h{zAje^$?Ko_A3{X0SEsNer!23TD5k5VXCX@_I3zF z(5q~kaTqh_Iz~XRm`Wij zKKcZd&Th19l-SO0(CLp-d}a31BomDqt2NY}fV-(A6FNY{GIL^Z1s-f$$wP|Xf)fi(Y%%_7Pb^gx=DwYsqU$9`}r zyl5f%@4)Vu;=;^{PLSo@b5O&o-t2C`OO6_}x)3B4xyr_x3&%l{UuL0(K=+1p+Cm-U z2axovFj2{Tb`&HHR3CU9ENL4esKG+tN-;YpOzmPkB`vOB!%)LKwkP!f?Bb|F?nrmx zad&l(b_=ra>NFe+70WS~ZZG`Kba)G->t?G!Tf#Apq; zm3xFP_faO2FTv%8&2Skut;vCuGLP{t@>2khb-N#}441`eZR*C|7u8ohm+C;Rfm*b> z4Ho5mC*{}RR_*J-EtDFdE8q*7KrQlU@#VCx*|gCYPDWSdLsGbw2=ueV99iI~CFu3B zhbIm_c}xZRBuHrgHzyt*j!XHqTVX+*q6gwkO*z!Y3~=g9%{wH4dUPlZOGXGsFG66( zgh+UI=?bLeG#UwfyNs0u;POGU!SF4zjlI$$t#@M*G-U>2CU72wO(n2A|oPV|6=eEbkJ`3OTMA3tOb zla4SHI;GF=z$-YE_59x0^l;Ymd*jY2(CU%$XK4x$yE`vh< zC0+i6&60Qm{o^dj7TP^=64G?*_WuNBq4x3d_K;&S^o3F9)i1gj6}sm_KS)-$X!4uW z@xzh&yw;M);j%S;N-&M+ld-yz*1Y;Hmp__zP>EJa=bvp43{-MDItx|*{O$MWoC$ZOWEZ5%ueF5w`X3cQx(yTV)q?SV+cWprrm8rOpDD=7 z!{KC#LWqE2u*n?TVsr>9BO#z>$YVw{#sHDY!>ADqASgsM{=*;WP?$zV;-=@G-S)2U zZtJ=y@3(K;-rhd;O51hXwLNVUS9ZI$+r76l-j;v({QXVOx8HMe&+qp;1AZTrDy=IM z-bA;WOe!5BD7CD|5%WOL=WMAYxtu4~KD~fTU9!OzTiwwkQ&R*&!7ls$1XadltIbbh zjL256%KbNc9J*%YbGPohIciq($t-U1VH3OYHn&RAiV=oLZFFXjN-Hzl+N(eiLM+Ag z0rohUX50lhnfjV(Oyxh9rvsL(jc6^sLmOX@r5Hj;G`XC$6UG?b<@ERvj6h0tKBdb- zGhH+Io=ra=-8eF)V|)Kcac&S`wj<-wp^BXAtGOm2E%`M?K&`-q-5Y62?gvK?K zsy6mEp_l>HXZ+f1d)y8zn#8Ny6lZ`8yYdbJLDubXQ)F}t!iH7+%8#uH4`95M&i4_l z0gP3%c7h}v${U2NV-%duakMzzdAhBi0DexZq6UzE;gcXKJ7*b4sgxiI6goUrXozKE zV%N|b0b%~KAd(`f6cNFD#x9uPQ>@5&f&9NrDK?;%i?Sb7Vb3fQz0>+O-@?XYyrs8Ds3<(gKb@7v2%fwee zy2(wrjqLAAKziBlU^uGR^zqz#qpEFs;xiz9EZiPPRrT8`BctwASy2(zb~Ma4czfWP0eaIUwcN|J*#Vm1cUy7 zLUHg_F^}ajC4~u$y%yTxurNYXafgGZy##>>=v@OuW9mK~h7e-XKpT@nt6)T|#Ydk8 zVll-_u;q-AN&aZgmNYo@5DopvAJJ5zvO{Z65xr5QszmzAzky2zio3z_885 z5O`k+5QoCU6l5VA=wtz4EvwZX%?Bd!bqGfzXmSfiz_>#r^;h$wqTLVADxfy%KsW+g zMnD$qeIAI$OA3rx1NA-3cmlvUup$G9#}uWGj24yx5zu~ieG>Zj08!auponM$7XAR5 zSW#HDP3!_;#b7fUcA%t_1%O%yCyQU8-0KdZ;Rswj5m@1K5$`{#bw~H~hHFk8iU%1fT+0oc$?C$Yc%*ACOEtFT=8OGRn z<+n3GYxH`}FK2)4Xfbv(=9Oh2O=Qis8(Z4X%k>&fU7bdwm!G}fVl-D&rm;CJzS>@= zuNy&r?)R%7Rf059HuJ{0I0JgkpGT@dTB)4bd0sxU{<*){`D;2I9Jzjmb)Y|gco}Hm*nzGm;X3X1a1i#+aI~};>90+TncU(%kTgGg{@1# d|KT>b`4=k%yj?&002ovPDHLkV1nbUi`D=D literal 0 HcmV?d00001 From 0bb8ea6781ed6262e1a254755d697ab80428754e Mon Sep 17 00:00:00 2001 From: Joerg Sonnenberger Date: Wed, 20 Jun 2018 21:54:59 +0200 Subject: [PATCH 082/119] Bug 1469827 - The etiquette check on "Create new a Bugzilla account" lacks a proper label --- extensions/BMO/template/en/default/account/create.html.tmpl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/extensions/BMO/template/en/default/account/create.html.tmpl b/extensions/BMO/template/en/default/account/create.html.tmpl index 64cf9bad81..fa897e0e88 100644 --- a/extensions/BMO/template/en/default/account/create.html.tmpl +++ b/extensions/BMO/template/en/default/account/create.html.tmpl @@ -157,9 +157,11 @@ function onSubmit() { + From fca89e2f4c4a0660d0ce8947f2de375ec20f124b Mon Sep 17 00:00:00 2001 From: Dylan William Hardison Date: Wed, 20 Jun 2018 16:42:07 -0400 Subject: [PATCH 083/119] Bug 1469920 - Update schema: add fulltext index to profiles.realname --- Bugzilla/DB/Schema.pm | 7 ++++++- Bugzilla/Install/DB.pm | 7 +++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/Bugzilla/DB/Schema.pm b/Bugzilla/DB/Schema.pm index 19275206d7..67ee9071c1 100644 --- a/Bugzilla/DB/Schema.pm +++ b/Bugzilla/DB/Schema.pm @@ -924,6 +924,8 @@ use constant ABSTRACT_SCHEMA => { cryptpassword => {TYPE => 'varchar(128)'}, realname => {TYPE => 'varchar(255)', NOTNULL => 1, DEFAULT => "''"}, + nickname => {TYPE => 'varchar(255)', NOTNULL => 1, + DEFAULT => "''"}, disabledtext => {TYPE => 'MEDIUMTEXT', NOTNULL => 1, DEFAULT => "''"}, disable_mail => {TYPE => 'BOOLEAN', NOTNULL => 1, @@ -943,7 +945,10 @@ use constant ABSTRACT_SCHEMA => { profiles_login_name_idx => {FIELDS => ['login_name'], TYPE => 'UNIQUE'}, profiles_extern_id_idx => {FIELDS => ['extern_id'], - TYPE => 'UNIQUE'} + TYPE => 'UNIQUE'}, + profiles_nickname_idx => ['nickname'], + profiles_realname_ft_idx => {FIELDS => ['realname'], + TYPE => 'FULLTEXT'}, ], }, diff --git a/Bugzilla/Install/DB.pm b/Bugzilla/Install/DB.pm index 1729a134bf..d81bcfbdcc 100644 --- a/Bugzilla/Install/DB.pm +++ b/Bugzilla/Install/DB.pm @@ -767,6 +767,13 @@ sub update_table_definitions { $dbh->bz_add_column('components', 'triage_owner_id', {TYPE => 'INT3'}); + $dbh->bz_add_column('profiles', 'nickname', + {TYPE => 'varchar(255)', NOTNULL => 1, DEFAULT => "''"}); + $dbh->bz_add_index('profiles', 'profiles_nickname_idx', [qw(nickname)]); + + $dbh->bz_add_index('profiles', 'profiles_realname_ft_idx', + {TYPE => 'FULLTEXT', FIELDS => ['realname']}); + ################################################################ # New --TABLE-- changes should go *** A B O V E *** this point # ################################################################ From 52100a9f4f2e5b5d3249934143fb9a7097f156f9 Mon Sep 17 00:00:00 2001 From: Kohei Yoshino Date: Thu, 21 Jun 2018 10:28:21 -0400 Subject: [PATCH 084/119] Bug 1469333 - Check attachment file size client-side and inform user of too large file before uploading it --- js/attachment.js | 15 +++++++++++++++ skins/standard/attachment.css | 8 ++++++++ .../attachment/createformcontents.html.tmpl | 5 +++-- template/en/default/global/header.html.tmpl | 1 + 4 files changed, 27 insertions(+), 2 deletions(-) diff --git a/js/attachment.js b/js/attachment.js index f967f64d35..6d6dae58de 100644 --- a/js/attachment.js +++ b/js/attachment.js @@ -93,6 +93,21 @@ function DataFieldHandler() { } } } + + // Check the current file size (in KB) + const file_size = field_data.files[0].size / 1024; + const max_size = BUGZILLA.param.maxattachmentsize; + const invalid = file_size > max_size; + const message = invalid ? `This file (${(file_size / 1024).toFixed(1)} MB) is larger than the ` + + `maximum allowed size (${(max_size / 1024).toFixed(1)} MB).
    Please consider uploading it ` + + `to an online file storage and sharing the link in a bug comment instead.` : ''; + const message_short = invalid ? 'File too large' : ''; + const $error = document.querySelector('#data-error'); + + // Show an error message if the file is too large + $error.innerHTML = message; + field_data.setCustomValidity(message_short); + field_data.setAttribute('aria-invalid', invalid); } function clearAttachmentFields() { diff --git a/skins/standard/attachment.css b/skins/standard/attachment.css index ace4b67813..401bce92b8 100644 --- a/skins/standard/attachment.css +++ b/skins/standard/attachment.css @@ -38,6 +38,14 @@ table#attachment_flags td { font-size: small; } +#data-error { + margin: 4px 0 0; +} + +#data-error:empty { + margin: 0; +} + /* Rules used to view patches in diff mode. */ .file_head { diff --git a/template/en/default/attachment/createformcontents.html.tmpl b/template/en/default/attachment/createformcontents.html.tmpl index 41a02a913c..61ddceac37 100644 --- a/template/en/default/attachment/createformcontents.html.tmpl +++ b/template/en/default/attachment/createformcontents.html.tmpl @@ -37,7 +37,8 @@ Enter the path to the file on your computer (or paste text as attachment).
    - + +
    @@ -69,7 +70,7 @@ [%# Reset this whenever the page loads so that the JS state is up to date %]