From 1f30fac936a3b0905e736dd86e559e33caf036ac Mon Sep 17 00:00:00 2001 From: David Lawrence Date: Wed, 10 Aug 2011 18:26:03 -0400 Subject: Initial checkin of bmo/4.0 extensions. Still todo: port changes to core Bugzilla code --- extensions/ComponentWatching/Config.pm | 26 ++ extensions/ComponentWatching/Extension.pm | 343 +++++++++++++++++++++ .../ComponentWatching/reset-watch-preferences.pl | 75 +++++ .../account/prefs/component_watch.html.tmpl | 148 +++++++++ .../account/prefs/email-relationships.html.tmpl | 22 ++ .../hook/account/prefs/prefs-tabs.html.tmpl | 27 ++ .../default/hook/global/reason-descs-end.none.tmpl | 22 ++ 7 files changed, 663 insertions(+) create mode 100644 extensions/ComponentWatching/Config.pm create mode 100644 extensions/ComponentWatching/Extension.pm create mode 100755 extensions/ComponentWatching/reset-watch-preferences.pl create mode 100644 extensions/ComponentWatching/template/en/default/account/prefs/component_watch.html.tmpl create mode 100644 extensions/ComponentWatching/template/en/default/hook/account/prefs/email-relationships.html.tmpl create mode 100644 extensions/ComponentWatching/template/en/default/hook/account/prefs/prefs-tabs.html.tmpl create mode 100644 extensions/ComponentWatching/template/en/default/hook/global/reason-descs-end.none.tmpl (limited to 'extensions/ComponentWatching') diff --git a/extensions/ComponentWatching/Config.pm b/extensions/ComponentWatching/Config.pm new file mode 100644 index 000000000..534afaa61 --- /dev/null +++ b/extensions/ComponentWatching/Config.pm @@ -0,0 +1,26 @@ +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Component Watching Extension +# +# The Initial Developer of the Original Code is the Mozilla Foundation +# Portions created by the Initial Developers are Copyright (C) 2011 the +# Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Byron Jones + +package Bugzilla::Extension::ComponentWatching; +use strict; +use constant NAME => 'ComponentWatching'; + +__PACKAGE__->NAME; diff --git a/extensions/ComponentWatching/Extension.pm b/extensions/ComponentWatching/Extension.pm new file mode 100644 index 000000000..d73ce47cd --- /dev/null +++ b/extensions/ComponentWatching/Extension.pm @@ -0,0 +1,343 @@ +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Component Watching Extension +# +# The Initial Developer of the Original Code is the Mozilla Foundation +# Portions created by the Initial Developers are Copyright (C) 2011 the +# Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Byron Jones + +package Bugzilla::Extension::ComponentWatching; +use strict; +use base qw(Bugzilla::Extension); + +use Bugzilla::Constants; +use Bugzilla::Error; +use Bugzilla::Group; +use Bugzilla::User; +use Bugzilla::User::Setting; + +our $VERSION = '1.1'; + +use constant REL_COMPONENT_WATCHER => 15; + +# +# installation +# + +sub db_schema_abstract_schema { + my ($self, $args) = @_; + $args->{'schema'}->{'component_watch'} = { + FIELDS => [ + user_id => { + TYPE => 'INT3', + NOTNULL => 1, + REFERENCES => { + TABLE => 'profiles', + COLUMN => 'userid', + DELETE => 'CASCADE', + } + }, + component_id => { + TYPE => 'INT2', + NOTNULL => 0, + REFERENCES => { + TABLE => 'components', + COLUMN => 'id', + DELETE => 'CASCADE', + } + }, + product_id => { + TYPE => 'INT2', + NOTNULL => 0, + REFERENCES => { + TABLE => 'products', + COLUMN => 'id', + DELETE => 'CASCADE', + } + }, + ], + }; +} + +# +# templates +# + +sub template_before_create { + my ($self, $args) = @_; + my $config = $args->{config}; + my $constants = $config->{CONSTANTS}; + $constants->{REL_COMPONENT_WATCHER} = REL_COMPONENT_WATCHER; +} + +# +# preferences +# + +sub user_preferences { + my ($self, $args) = @_; + my $tab = $args->{'current_tab'}; + return unless $tab eq 'component_watch'; + + my $save = $args->{'save_changes'}; + my $handled = $args->{'handled'}; + my $user = Bugzilla->user; + + if ($save) { + my ($sth, $sthAdd, $sthDel); + + if (Bugzilla->input_params->{'add'}) { + # add watch + + my $productName = Bugzilla->input_params->{'add_product'}; + my $ra_componentNames = Bugzilla->input_params->{'add_component'}; + $ra_componentNames = [$ra_componentNames] unless ref($ra_componentNames); + + # load product and verify access + my $product = Bugzilla::Product->new({ name => $productName }); + unless ($product && $user->can_access_product($product)) { + ThrowUserError('product_access_denied', { product => $productName }); + } + + if (grep { $_ eq '' } @$ra_componentNames) { + # watching a product + _addProductWatch($user, $product); + + } else { + # watching specific components + foreach my $componentName (@$ra_componentNames) { + my $component = Bugzilla::Component->new({ name => $componentName, product => $product }); + unless ($component) { + ThrowUserError('product_access_denied', { product => $productName }); + } + _addComponentWatch($user, $component); + } + } + + _addDefaultSettings($user); + + } else { + # remove watch(s) + + foreach my $name (keys %{Bugzilla->input_params}) { + if ($name =~ /^del_(\d+)$/) { + _deleteProductWatch($user, $1); + } elsif ($name =~ /^del_(\d+)_(\d+)$/) { + _deleteComponentWatch($user, $1, $2); + } + } + } + } + + $args->{'vars'}->{'watches'} = _getWatches($user); + + $$handled = 1; +} + +# +# bugmail +# + +sub bugmail_recipients { + my ($self, $args) = @_; + my $bug = $args->{'bug'}; + my $recipients = $args->{'recipients'}; + my $diffs = $args->{'diffs'}; + + my ($oldProductId, $newProductId) = ($bug->product_id, $bug->product_id); + my ($oldComponentId, $newComponentId) = ($bug->component_id, $bug->component_id); + + # notify when the product/component is switch from one being watched + if (@$diffs) { + # we need the product to process the component, so scan for that first + my $product; + foreach my $ra (@$diffs) { + my (undef, undef, undef, undef, $old, $new, undef, $field) = @$ra; + if ($field eq 'product') { + $product = Bugzilla::Product->new({ name => $old }); + $oldProductId = $product->id; + } + } + if (!$product) { + $product = Bugzilla::Product->new($oldProductId); + } + foreach my $ra (@$diffs) { + my (undef, undef, undef, undef, $old, $new, undef, $field) = @$ra; + if ($field eq 'component') { + my $component = Bugzilla::Component->new({ name => $old, product => $product }); + $oldComponentId = $component->id; + } + } + } + + my $dbh = Bugzilla->dbh; + my $sth = $dbh->prepare(" + SELECT user_id + FROM component_watch + WHERE ((product_id = ? OR product_id = ?) AND component_id IS NULL) + OR (component_id = ? OR component_id = ?) + "); + $sth->execute($oldProductId, $newProductId, $oldComponentId, $newComponentId); + while (my ($uid) = $sth->fetchrow_array) { + if (!exists $recipients->{$uid}) { + $recipients->{$uid}->{+REL_COMPONENT_WATCHER} = Bugzilla::BugMail::BIT_WATCHING(); + } + } +} + +sub bugmail_relationships { + my ($self, $args) = @_; + my $relationships = $args->{relationships}; + $relationships->{+REL_COMPONENT_WATCHER} = 'Component-Watcher'; +} + +# +# db +# + +sub _getWatches { + my ($user) = @_; + my $dbh = Bugzilla->dbh; + + my $sth = $dbh->prepare(" + SELECT product_id, component_id + FROM component_watch + WHERE user_id = ? + "); + $sth->execute($user->id); + my @watches; + while (my ($productId, $componentId) = $sth->fetchrow_array) { + my $product = Bugzilla::Product->new($productId); + next unless $product && $user->can_access_product($product); + + my %watch = ( product => $product ); + if ($componentId) { + my $component = Bugzilla::Component->new($componentId); + next unless $component; + $watch{'component'} = $component; + } + + push @watches, \%watch; + } + + @watches = sort { + $a->{'product'}->name cmp $b->{'product'}->name + || $a->{'component'}->name cmp $b->{'component'}->name + } @watches; + + return \@watches; +} + +sub _addProductWatch { + my ($user, $product) = @_; + my $dbh = Bugzilla->dbh; + + my $sth = $dbh->prepare(" + SELECT 1 + FROM component_watch + WHERE user_id = ? AND product_id = ? AND component_id IS NULL + "); + $sth->execute($user->id, $product->id); + return if $sth->fetchrow_array; + + $sth = $dbh->prepare(" + DELETE FROM component_watch + WHERE user_id = ? AND product_id = ? + "); + $sth->execute($user->id, $product->id); + + $sth = $dbh->prepare(" + INSERT INTO component_watch(user_id, product_id) + VALUES (?, ?) + "); + $sth->execute($user->id, $product->id); +} + +sub _addComponentWatch { + my ($user, $component) = @_; + my $dbh = Bugzilla->dbh; + + my $sth = $dbh->prepare(" + SELECT 1 + FROM component_watch + WHERE user_id = ? + AND (component_id = ? OR (product_id = ? AND component_id IS NULL)) + "); + $sth->execute($user->id, $component->id, $component->product_id); + return if $sth->fetchrow_array; + + $sth = $dbh->prepare(" + INSERT INTO component_watch(user_id, product_id, component_id) + VALUES (?, ?, ?) + "); + $sth->execute($user->id, $component->product_id, $component->id); +} + +sub _deleteProductWatch { + my ($user, $productId) = @_; + my $dbh = Bugzilla->dbh; + + my $sth = $dbh->prepare(" + DELETE FROM component_watch + WHERE user_id = ? AND product_id = ? AND component_id IS NULL + "); + $sth->execute($user->id, $productId); +} + +sub _deleteComponentWatch { + my ($user, $productId, $componentId) = @_; + my $dbh = Bugzilla->dbh; + + my $sth = $dbh->prepare(" + DELETE FROM component_watch + WHERE user_id = ? AND product_id = ? AND component_id = ? + "); + $sth->execute($user->id, $productId, $componentId); +} + +sub _addDefaultSettings { + my ($user) = @_; + my $dbh = Bugzilla->dbh; + + my $sth = $dbh->prepare(" + SELECT 1 + FROM email_setting + WHERE user_id = ? AND relationship = ? + "); + $sth->execute($user->id, REL_COMPONENT_WATCHER); + return if $sth->fetchrow_array; + + my @defaultEvents = ( + EVT_OTHER, + EVT_COMMENT, + EVT_ATTACHMENT, + EVT_ATTACHMENT_DATA, + EVT_PROJ_MANAGEMENT, + EVT_OPENED_CLOSED, + EVT_KEYWORD, + EVT_DEPEND_BLOCK, + EVT_BUG_CREATED, + ); + foreach my $event (@defaultEvents) { + $dbh->do( + "INSERT INTO email_setting(user_id,relationship,event) VALUES (?,?,?)", + undef, + $user->id, REL_COMPONENT_WATCHER, $event + ); + } +} + +__PACKAGE__->NAME; diff --git a/extensions/ComponentWatching/reset-watch-preferences.pl b/extensions/ComponentWatching/reset-watch-preferences.pl new file mode 100755 index 000000000..f190ebd48 --- /dev/null +++ b/extensions/ComponentWatching/reset-watch-preferences.pl @@ -0,0 +1,75 @@ +#!/usr/bin/perl + +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Component Watching Extension +# +# The Initial Developer of the Original Code is the Mozilla Foundation +# Portions created by the Initial Developers are Copyright (C) 2011 the +# Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Byron Jones + +use strict; +use warnings; + +use lib '.'; +$| = 1; + +use Bugzilla; +use Bugzilla::Constants; +use Bugzilla::Install::Util qw(indicate_progress); + +Bugzilla->usage_mode(USAGE_MODE_CMDLINE); + +my @DEFAULT_EVENTS = qw(0 2 3 4 5 6 7 9 10 50); +my $REL_COMP_WATCH = 15; + +print "This script resets the component watching preferences back to\n"; +print "default values. It is required to be run when upgrading from\n"; +print "version 1.0 to 1.1\n"; +print "Press to start, or CTRL+C to cancel... "; +getc(); +print "\n"; + +my $dbh = Bugzilla->dbh; + +$dbh->bz_start_transaction(); + +my @users; +my $ra_user_ids = $dbh->selectcol_arrayref( + "SELECT DISTINCT user_id FROM component_watch" +); + +my $total = scalar @$ra_user_ids; +my $count = 0; +foreach my $user_id (@$ra_user_ids) { + indicate_progress({ current => $count++, total => $total }) if $total > 10; + $dbh->do( + "DELETE FROM email_setting WHERE user_id=? AND relationship=?", + undef, + $user_id, $REL_COMP_WATCH + ); + foreach my $event (@DEFAULT_EVENTS) { + $dbh->do( + "INSERT INTO email_setting(user_id,relationship,event) VALUES (?,?,?)", + undef, + $user_id, $REL_COMP_WATCH, $event + ); + } +} + +$dbh->bz_commit_transaction(); + +print "Done.\n"; diff --git a/extensions/ComponentWatching/template/en/default/account/prefs/component_watch.html.tmpl b/extensions/ComponentWatching/template/en/default/account/prefs/component_watch.html.tmpl new file mode 100644 index 000000000..c3247078a --- /dev/null +++ b/extensions/ComponentWatching/template/en/default/account/prefs/component_watch.html.tmpl @@ -0,0 +1,148 @@ +[%# + # The contents of this file are subject to the Mozilla Public + # License Version 1.1 (the "License"); you may not use this file + # except in compliance with the License. You may obtain a copy of + # the License at http://www.mozilla.org/MPL/ + # + # Software distributed under the License is distributed on an "AS + # IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + # implied. See the License for the specific language governing + # rights and limitations under the License. + # + # The Original Code is the Component Watching Extension + # + # The Initial Developer of the Original Code is the Mozilla Foundation + # Portions created by the Initial Developers are Copyright (C) 2011 the + # Initial Developer. All Rights Reserved. + # + # Contributor(s): + # Byron Jones + #%] + +[%# initialise product to component mapping #%] + +[% SET selectable_products = user.get_selectable_products %] + + + + + + +

