diff options
Diffstat (limited to 'extensions/BugModal/Extension.pm')
-rw-r--r-- | extensions/BugModal/Extension.pm | 551 |
1 files changed, 275 insertions, 276 deletions
diff --git a/extensions/BugModal/Extension.pm b/extensions/BugModal/Extension.pm index ef9c93a37..8b72bb757 100644 --- a/extensions/BugModal/Extension.pm +++ b/extensions/BugModal/Extension.pm @@ -26,322 +26,321 @@ use JSON::XS qw(encode_json); our $VERSION = '1'; use constant READABLE_BUG_STATUS_PRODUCTS => ( - 'Core', - 'Toolkit', - 'Firefox', - 'Firefox for Android', - 'Firefox for iOS', - 'Bugzilla', - 'bugzilla.mozilla.org' + 'Core', 'Toolkit', + 'Firefox', 'Firefox for Android', + 'Firefox for iOS', 'Bugzilla', + 'bugzilla.mozilla.org' ); sub show_bug_format { - my ($self, $args) = @_; - $args->{format} = _alternative_show_bug_format(); + my ($self, $args) = @_; + $args->{format} = _alternative_show_bug_format(); } sub edit_bug_format { - my ($self, $args) = @_; - $args->{format} = _alternative_show_bug_format(); + my ($self, $args) = @_; + $args->{format} = _alternative_show_bug_format(); } sub _alternative_show_bug_format { - my $cgi = Bugzilla->cgi; - my $user = Bugzilla->user; - if (my $ctype = $cgi->param('ctype')) { - return '' if $ctype ne 'html'; - } - if (my $format = $cgi->param('format')) { - return ($format eq '__default__' || $format eq 'default') ? '' : $format; - } - return $user->setting('ui_experiments') eq 'on' ? 'modal' : ''; + my $cgi = Bugzilla->cgi; + my $user = Bugzilla->user; + if (my $ctype = $cgi->param('ctype')) { + return '' if $ctype ne 'html'; + } + if (my $format = $cgi->param('format')) { + return ($format eq '__default__' || $format eq 'default') ? '' : $format; + } + return $user->setting('ui_experiments') eq 'on' ? 'modal' : ''; } sub template_after_create { - my ($self, $args) = @_; - my $context = $args->{template}->context; - - # wrapper around time_ago() - $context->define_filter( - time_duration => sub { - my ($context) = @_; - return sub { - my ($timestamp) = @_; - my $datetime = datetime_from($timestamp) - // return $timestamp; - return time_ago($datetime); - }; - }, 1 - ); - - # morph a string into one which is suitable to use as an element's id - $context->define_filter( - id => sub { - my ($context) = @_; - return sub { - my ($id) = @_; - $id //= ''; - $id = lc($id); - while ($id ne '' && $id !~ /^[a-z]/) { - $id = substr($id, 1); - } - $id =~ tr/ /-/; - $id =~ s/[^a-z\d\-_:\.]/_/g; - return $id; - }; - }, 1 - ); - - # parse date string and output epoch - $context->define_filter( - epoch => sub { - my ($context) = @_; - return sub { - my ($date_str) = @_; - return date_str_to_time($date_str); - }; - }, 1 - ); - - # flatten a list of hashrefs to a list of values - # eg. logins = users.pluck("login") - $context->define_vmethod( - list => pluck => sub { - my ($list, $field) = @_; - return [ map { $_->$field } @$list ]; - } - ); - - # returns array where the value in $field does not equal $value - # opposite of "only" - # eg. not_byron = users.skip("name", "Byron") - $context->define_vmethod( - list => skip => sub { - my ($list, $field, $value) = @_; - return [ grep { $_->$field ne $value } @$list ]; - } - ); - - # returns array where the value in $field equals $value - # opposite of "skip" - # eg. byrons_only = users.only("name", "Byron") - $context->define_vmethod( - list => only => sub { - my ($list, $field, $value) = @_; - return [ grep { $_->$field eq $value } @$list ]; - } - ); - - # returns boolean indicating if the value exists in the list - # eg. has_byron = user_names.exists("byron") - $context->define_vmethod( - list => exists => sub { - my ($list, $value) = @_; - return any { $_ eq $value } @$list; - } - ); - - # ucfirst is only available in new template::toolkit versions - $context->define_vmethod( - item => ucfirst => sub { - my ($text) = @_; - return ucfirst($text); - } - ); -} - -sub template_before_process { - my ($self, $args) = @_; - my $file = $args->{file}; - my $vars = $args->{vars}; - - if ($file eq 'bug/process/header.html.tmpl' - || $file eq 'bug/create/created.html.tmpl' - || $file eq 'attachment/created.html.tmpl' - || $file eq 'attachment/updated.html.tmpl') - { - if (_alternative_show_bug_format() eq 'modal') { - $vars->{alt_ui_header} = 'bug_modal/header.html.tmpl'; - $vars->{alt_ui_show} = 'bug/show-modal.html.tmpl'; - $vars->{alt_ui_edit} = 'bug_modal/edit.html.tmpl'; + my ($self, $args) = @_; + my $context = $args->{template}->context; + + # wrapper around time_ago() + $context->define_filter( + time_duration => sub { + my ($context) = @_; + return sub { + my ($timestamp) = @_; + my $datetime = datetime_from($timestamp) // return $timestamp; + return time_ago($datetime); + }; + }, + 1 + ); + + # morph a string into one which is suitable to use as an element's id + $context->define_filter( + id => sub { + my ($context) = @_; + return sub { + my ($id) = @_; + $id //= ''; + $id = lc($id); + while ($id ne '' && $id !~ /^[a-z]/) { + $id = substr($id, 1); } - return; + $id =~ tr/ /-/; + $id =~ s/[^a-z\d\-_:\.]/_/g; + return $id; + }; + }, + 1 + ); + + # parse date string and output epoch + $context->define_filter( + epoch => sub { + my ($context) = @_; + return sub { + my ($date_str) = @_; + return date_str_to_time($date_str); + }; + }, + 1 + ); + + # flatten a list of hashrefs to a list of values + # eg. logins = users.pluck("login") + $context->define_vmethod( + list => pluck => sub { + my ($list, $field) = @_; + return [map { $_->$field } @$list]; } - - if ($file =~ m#^bug/show-([^\.]+)\.html\.tmpl$#) { - my $format = $1; - return unless _alternative_show_bug_format() eq $format; + ); + + # returns array where the value in $field does not equal $value + # opposite of "only" + # eg. not_byron = users.skip("name", "Byron") + $context->define_vmethod( + list => skip => sub { + my ($list, $field, $value) = @_; + return [grep { $_->$field ne $value } @$list]; } - elsif ($file ne 'bug_modal/edit.html.tmpl') { - return; + ); + + # returns array where the value in $field equals $value + # opposite of "skip" + # eg. byrons_only = users.only("name", "Byron") + $context->define_vmethod( + list => only => sub { + my ($list, $field, $value) = @_; + return [grep { $_->$field eq $value } @$list]; } - - if ($vars->{bug} && !$vars->{bugs}) { - $vars->{bugs} = [$vars->{bug}]; + ); + + # returns boolean indicating if the value exists in the list + # eg. has_byron = user_names.exists("byron") + $context->define_vmethod( + list => exists => sub { + my ($list, $value) = @_; + return any { $_ eq $value } @$list; } + ); - return unless - $vars->{bugs} - && ref($vars->{bugs}) eq 'ARRAY' - && scalar(@{ $vars->{bugs} }) == 1; - my $bug = $vars->{bugs}->[0]; - return if exists $bug->{error}; - - # trigger loading of tracking flags - if (Bugzilla->has_extension('TrackingFlags')) { - Bugzilla::Extension::TrackingFlags->template_before_process({ - file => 'bug/edit.html.tmpl', - vars => $vars, - }); + # ucfirst is only available in new template::toolkit versions + $context->define_vmethod( + item => ucfirst => sub { + my ($text) = @_; + return ucfirst($text); } + ); +} - if (any { $bug->product eq $_ } READABLE_BUG_STATUS_PRODUCTS) { - my @flags = map { { name => $_->name, status => $_->status } } @{$bug->flags}; - $vars->{readable_bug_status_json} = encode_json({ - dupe_of => $bug->dup_id, - id => $bug->id, - keywords => [ map { $_->name } @{$bug->keyword_objects} ], - priority => $bug->priority, - resolution => $bug->resolution, - status => $bug->bug_status, - flags => \@flags, - target_milestone => $bug->target_milestone, - map { $_->name => $_->bug_flag($bug->id)->value } @{$vars->{tracking_flags}}, - }); - # HTML4 attributes cannot be longer than this, so just skip it in this case. - if (length($vars->{readable_bug_status_json}) > 65536) { - delete $vars->{readable_bug_status_json}; - } +sub template_before_process { + my ($self, $args) = @_; + my $file = $args->{file}; + my $vars = $args->{vars}; + + if ( $file eq 'bug/process/header.html.tmpl' + || $file eq 'bug/create/created.html.tmpl' + || $file eq 'attachment/created.html.tmpl' + || $file eq 'attachment/updated.html.tmpl') + { + if (_alternative_show_bug_format() eq 'modal') { + $vars->{alt_ui_header} = 'bug_modal/header.html.tmpl'; + $vars->{alt_ui_show} = 'bug/show-modal.html.tmpl'; + $vars->{alt_ui_edit} = 'bug_modal/edit.html.tmpl'; } + return; + } + + if ($file =~ m#^bug/show-([^\.]+)\.html\.tmpl$#) { + my $format = $1; + return unless _alternative_show_bug_format() eq $format; + } + elsif ($file ne 'bug_modal/edit.html.tmpl') { + return; + } + + if ($vars->{bug} && !$vars->{bugs}) { + $vars->{bugs} = [$vars->{bug}]; + } + + return + unless $vars->{bugs} + && ref($vars->{bugs}) eq 'ARRAY' + && scalar(@{$vars->{bugs}}) == 1; + my $bug = $vars->{bugs}->[0]; + return if exists $bug->{error}; + + # trigger loading of tracking flags + if (Bugzilla->has_extension('TrackingFlags')) { + Bugzilla::Extension::TrackingFlags->template_before_process({ + file => 'bug/edit.html.tmpl', vars => $vars, + }); + } + + if (any { $bug->product eq $_ } READABLE_BUG_STATUS_PRODUCTS) { + my @flags = map { {name => $_->name, status => $_->status} } @{$bug->flags}; + $vars->{readable_bug_status_json} = encode_json({ + dupe_of => $bug->dup_id, + id => $bug->id, + keywords => [map { $_->name } @{$bug->keyword_objects}], + priority => $bug->priority, + resolution => $bug->resolution, + status => $bug->bug_status, + flags => \@flags, + target_milestone => $bug->target_milestone, + map { $_->name => $_->bug_flag($bug->id)->value } @{$vars->{tracking_flags}}, + }); - # bug->choices loads a lot of data that we want to lazy-load - # just load the status and resolutions and perform extra checks here - # upstream does these checks in the bug/fields template - my $perms = $bug->user; - my @resolutions; - foreach my $r (@{ Bugzilla::Field->new({ name => 'resolution', cache => 1 })->legal_values }) { - my $resolution = $r->name; - next unless $resolution; - - # always allow the current value - if ($resolution eq $bug->resolution) { - push @resolutions, $r; - next; - } - - # never allow inactive values - next unless $r->is_active; + # HTML4 attributes cannot be longer than this, so just skip it in this case. + if (length($vars->{readable_bug_status_json}) > 65536) { + delete $vars->{readable_bug_status_json}; + } + } + + # bug->choices loads a lot of data that we want to lazy-load + # just load the status and resolutions and perform extra checks here + # upstream does these checks in the bug/fields template + my $perms = $bug->user; + my @resolutions; + foreach my $r ( + @{Bugzilla::Field->new({name => 'resolution', cache => 1})->legal_values}) + { + my $resolution = $r->name; + next unless $resolution; + + # always allow the current value + if ($resolution eq $bug->resolution) { + push @resolutions, $r; + next; + } - # ensure the user has basic rights to change this field - next unless $bug->check_can_change_field('resolution', '---', $resolution); + # never allow inactive values + next unless $r->is_active; - # canconfirm users can only set the resolution to WFM, INCOMPLETE or DUPE - if ($perms->{canconfirm} - && !($perms->{canedit} || $perms->{isreporter})) - { - next if - $resolution ne 'WORKSFORME' - && $resolution ne 'INCOMPLETE' - && $resolution ne 'DUPLICATE'; - } + # ensure the user has basic rights to change this field + next unless $bug->check_can_change_field('resolution', '---', $resolution); - # reporters can set it to anything, except INCOMPLETE - if ($perms->{isreporter} - && !($perms->{canconfirm} || $perms->{canedit})) - { - next if $resolution eq 'INCOMPLETE'; - } + # canconfirm users can only set the resolution to WFM, INCOMPLETE or DUPE + if ($perms->{canconfirm} && !($perms->{canedit} || $perms->{isreporter})) { + next + if $resolution ne 'WORKSFORME' + && $resolution ne 'INCOMPLETE' + && $resolution ne 'DUPLICATE'; + } - # expired has, uh, expired - next if $resolution eq 'EXPIRED'; + # reporters can set it to anything, except INCOMPLETE + if ($perms->{isreporter} && !($perms->{canconfirm} || $perms->{canedit})) { + next if $resolution eq 'INCOMPLETE'; + } - push @resolutions, $r; + # expired has, uh, expired + next if $resolution eq 'EXPIRED'; + + push @resolutions, $r; + } + $bug->{choices} = { + bug_status => [ + grep { $_->is_active || $_->name eq $bug->bug_status } + @{$bug->statuses_available} + ], + resolution => \@resolutions, + }; + + # group tracking flags by version to allow for a better tabular output + my @tracking_table; + my $tracking_flags = $vars->{tracking_flags}; + foreach my $flag (@$tracking_flags) { + my $flag_type = $flag->flag_type; + my $type = 'status'; + my $name = $flag->description; + if ($flag_type eq 'tracking' && $name =~ /^(tracking|status)-(.+)/) { + ($type, $name) = ($1, $2); } - $bug->{choices} = { - bug_status => [ - grep { $_->is_active || $_->name eq $bug->bug_status } - @{ $bug->statuses_available } - ], - resolution => \@resolutions, - }; - - # group tracking flags by version to allow for a better tabular output - my @tracking_table; - my $tracking_flags = $vars->{tracking_flags}; - foreach my $flag (@$tracking_flags) { - my $flag_type = $flag->flag_type; - my $type = 'status'; - my $name = $flag->description; - if ($flag_type eq 'tracking' && $name =~ /^(tracking|status)-(.+)/) { - ($type, $name) = ($1, $2); - } - my ($existing) = grep { $_->{type} eq $flag_type && $_->{name} eq $name } @tracking_table; - if ($existing) { - $existing->{$type} = $flag; - } - else { - push @tracking_table, { - $type => $flag, - name => $name, - type => $flag_type, - }; - } + my ($existing) + = grep { $_->{type} eq $flag_type && $_->{name} eq $name } @tracking_table; + if ($existing) { + $existing->{$type} = $flag; } - $vars->{tracking_flags_table} = \@tracking_table; - - # for the "view -> hide treeherder comments" menu item - my $treeherder_id = Bugzilla->treeherder_user->id; - foreach my $change_set (@{ $bug->activity_stream }) { - if ($change_set->{comment} && $change_set->{comment}->author->id == $treeherder_id) { - $vars->{treeherder} = Bugzilla->treeherder_user; - last; - } + else { + push @tracking_table, {$type => $flag, name => $name, type => $flag_type,}; + } + } + $vars->{tracking_flags_table} = \@tracking_table; + + # for the "view -> hide treeherder comments" menu item + my $treeherder_id = Bugzilla->treeherder_user->id; + foreach my $change_set (@{$bug->activity_stream}) { + if ( $change_set->{comment} + && $change_set->{comment}->author->id == $treeherder_id) + { + $vars->{treeherder} = Bugzilla->treeherder_user; + last; } + } } sub bug_start_of_set_all { - my ($self, $args) = @_; - my $bug = $args->{bug}; - my $params = $args->{params}; - - # reset to the component defaults if not supplied - if (exists $params->{assigned_to} && (!defined $params->{assigned_to} || $params->{assigned_to} eq '')) { - $params->{assigned_to} = $bug->component_obj->default_assignee->login; - } - if (exists $params->{qa_contact} && (!defined $params->{qa_contact} || $params->{qa_contact} eq '') - && $bug->component_obj->default_qa_contact->id) - { - $params->{qa_contact} = $bug->component_obj->default_qa_contact->login; - } + my ($self, $args) = @_; + my $bug = $args->{bug}; + my $params = $args->{params}; + + # reset to the component defaults if not supplied + if (exists $params->{assigned_to} + && (!defined $params->{assigned_to} || $params->{assigned_to} eq '')) + { + $params->{assigned_to} = $bug->component_obj->default_assignee->login; + } + if ( exists $params->{qa_contact} + && (!defined $params->{qa_contact} || $params->{qa_contact} eq '') + && $bug->component_obj->default_qa_contact->id) + { + $params->{qa_contact} = $bug->component_obj->default_qa_contact->login; + } } sub webservice { - my ($self, $args) = @_; - my $dispatch = $args->{dispatch}; - $dispatch->{bug_modal} = 'Bugzilla::Extension::BugModal::WebService'; + my ($self, $args) = @_; + my $dispatch = $args->{dispatch}; + $dispatch->{bug_modal} = 'Bugzilla::Extension::BugModal::WebService'; } sub install_before_final_checks { - my ($self, $args) = @_; - add_setting({ - name => 'ui_experiments', - options => ['on', 'off'], - default => 'on', - category => 'User Interface' - }); - add_setting({ - name => 'ui_remember_collapsed', - options => ['on', 'off'], - default => 'off', - category => 'User Interface' - }); - add_setting({ - name => 'ui_use_absolute_time', - options => ['on', 'off'], - default => 'off', - category => 'User Interface', - }); + my ($self, $args) = @_; + add_setting({ + name => 'ui_experiments', + options => ['on', 'off'], + default => 'on', + category => 'User Interface' + }); + add_setting({ + name => 'ui_remember_collapsed', + options => ['on', 'off'], + default => 'off', + category => 'User Interface' + }); + add_setting({ + name => 'ui_use_absolute_time', + options => ['on', 'off'], + default => 'off', + category => 'User Interface', + }); } __PACKAGE__->NAME; |