[Rt-commit] rt branch, 4.4/dashboard-subscription-user-group, created. rt-4.4.0rc2-16-g44f4599
Dustin Graves
dustin at bestpractical.com
Fri Dec 4 13:00:54 EST 2015
The branch, 4.4/dashboard-subscription-user-group has been created
at 44f4599a1f2a32222dd9821454f139618d828f59 (commit)
- Log -----------------------------------------------------------------
commit 44f4599a1f2a32222dd9821454f139618d828f59
Author: Dustin Graves <dustin at bestpractical.com>
Date: Tue Nov 17 22:45:06 2015 +0000
change dashboard subscription recipient input to robust user/group search
Fixes:T#158520
diff --git a/etc/upgrade/4.4.1/content b/etc/upgrade/4.4.1/content
new file mode 100644
index 0000000..62cb087
--- /dev/null
+++ b/etc/upgrade/4.4.1/content
@@ -0,0 +1,54 @@
+use strict;
+use warnings;
+
+use List::MoreUtils 'uniq';
+
+our @Initial = (
+ # migrate old Recipient field to new Recipients format
+ sub {
+ $RT::Logger->debug("Going to migrate dashboard subscription recipients");
+
+ my $attrs = RT::Attributes->new( RT->SystemUser );
+ $attrs->Limit( FIELD => 'ObjectType', VALUE => 'RT::User' );
+ $attrs->Limit( FIELD => 'Name', VALUE => 'Subscription' );
+
+ while ( my $attr = $attrs->Next ) {
+ my %fields = ( Recipients => { Users => [], Groups => [] } );
+
+ my $recipient = $attr->SubValue('Recipient');
+ my @users;
+
+ if ($recipient) {
+ for ( RT::EmailParser->ParseEmailAddress($recipient) ) {
+ my $addr = $_->address;
+
+ my $user = RT::User->new(RT->SystemUser);
+ $user->LoadByEmail($addr);
+
+ unless ($user->id) { # we need to create user
+ my $principal = RT::System->CanonicalizePrincipal(User => $addr);
+ $user->Load($principal->Object->id);
+ }
+
+ push @users, $user->id;
+ }
+ } else { # blank recipient represents dashboard creator subscription
+ push @users, $attr->ObjectId;
+ }
+
+ @{ $fields{Recipients}->{Users} } = uniq @users;
+
+ my ($ok, $msg) = $attr->SetSubValues(%fields);
+ unless ($ok) {
+ $RT::Logger->error("Couldn't update subscription: $msg");
+ $RT::Logger->error("Aborting dashboard subscription recipient migration");
+ exit;
+ }
+
+ ($ok, $msg) = $attr->DeleteSubValue('Recipient');
+ $RT::Logger->error("Couldn't delete Recipient field from subscription: $msg") unless $ok;
+ }
+ return 1;
+ },
+);
+
diff --git a/lib/RT/Dashboard/Mailer.pm b/lib/RT/Dashboard/Mailer.pm
index 276e93f..1786c06 100644
--- a/lib/RT/Dashboard/Mailer.pm
+++ b/lib/RT/Dashboard/Mailer.pm
@@ -61,6 +61,7 @@ use RT::Interface::Web;
use File::Temp 'tempdir';
use HTML::Scrubber;
use URI::QueryParam;
+use List::MoreUtils 'uniq';
sub MailDashboards {
my $self = shift;
@@ -103,22 +104,53 @@ sub MailDashboards {
LocalTime => [$hour, $dow, $dom],
);
- my $email = $subscription->SubValue('Recipient')
- || $user->EmailAddress;
-
- eval {
- $self->SendDashboard(
- %args,
- CurrentUser => $currentuser,
- Email => $email,
- Subscription => $subscription,
- From => $from,
- )
- };
- if ( $@ ) {
- $RT::Logger->error("Caught exception: $@");
+ my $recipients = $subscription->SubValue('Recipients');
+ my $recipients_users = $recipients->{Users};
+ my $recipients_groups = $recipients->{Groups};
+
+ my @emails;
+
+ # add users' emails to email list
+ for my $user_id (@{ $recipients_users || [] }) {
+ my $user = RT::User->new(RT->SystemUser);
+ $user->Load($user_id);
+ next unless $user->id;
+
+ push @emails, $user->EmailAddress;
+ }
+
+ # add emails for every group's members
+ for my $group_id (@{ $recipients_groups || [] }) {
+ my $group = RT::Group->new(RT->SystemUser);
+ $group->Load($group_id);
+ next unless $group->id;
+
+ my $users = $group->UserMembersObj;
+ while (my $user = $users->Next) {
+ push @emails, $user->EmailAddress;
+ }
+ }
+
+ my $email_success = 0;
+ for my $email (uniq @emails) {
+ eval {
+ $self->SendDashboard(
+ %args,
+ CurrentUser => $currentuser,
+ Email => $email,
+ Subscription => $subscription,
+ From => $from,
+ )
+ };
+ if ( $@ ) {
+ $RT::Logger->error("Caught exception: $@");
+ }
+ else {
+ $email_success = 1;
+ }
}
- else {
+
+ if ($email_success) {
my $counter = $subscription->SubValue('Counter') || 0;
$subscription->SetSubValues(Counter => $counter + 1)
unless $args{DryRun};
diff --git a/share/html/Dashboards/Elements/SubscriptionRecipients b/share/html/Dashboards/Elements/SubscriptionRecipients
new file mode 100644
index 0000000..309fb12
--- /dev/null
+++ b/share/html/Dashboards/Elements/SubscriptionRecipients
@@ -0,0 +1,212 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
+%# <sales at bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+%#
+%#
+%# CONTRIBUTION SUBMISSION POLICY:
+%#
+%# (The following paragraph is not intended to limit the rights granted
+%# to you to modify and distribute this software under the terms of
+%# the GNU General Public License and is only of importance to you if
+%# you choose to contribute your changes and enhancements to the
+%# community by submitting them to Best Practical Solutions, LLC.)
+%#
+%# By intentionally submitting any modifications, corrections or
+%# derivatives to this work, or any other work intended for use with
+%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+%# you are the copyright holder for those contributions and you grant
+%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+%# royalty-free, perpetual, license to use, copy, create derivative
+%# works based on those contributions, and sublicense and distribute
+%# those contributions and any derivatives thereof.
+%#
+%# END BPS TAGGED BLOCK }}}
+<%ARGS>
+$UserField => 'Name'
+$UserOp => '='
+$UserString => undef
+$PrivilegedOnly => undef
+
+$GroupField => 'Name'
+$GroupOp => '='
+$GroupString => undef
+
+$Recipients => undef
+$IsFirstSubscription => undef
+</%ARGS>
+
+<%INIT>
+my ($recipients_users, $recipients_groups);
+my ($Users, $Groups);
+
+if ($Recipients) {
+ $recipients_users = $Recipients->{Users};
+ $recipients_groups = $Recipients->{Groups};
+}
+
+if ($UserString) {
+ $Users = RT::Users->new($session{'CurrentUser'});
+ $Users->Limit(FIELD => $UserField, VALUE => $UserString, OPERATOR => $UserOp, CASESENSITIVE => 0);
+ $Users->LimitToPrivileged if $PrivilegedOnly;
+
+ my $excluded_users = [ $session{'CurrentUser'}->id ];
+ push @$excluded_users, @{ $recipients_users || [] };
+ $Users->Limit(FIELD => 'id', VALUE => $excluded_users, OPERATOR => 'NOT IN');
+}
+
+if ($GroupString) {
+ $Groups = RT::Groups->new($session{'CurrentUser'});
+ $Groups->LimitToUserDefinedGroups;
+ $Groups->Limit(FIELD => $GroupField, VALUE => $GroupString, OPERATOR => $GroupOp, CASESENSITIVE => 0);
+ $Groups->Limit(FIELD => 'id', VALUE => $recipients_groups, OPERATOR => 'NOT IN') if @{ $recipients_groups || [] };
+}
+</%INIT>
+
+<table>
+<tr>
+<td>
+
+<table width="100%">
+<tr>
+<td valign="top">
+<&|/l&>Find people whose</&><br />
+% if ($UserString) {
+<& /Elements/SelectUsers, UserString => $UserString, UserField => $UserField, UserOp => $UserOp &>
+% } else {
+<& /Elements/SelectUsers &>
+% }
+<input type="submit" class="button" name="OnlySearchForPeople" value="<&|/l&>Go!</&>" />
+
+<br />
+
+<&|/l&>Find groups whose</&><br />
+% if ($GroupString) {
+<& /Elements/SelectGroups, GroupString => $GroupString, GroupField => $GroupField, GroupOp => $GroupOp &>
+% } else {
+<& /Elements/SelectGroups &>
+% }
+<input type="submit" class="button" name="OnlySearchForGroup" value="<&|/l&>Go!</&>" />
+</td>
+</tr>
+</table>
+
+% if ($UserString || $GroupString) {
+<strong><&|/l&>Search Results</&>:</strong>
+<ul>
+
+% if ($Users) {
+% while (my $u = $Users->Next ) {
+<li>
+<input type="checkbox" class="checkbox" name="Dashboard-Subscription-Users-<%$u->id%>" value="1" />
+<input type="hidden" name="Dashboard-Subscription-Users-<%$u->id%>" value="0" />
+<& '/Elements/ShowUser', User => $u, style=>'verbose' &>
+% }
+% }
+
+% if ($Groups) {
+% while (my $g = $Groups->Next ) {
+<li>
+<input type="checkbox" class="checkbox" name="Dashboard-Subscription-Groups-<%$g->id%>" value="1" />
+<input type="hidden" name="Dashboard-Subscription-Groups-<%$g->id%>" value="0" />
+<%$g->Name%> (<%$g->Description%>)
+</li>
+% }
+% }
+
+% unless (($Users && $Users->Count) || ($Groups && $Groups->Count)) {
+<li><i><&|/l&>none</&></i></li>
+% }
+
+</ul>
+% }
+
+</td>
+<td class="current-recipients">
+
+<strong><&|/l&>Current Recipients</&>:</strong>
+<ul>
+
+% my $current_user_id = $session{CurrentUser}->id;
+% my $current_user_subscribed = $recipients_users && grep { $_ == $current_user_id } @$recipients_users;
+<li>
+<input type="checkbox" class="checkbox" name="Dashboard-Subscription-Users-<%$current_user_id%>" value="1" <% $current_user_subscribed || $IsFirstSubscription ? 'checked' : '' %> />
+<input type="hidden" name="Dashboard-Subscription-Users-<%$current_user_id%>" value="0" />
+% if ( $session{CurrentUser}->HasRight( Right => 'AdminUsers', Object => $RT::System ) &&
+% $session{CurrentUser}->HasRight( Right => 'ShowConfigTab', Object =>$RT::System ) ) {
+<a href="<% RT->Config->Get('WebPath') %>/Admin/Users/Modify.html?id=<% $current_user_id %>">
+<& /Elements/ShowUser, User => $session{CurrentUser} &></a>
+% } else {
+<& /Elements/ShowUser, User => $session{CurrentUser} &>
+% }
+<& /Elements/ShowUserEmailFrequency, User => $session{CurrentUser} &>
+</li>
+
+% for my $user_id (@{ $recipients_users || [] }) {
+% next if $user_id == $session{'CurrentUser'}->id; # already listed current user
+% my $user = RT::User->new( $session{'CurrentUser'} );
+% $user->Load($user_id);
+% next unless $user->id;
+<li>
+<input type="checkbox" class="checkbox" name="Dashboard-Subscription-Users-<%$user_id%>" value="1" checked />
+<input type="hidden" name="Dashboard-Subscription-Users-<%$user_id%>" value="0" />
+% if ( $session{CurrentUser}->HasRight( Right => 'AdminUsers', Object => $RT::System ) &&
+% $session{CurrentUser}->HasRight( Right => 'ShowConfigTab', Object =>$RT::System ) ) {
+<a href="<% RT->Config->Get('WebPath') %>/Admin/Users/Modify.html?id=<% $user_id %>">
+<& /Elements/ShowUser, User => $user &></a>
+% } else {
+<& /Elements/ShowUser, User => $user &>
+% }
+<& /Elements/ShowUserEmailFrequency, User => $user &>
+</li>
+% }
+
+% for my $group_id (@{ $recipients_groups || [] }) {
+% my $group = RT::Group->new( $session{'CurrentUser'} );
+% $group->Load($group_id);
+% next unless $group->id;
+<li>
+<input type="checkbox" class="checkbox" name="Dashboard-Subscription-Groups-<%$group_id%>" value="1" checked />
+<input type="hidden" name="Dashboard-Subscription-Groups-<%$group_id%>" value="0" />
+
+
+% if ( $session{CurrentUser}->HasRight( Right => 'AdminGroup', Object => $RT::System ) &&
+% $session{CurrentUser}->HasRight( Right => 'ShowConfigTab', Object =>$RT::System ) ) {
+<a href="<% RT->Config->Get('WebPath') %>/Admin/Groups/Modify.html?id=<% $group_id %>">
+<% $group->Name %>
+</a>
+% } else {
+<% $group->Name %>
+% }
+</li>
+% }
+
+</ul>
+
+</td>
+</tr>
+</table>
+
diff --git a/share/html/Dashboards/Subscription.html b/share/html/Dashboards/Subscription.html
index d55076d..72f1119 100644
--- a/share/html/Dashboards/Subscription.html
+++ b/share/html/Dashboards/Subscription.html
@@ -173,15 +173,17 @@
% }
</select>
</td></tr>
-
-<tr><td class="label">
-<&|/l&>Recipient</&>:
-</td><td class="value">
-<input name="Recipient" id="Recipient" size="30" value="<%$fields{Recipient} ? $fields{Recipient} : ''%>" />
-<div class="hints"><% loc("Leave blank to send to your current email address ([_1])", $session{'CurrentUser'}->EmailAddress) %></div>
-</td></tr>
</table>
</&>
+
+<&| /Widgets/TitleBox, title => loc('Recipients') &>
+<& Elements/SubscriptionRecipients,
+ UserField => $UserField, UserString => $UserString, UserOp => $UserOp,
+ GroupString => $GroupString, GroupOp => $GroupOp, GroupField => $GroupField,
+ Recipients => $fields{Recipients},
+ IsFirstSubscription => $SubscriptionObj ? 0 : 1 &>
+</&>
+
</td>
</tr>
</table>
@@ -222,7 +224,7 @@ my %fields = (
Dow => 'Monday',
Dom => 1,
Rows => 20,
- Recipient => '',
+ Recipients => { Users => [], Groups => [] },
Fow => 1,
Counter => 0,
);
@@ -241,53 +243,84 @@ for my $field (keys %fields) {
if defined($ARGS{$field}) || $ARGS{$field.'-Magic'};
}
+# update recipients
+for my $key (keys %ARGS) {
+ next unless $key =~ /^Dashboard-Subscription-(Users|Groups)-(\d+)$/;
+
+ my ($mode, $type, $id) = ('', $1, $2);
+
+ # find out proper value for user/group checkbox
+ my $add_keep_recipient = ref $ARGS{$key} eq 'ARRAY' ?
+ grep { $_ } @{ $ARGS{$key} } :
+ $ARGS{$key};
+
+ my $record; # hold user/group object
+ if ($type eq 'Users') {
+ my $user = RT::User->new($session{CurrentUser});
+ $user->Load( $id );
+ $record = $user;
+ } elsif ($type eq 'Groups') {
+ my $group = RT::Group->new($session{CurrentUser});
+ $group->Load( $id );
+ $record = $group;
+ }
-# this'll be defined on submit
-if (defined $ARGS{Save}) {
- my $ok = 1;
-
- # validation
- if ($fields{Recipient}) {
- my @addresses = Email::Address->parse($fields{Recipient});
- if (@addresses == 0) {
- push @results, loc('Recipient must be an email address');
- $ok = 0;
- }
+ my @recipients = @{ $fields{Recipients}->{$type} };
+ my $is_prev_recipient = grep { $_ == $id } @recipients;
+
+ if ($add_keep_recipient and not $is_prev_recipient) { # Add User/Group
+ push @recipients, $id;
+ push @results, loc("[_1] added to dashboard subscription recipients.", $record->Name);
+ } elsif (not $add_keep_recipient and $is_prev_recipient) { # Remove User/Group
+ @recipients = grep { $_ != $id } @recipients;
+ push @results, loc("[_1] removed from dashboard subscription recipients.", $record->Name);
}
- if ($ok) {
- # update
- if ($SubscriptionObj) {
- $id = delete $fields{'DashboardId'}; # immutable
- ($ok, $msg) = $SubscriptionObj->SetSubValues(%fields);
- $fields{'DashboardId'} = $id;
+ use List::MoreUtils 'uniq';
+ @{ $fields{Recipients}->{$type} } = uniq @recipients;
+}
- $msg = loc("Subscription updated") if $ok;
- push @results, $msg;
+# this'll be defined on submit
+if (defined $ARGS{Save}) {
+ # update
+ if ($SubscriptionObj) {
+ $id = delete $fields{'DashboardId'}; # immutable
+ ($ok, $msg) = $SubscriptionObj->SetSubValues(%fields);
+ $fields{'DashboardId'} = $id;
+
+ $msg = loc("Subscription updated") if $ok;
+ push @results, $msg;
+ }
+ # create
+ else {
+ Abort(loc("Unable to subscribe to dashboard [_1]: Permission Denied", $id))
+ unless $Dashboard->CurrentUserCanSubscribe;
+
+ $SubscriptionObj = RT::Attribute->new($session{CurrentUser});
+ ($ok, $msg) = $SubscriptionObj->Create(
+ Name => 'Subscription',
+ Description => 'Subscription to dashboard ' . $id,
+ ContentType => 'storable',
+ Object => $session{'CurrentUser'}->UserObj,
+ Content => \%fields,
+ );
+ if ($ok) {
+ push @results, loc("Subscribed to dashboard [_1]", $Dashboard->Name);
}
- # create
else {
- Abort(loc("Unable to subscribe to dashboard [_1]: Permission Denied", $id))
- unless $Dashboard->CurrentUserCanSubscribe;
-
- $SubscriptionObj = RT::Attribute->new($session{CurrentUser});
- ($ok, $msg) = $SubscriptionObj->Create(
- Name => 'Subscription',
- Description => 'Subscription to dashboard ' . $id,
- ContentType => 'storable',
- Object => $session{'CurrentUser'}->UserObj,
- Content => \%fields,
- );
- if ($ok) {
- push @results, loc("Subscribed to dashboard [_1]", $Dashboard->Name);
- push @results, loc("Warning: you have no email address set, so you will not receive this dashboard until you have it set")
- unless $session{'CurrentUser'}->EmailAddress || $fields{Recipient};
- }
- else {
- push @results, loc('Subscription could not be created: [_1]', $msg);
- }
+ push @results, loc('Subscription could not be created: [_1]', $msg);
}
}
+ push @results, loc("Warning: This dashboard has no recipients")
+ unless @{ $fields{Recipients}->{Users} } || @{ $fields{Recipients}->{Groups} };
+} elsif (defined $ARGS{OnlySearchForPeople}) {
+ $GroupString = undef;
+ $GroupField = undef;
+ $GroupOp = undef;
+} elsif (defined $ARGS{OnlySearchForGroup}) {
+ $UserString = undef;
+ $UserField = undef;
+ $UserOp = undef;
}
if ($SubscriptionObj) {
@@ -305,6 +338,12 @@ $Hour => undef
$Dow => undef
$Dom => undef
$Rows => undef
-$Recipient => undef
+
+$UserField => undef
+$UserOp => undef
+$UserString => undef
+$GroupField => undef
+$GroupOp => undef
+$GroupString => undef
</%ARGS>
diff --git a/share/static/css/base/misc.css b/share/static/css/base/misc.css
index 3d0fb0e..3d1612d 100644
--- a/share/static/css/base/misc.css
+++ b/share/static/css/base/misc.css
@@ -119,3 +119,9 @@ div.cke {
text-decoration: underline !important;
color: white !important;
}
+
+/* dashboard subscription */
+
+td.current-recipients {
+ vertical-align: top;
+}
-----------------------------------------------------------------------
More information about the rt-commit
mailing list