+Select the components you want to watch. To watch all components in a product, +watch "__Any__". +

+ + + + + + + + + + + + + + +
Product + +
Component + +
 
+ +

+Use Email Preferences to filter which +notification emails you receive. +

+ +
+

+You are currently watching: +

+ +[% IF watches.size %] + + + + + + + +[% FOREACH watch IN watches %] + + [% IF (watch.component) %] + + + + [% ELSE %] + + + + [% END %] + +[% END %] +
 ProductComponent
[% watch.component.product.name FILTER html %] + + [% watch.component.name FILTER html %] + + [% watch.product.name FILTER html %] + + __Any__ + +
+ +

+Select the items you want to stop watching. +

+ +[% ELSE %] + +

+You are not watching any components. +

+ +[% END %] + diff --git a/extensions/ComponentWatching/template/en/default/hook/account/prefs/email-relationships.html.tmpl b/extensions/ComponentWatching/template/en/default/hook/account/prefs/email-relationships.html.tmpl new file mode 100644 index 000000000..cab637967 --- /dev/null +++ b/extensions/ComponentWatching/template/en/default/hook/account/prefs/email-relationships.html.tmpl @@ -0,0 +1,22 @@ +[%# The contents of this file are subject to the Mozilla Public + # License Version 1.1 (the "License"); you may not use this file + # except in compliance with the License. You may obtain a copy of + # the License at http://www.mozilla.org/MPL/ + # + # Software distributed under the License is distributed on an "AS + # IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + # implied. See the License for the specific language governing + # rights and limitations under the License. + # + # The Original Code is the Component Watching Extension + # + # The Initial Developer of the Original Code is the Mozilla Foundation + # Portions created by the Initial Developers are Copyright (C) 2011 the + # Initial Developer. All Rights Reserved. + # + # Contributor(s): + # Byron Jones + #%] + +[% relationships.push({ id = constants.REL_COMPONENT_WATCHER, description = "Component" }) %] +[% no_added_removed.push(constants.REL_COMPONENT_WATCHER) %] diff --git a/extensions/ComponentWatching/template/en/default/hook/account/prefs/prefs-tabs.html.tmpl b/extensions/ComponentWatching/template/en/default/hook/account/prefs/prefs-tabs.html.tmpl new file mode 100644 index 000000000..7a615a8ac --- /dev/null +++ b/extensions/ComponentWatching/template/en/default/hook/account/prefs/prefs-tabs.html.tmpl @@ -0,0 +1,27 @@ +[%# + # The contents of this file are subject to the Mozilla Public + # License Version 1.1 (the "License"); you may not use this file + # except in compliance with the License. You may obtain a copy of + # the License at http://www.mozilla.org/MPL/ + # + # Software distributed under the License is distributed on an "AS + # IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + # implied. See the License for the specific language governing + # rights and limitations under the License. + # + # The Original Code is the Component Watching Extension + # + # The Initial Developer of the Original Code is the Mozilla Foundation + # Portions created by the Initial Developers are Copyright (C) 2011 the + # Initial Developer. All Rights Reserved. + # + # Contributor(s): + # Byron Jones + #%] + +[% tabs = tabs.import([{ + name => "component_watch", + label => "Component Watching", + link => "userprefs.cgi?tab=component_watch", + saveable => 1 + }]) %] diff --git a/extensions/ComponentWatching/template/en/default/hook/global/reason-descs-end.none.tmpl b/extensions/ComponentWatching/template/en/default/hook/global/reason-descs-end.none.tmpl new file mode 100644 index 000000000..2621f85e4 --- /dev/null +++ b/extensions/ComponentWatching/template/en/default/hook/global/reason-descs-end.none.tmpl @@ -0,0 +1,22 @@ +[%# The contents of this file are subject to the Mozilla Public + # License Version 1.1 (the "License"); you may not use this file + # except in compliance with the License. You may obtain a copy of + # the License at http://www.mozilla.org/MPL/ + # + # Software distributed under the License is distributed on an "AS + # IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + # implied. See the License for the specific language governing + # rights and limitations under the License. + # + # The Original Code is the Component Watching Extension + # + # The Initial Developer of the Original Code is the Mozilla Foundation + # Portions created by the Initial Developers are Copyright (C) 2011 the + # Initial Developer. All Rights Reserved. + # + # Contributor(s): + # Byron Jones + #%] + +[% watch_reason_descs.${constants.REL_COMPONENT_WATCHER} = + "You are watching the component for the ${terms.bug}." %] -- cgit v1.2.3-24-g4f1b