diff options
Diffstat (limited to 'extensions/Needinfo')
10 files changed, 442 insertions, 0 deletions
diff --git a/extensions/Needinfo/Config.pm b/extensions/Needinfo/Config.pm new file mode 100644 index 000000000..86c99ec59 --- /dev/null +++ b/extensions/Needinfo/Config.pm @@ -0,0 +1,18 @@ +# 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::Needinfo; +use strict; + +use constant NAME => 'Needinfo'; + +use constant REQUIRED_MODULES => [ +]; + +use constant OPTIONAL_MODULES => [ +]; + +__PACKAGE__->NAME; diff --git a/extensions/Needinfo/Extension.pm b/extensions/Needinfo/Extension.pm new file mode 100644 index 000000000..2a4bfa3b3 --- /dev/null +++ b/extensions/Needinfo/Extension.pm @@ -0,0 +1,180 @@ +# 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::Needinfo; + +use strict; + +use base qw(Bugzilla::Extension); + +use Bugzilla::Error; +use Bugzilla::Flag; +use Bugzilla::FlagType; +use Bugzilla::User; + +our $VERSION = '0.01'; + +sub install_update_db { + my ($self, $args) = @_; + my $dbh = Bugzilla->dbh; + + if (@{ Bugzilla::FlagType::match({ name => 'needinfo' }) }) { + return; + } + + print "Creating needinfo flag ... " . + "enable the Needinfo feature by editing the flag's properties.\n"; + + # Initially populate the list of exclusions as __Any__:__Any__ to + # allow admin to decide which products to enable the flag for. + my $flagtype = Bugzilla::FlagType->create({ + name => 'needinfo', + description => "Set this flag when the bug is in need of additional information", + target_type => 'bug', + cc_list => '', + sortkey => 1, + is_active => 1, + is_requestable => 1, + is_requesteeble => 1, + is_multiplicable => 0, + request_group => '', + grant_group => '', + inclusions => [], + exclusions => ['0:0'], + }); +} + +# Clear the needinfo? flag if comment is being given by +# requestee or someone used the override flag. +sub bug_start_of_update { + my ($self, $args) = @_; + my $bug = $args->{bug}; + my $old_bug = $args->{old_bug}; + + my $user = Bugzilla->user; + my $cgi = Bugzilla->cgi; + my $params = Bugzilla->input_params; + + if ($params->{needinfo}) { + # do a match if applicable + Bugzilla::User::match_field({ + 'needinfo_from' => { 'type' => 'multi' } + }); + } + + # Set needinfo_done param to true so as to not loop back here + return if $params->{needinfo_done}; + $params->{needinfo_done} = 1; + Bugzilla->input_params($params); + + my $add_needinfo = delete $params->{needinfo}; + my $needinfo_from = delete $params->{needinfo_from}; + my $needinfo_role = delete $params->{needinfo_role}; + my $is_private = $params->{'comment_is_private'}; + + my @needinfo_overrides; + foreach my $key (grep(/^needinfo_override_/, keys %$params)) { + my ($id) = $key =~ /(\d+)$/; + # Should always be true if key exists (checkbox) but better to be sure + push(@needinfo_overrides, $id) if $id && $params->{$key}; + } + + # Set the needinfo flag if user is requesting more information + my @new_flags; + my $needinfo_requestee; + + if ($add_needinfo) { + foreach my $type (@{ $bug->flag_types }) { + next if $type->name ne 'needinfo'; + my %requestees; + + # Allow anyone to be the requestee + if (!$needinfo_role) { + $requestees{'anyone'} = 1; + } + # Use assigned_to as requestee + elsif ($needinfo_role eq 'assigned_to') { + $requestees{$bug->assigned_to->login} = 1; + } + # Use reporter as requestee + elsif ($needinfo_role eq 'reporter') { + $requestees{$bug->reporter->login} = 1; + } + # Use qa_contact as requestee + elsif ($needinfo_role eq 'qa_contact') { + $requestees{$bug->qa_contact->login} = 1; + } + # Use current user as requestee + elsif ($needinfo_role eq 'user') { + $requestees{$user->login} = 1; + } + # Use user specified requestee + elsif ($needinfo_role eq 'other' && $needinfo_from) { + my @needinfo_from_list = ref $needinfo_from + ? @$needinfo_from : + ($needinfo_from); + foreach my $requestee (@needinfo_from_list) { + my $requestee_obj = Bugzilla::User->check($requestee); + $requestees{$requestee_obj->login} = 1; + } + } + + # Find out if the requestee has already been used and skip if so + my $requestee_found; + foreach my $flag (@{ $type->{flags} }) { + if (!$flag->requestee && $requestees{'anyone'}) { + delete $requestees{'anyone'}; + } + if ($flag->requestee && $requestees{$flag->requestee->login}) { + delete $requestees{$flag->requestee->login}; + } + } + + foreach my $requestee (keys %requestees) { + my $needinfo_flag = { type_id => $type->id, status => '?' }; + if ($requestee ne 'anyone') { + $needinfo_flag->{requestee} = $requestee; + } + push(@new_flags, $needinfo_flag); + } + } + } + + my @flags; + foreach my $flag (@{ $bug->flags }) { + next if $flag->type->name ne 'needinfo'; + # Clear if somehow the flag has been set to +/- + # or if the "clear needinfo" override checkbox is selected + if ($flag->status ne '?' + or grep { $_ == $flag->id } @needinfo_overrides) + { + push(@flags, { id => $flag->id, status => 'X' }); + } + } + + if (@flags || @new_flags) { + $bug->set_flags(\@flags, \@new_flags); + } +} + +sub object_before_delete { + my ($self, $args) = @_; + my $object = $args->{object}; + return unless $object->isa('Bugzilla::Flag') + && $object->type->name eq 'needinfo'; + my $user = Bugzilla->user; + + # Require canconfirm to clear requests targetted at someone else + if ($object->setter_id != $user->id + && $object->requestee + && $object->requestee->id != $user->id + && !$user->in_group('canconfirm')) + { + ThrowUserError('needinfo_illegal_change'); + } +} + +__PACKAGE__->NAME; diff --git a/extensions/Needinfo/template/en/default/bug/needinfo.html.tmpl b/extensions/Needinfo/template/en/default/bug/needinfo.html.tmpl new file mode 100644 index 000000000..de1191520 --- /dev/null +++ b/extensions/Needinfo/template/en/default/bug/needinfo.html.tmpl @@ -0,0 +1,158 @@ +[%# 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. + #%] + +[% needinfo_flagtype = "" %] +[% needinfo_flags = [] %] + +[% FOREACH type = bug.flag_types %] + [% IF type.name == 'needinfo' %] + [% needinfo_flagtype = type %] + [% FOREACH flag = type.flags %] + [% IF flag.status == '?' %] + [% needinfo_flags.push(flag) %] + [% END %] + [% END %] + [% LAST IF needinfo_flagtype %] + [% END %] +[% END %] + +[% IF needinfo_flagtype %] + <div id="needinfo_container"> + [% IF needinfo_flags.size > 0 %] + [%# Displays NEEDINFO tag in bug header %] + <script> + var summary_container = document.getElementById('static_bug_status'); + summary_container.appendChild(document.createTextNode('[NEEDINFO]')); + </script> + [% END %] + <table> + [% FOREACH flag = needinfo_flags %] + <tr> + [% IF !flag.requestee || flag.requestee.id == user.id %] + [%# needinfo targetted at the current user, or anyone %] + <td align="center"> + <input type="checkbox" id="needinfo_override_[% flag.id FILTER html %]" + name="needinfo_override_[% flag.id FILTER html %]" value="1" + [% " checked" IF flag.requestee || user.in_group("canconfirm") %]> + </td> + <td> + <label for="needinfo_override_[% flag.id FILTER html %]"> + Clear the needinfo request for + <em>[% IF !flag.requestee %]anyone[% ELSE %][% flag.requestee.login FILTER html %][% END %]</em>. + </label> + </td> + [% ELSIF user.in_group("canconfirm") || flag.setter_id == user.id %] + [%# needinfo targetted at someone else, but the user can clear %] + <td align="center"> + <input type="checkbox" id="needinfo_override_[% flag.id FILTER html %]" + name="needinfo_override_[% flag.id FILTER html %]" value="1"> + </td> + <td> + <label for="needinfo_override_[% flag.id FILTER html %]"> + I am providing the requested information for <em>[% flag.requestee.login FILTER html %]</em> + (clears the needinfo request). + </label> + </td> + [% ELSE %] + [%# current user does not have permissions to clear needinfo %] + <td> </td> + <td> + Needinfo requested from <em>[% flag.requestee.login FILTER html %]</em>. + </td> + [% END %] + </tr> + [% END %] + [% IF needinfo_flags.size == 0 || needinfo_flagtype.is_multiplicable %] + <tr> + <td align="center"> + <script> + function needinfo_init() { + needinfo_visibility(); + [% FOREACH flag = needinfo_flags %] + YAHOO.util.Event.on('requestee-[% flag.id FILTER none %]', 'blur', function(e) { + YAHOO.util.Dom.get('needinfo_override_[% flag.id FILTER none %]').checked = + e.target.value == '[% flag.requestee.login FILTER js %]'; + }); + [% END %] + } + + function needinfo_visibility() { + var role = YAHOO.util.Dom.get('needinfo_role').value; + if (role == 'other') { + YAHOO.util.Dom.removeClass('needinfo_from_container', 'bz_default_hidden'); + YAHOO.util.Dom.get('needinfo_from').disabled = false; + YAHOO.util.Dom.get('needinfo_role_identity').innerHTML = ''; + } else { + YAHOO.util.Dom.addClass('needinfo_from_container', 'bz_default_hidden'); + YAHOO.util.Dom.get('needinfo_from').disabled = true; + var identity = ''; + if (role == 'reporter') { + identity = '[% bug.reporter.realname || bug.reporter.login FILTER html FILTER js %]'; + } else if (role == 'assigned_to') { + identity = '[% bug.assigned_to.realname || bug.assigned_to.login FILTER html FILTER js %]'; + } else if (role == 'qa_contact') { + identity = '[% bug.qa_contact.realname || bug.qa_contact.login FILTER html FILTER js %]'; + } else if (role == 'user') { + identity = '[% user.realname || user.login FILTER html FILTER js %]'; + } + YAHOO.util.Dom.get('needinfo_role_identity').innerHTML = identity; + } + } + + function needinfo_focus() { + if (YAHOO.util.Dom.get('needinfo').checked + && YAHOO.util.Dom.get('needinfo_role').value == 'other') + { + YAHOO.util.Dom.get('needinfo_from').focus(); + YAHOO.util.Dom.get('needinfo_from').select(); + } + } + + function needinfo_role_changed() { + YAHOO.util.Dom.get('needinfo').checked = true; + needinfo_visibility(); + needinfo_focus(); + } + + function needinfo_other_changed() { + YAHOO.util.Dom.get('needinfo').checked = YAHOO.util.Dom.get('needinfo_from').value != ''; + } + + YAHOO.util.Event.onDOMReady(needinfo_init); + </script> + <input type="checkbox" name="needinfo" value="1" id="needinfo" onchange="needinfo_focus()"> + </td> + <td> + <label for="needinfo">Need more information from</label> + <select name="needinfo_role" id="needinfo_role" onchange="needinfo_role_changed()"> + <option value="other">other</option> + <option value="reporter">reporter</option> + <option value="assigned_to">assignee</option> + [% IF Param('useqacontact') && bug.qa_contact.login != "" %] + <option value="qa_contact">qa contact</option> + [% END %] + <option value="user">myself</option> + </select> + <span id="needinfo_from_container"> + [% INCLUDE global/userselect.html.tmpl + id => "needinfo_from" + name => "needinfo_from" + value => "" + size => 30 + multiple => 5 + onchange => "needinfo_other_changed()" + field_title => "Enter one or more comma separated users to request more information from" + %] + </span> + <span id="needinfo_role_identity"></span> + </td> + </tr> + [% END %] + </table> + </div> +[% END %] diff --git a/extensions/Needinfo/template/en/default/hook/attachment/create-form_before_submit.html.tmpl b/extensions/Needinfo/template/en/default/hook/attachment/create-form_before_submit.html.tmpl new file mode 100644 index 000000000..81b6d57ea --- /dev/null +++ b/extensions/Needinfo/template/en/default/hook/attachment/create-form_before_submit.html.tmpl @@ -0,0 +1,16 @@ +[%# 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. + #%] + +<tr> + <td> </td> + <td> + [% PROCESS bug/needinfo.html.tmpl + bug => bug + %] + </td> +</tr> diff --git a/extensions/Needinfo/template/en/default/hook/attachment/edit-after_comment_textarea.html.tmpl b/extensions/Needinfo/template/en/default/hook/attachment/edit-after_comment_textarea.html.tmpl new file mode 100644 index 000000000..cb6ddf30e --- /dev/null +++ b/extensions/Needinfo/template/en/default/hook/attachment/edit-after_comment_textarea.html.tmpl @@ -0,0 +1,11 @@ +[%# 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 bug/needinfo.html.tmpl + bug => attachment.bug +%] diff --git a/extensions/Needinfo/template/en/default/hook/bug/edit-after_comment_commit_button.html.tmpl b/extensions/Needinfo/template/en/default/hook/bug/edit-after_comment_commit_button.html.tmpl new file mode 100644 index 000000000..90f0cc584 --- /dev/null +++ b/extensions/Needinfo/template/en/default/hook/bug/edit-after_comment_commit_button.html.tmpl @@ -0,0 +1,11 @@ +[%# 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 bug/needinfo.html.tmpl + bug = bug +%] diff --git a/extensions/Needinfo/template/en/default/hook/global/header-start.html.tmpl b/extensions/Needinfo/template/en/default/hook/global/header-start.html.tmpl new file mode 100644 index 000000000..7f2095e3d --- /dev/null +++ b/extensions/Needinfo/template/en/default/hook/global/header-start.html.tmpl @@ -0,0 +1,12 @@ +[%# 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. + #%] + +[% IF template.name == 'attachment/create.html.tmpl' + || template.name == 'attachment/edit.html.tmpl' %] + [% style_urls.push('extensions/Needinfo/web/styles/needinfo.css') %] +[% END %] diff --git a/extensions/Needinfo/template/en/default/hook/global/user-error-errors.html.tmpl b/extensions/Needinfo/template/en/default/hook/global/user-error-errors.html.tmpl new file mode 100644 index 000000000..f1241bc61 --- /dev/null +++ b/extensions/Needinfo/template/en/default/hook/global/user-error-errors.html.tmpl @@ -0,0 +1,13 @@ +[%# 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. + #%] + +[% IF error == "needinfo_illegal_change" %] + [% title = 'Needinfo Illegal Change' %] + Only the requestee or a user with the required permissions can clear a + needinfo flag. +[% END %] diff --git a/extensions/Needinfo/template/en/default/hook/request/email-after_summary.txt.tmpl b/extensions/Needinfo/template/en/default/hook/request/email-after_summary.txt.tmpl new file mode 100644 index 000000000..6a302b76a --- /dev/null +++ b/extensions/Needinfo/template/en/default/hook/request/email-after_summary.txt.tmpl @@ -0,0 +1,13 @@ +[%# 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. + #%] + +[% RETURN UNLESS flag && flag.type.name == 'needinfo' && flag.status == '?' %] +--- +--- This request has set a needinfo flag on the [% terms.bug %]. +--- You can clear it by logging in and replying in a comment. +--- diff --git a/extensions/Needinfo/web/styles/needinfo.css b/extensions/Needinfo/web/styles/needinfo.css new file mode 100644 index 000000000..e375ba610 --- /dev/null +++ b/extensions/Needinfo/web/styles/needinfo.css @@ -0,0 +1,10 @@ +/* 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. */ + +#needinfo_container label { + font-weight: normal !important; +} |