[Rt-commit] rt branch, 4.4/user-info-portlet, created. rt-4.4.2-75-gc71626ee2

Craig Kaiser craig at bestpractical.com
Fri May 18 14:38:58 EDT 2018


The branch, 4.4/user-info-portlet has been created
        at  c71626ee2e559fc8223fdbe74cf473c8bc57f9bd (commit)

- Log -----------------------------------------------------------------
commit cf6d5abb0ea6cf960a4a2f1ffccf01d9a46fdf12
Author: craig Kaiser <craig at bestpractical.com>
Date:   Mon Apr 23 09:37:18 2018 -0400

    Add keyboard shortcuts for reply and comment
    
    If the user is on a ticket page where the action link for reply or
    comment is available, then they should be able to use the keyboard
    shortcuts 'r' and 'c'.

diff --git a/share/html/Elements/ShortcutHelp b/share/html/Elements/ShortcutHelp
index 41180a279..bf192561f 100644
--- a/share/html/Elements/ShortcutHelp
+++ b/share/html/Elements/ShortcutHelp
@@ -46,8 +46,10 @@
 %#
 %# END BPS TAGGED BLOCK }}}
 <%args>
-$show_search      => 0
-$show_bulk_update => 0
+$show_bulk_update     => 0
+$show_search          => 0
+$show_ticket_reply    => 0
+$show_ticket_comment  => 0
 </%args>
 
 <div class="keyboard-shortcuts">
@@ -115,4 +117,29 @@ $show_bulk_update => 0
 
 % }
 
+% if ($show_ticket_reply || $show_ticket_comment) {
+    <div class="titlebox">
+        <div class="titlebox-title">
+            <span class="left"><&|/l&>Ticket</&></span>
+            <span class="right-empty"></span>
+        </div>
+        <div class="titlebox-content">
+            <hr class="clear">
+            <table>
+%   if ( $show_ticket_reply ) {
+                <tr>
+                    <td class="key-column"><span class="keyboard-shortcuts-key">r</span></td>
+                    <td><&|/l&>Reply To Current Ticket</&></td>
+                </tr>
+%   }
+%   if ( $show_ticket_comment ) {
+                <tr>
+                    <td class="key-column"><span class="keyboard-shortcuts-key">c</span></td>
+                    <td><&|/l&>Comment On Current Ticket</&></td>
+                </tr>
+%   }
+            </table>
+        </div>
+    </div>
+% }
 </div>
diff --git a/share/html/Helpers/ShortcutHelp b/share/html/Helpers/ShortcutHelp
index b9e71fd7d..21cdacf51 100644
--- a/share/html/Helpers/ShortcutHelp
+++ b/share/html/Helpers/ShortcutHelp
@@ -46,8 +46,10 @@
 %#
 %# END BPS TAGGED BLOCK }}}
 <%args>
-$show_search      => 0
-$show_bulk_update => 0
+$show_search         => 0
+$show_bulk_update    => 0
+$show_ticket_reply   => 0
+$show_ticket_comment => 0
 </%args>
 <& /Elements/ShortcutHelp, %ARGS &>
 % $m->abort;
diff --git a/share/html/SelfService/Helpers/ShortcutHelp b/share/html/SelfService/Helpers/ShortcutHelp
index b9e71fd7d..ab0a77671 100644
--- a/share/html/SelfService/Helpers/ShortcutHelp
+++ b/share/html/SelfService/Helpers/ShortcutHelp
@@ -48,6 +48,8 @@
 <%args>
 $show_search      => 0
 $show_bulk_update => 0
+$show_ticket_reply => 0
+$show_ticket_comment => 0
 </%args>
 <& /Elements/ShortcutHelp, %ARGS &>
 % $m->abort;
diff --git a/share/static/js/keyboard-shortcuts.js b/share/static/js/keyboard-shortcuts.js
index a7b4cf54f..3faf69cd3 100644
--- a/share/static/js/keyboard-shortcuts.js
+++ b/share/static/js/keyboard-shortcuts.js
@@ -34,10 +34,14 @@ jQuery(function() {
 
         var is_search = jQuery('body#comp-Search-Results').length > 0;
         var is_bulk_update = jQuery('body#comp-Search-Bulk').length > 0;
+        var is_ticket_reply = jQuery('a#page-actions-reply').length > 0;
+        var is_ticket_comment = jQuery('a#page-actions-comment').length > 0;
 
         var url = RT.Config.WebHomePath + '/Helpers/ShortcutHelp' +
                   '?show_search=' + ( is_search || is_bulk_update ? '1' : '0' ) +
-                  '&show_bulk_update=' + ( is_bulk_update ? '1' : '0' );
+                  '&show_bulk_update=' + ( is_bulk_update ? '1' : '0' ) +
+                  '&show_ticket_reply=' + ( is_ticket_reply ? '1' : '0' ) +
+                  '&show_ticket_comment=' + ( is_ticket_comment ? '1' : '0' );
 
         jQuery.ajax({
             url: url,
@@ -151,3 +155,22 @@ jQuery(function() {
     Mousetrap.bind('x', toggleTicketCheckbox);
 });
 
+jQuery(function() {
+    // Only load these shortcuts if on ticket display page
+    var ticket_reply = jQuery('a#page-actions-reply');
+    var ticket_comment = jQuery('a#page-actions-comment');
+    if (!ticket_reply.length && !ticket_comment.length) return;
+
+    var replyToTicket = function() {
+        if (!ticket_reply.length) return;
+        window.location.href = ticket_reply.attr('href');
+    };
+
+    var commentOnTicket = function() {
+        if (!ticket_comment.length) return;
+        window.location.href = ticket_comment.attr('href');
+    };
+
+    Mousetrap.bind('r', replyToTicket);
+    Mousetrap.bind('c', commentOnTicket);
+});

commit c71626ee2e559fc8223fdbe74cf473c8bc57f9bd
Author: Craig Kaiser <craig at bestpractical.com>
Date:   Tue May 8 15:30:44 2018 -0400

    Allow user to handle their information for GDPR
    
    Add new portlet to the Admin/Users/Modify.html page to allow a user to
    download their information / tickets / transactions. As well as remove
    their information being stored in the database.

diff --git a/share/html/Admin/Users/Modify.html b/share/html/Admin/Users/Modify.html
index 6b65aff67..d39871a63 100644
--- a/share/html/Admin/Users/Modify.html
+++ b/share/html/Admin/Users/Modify.html
@@ -232,6 +232,8 @@
 % }
 </form>
 
+<& /Elements/UserRelatedInfo, UserId => $UserObj->Id, id => $id, Anonymize => $ARGS{Anonymize} &>
+
 <%INIT>
 
 my $UserObj = RT::User->new($session{'CurrentUser'});
@@ -308,6 +310,27 @@ if ( $UserObj->Id ) {
     }
 }
 
+if ( $ARGS{Anonymize} ) {
+    my @attrs = keys %{ $UserObj->_CoreAccessible };
+
+    # Remove identifying user information from record
+    foreach my $attr (@attrs) {
+        my $method = 'Set' . $attr;
+            $UserObj->$method('');
+    }
+    my $valid_email = 1;
+    my $number = '';
+    while ( $valid_email ) {
+        my @Chars = ('1'..'9');
+        for (1..9) {
+            $number .= $Chars[int rand @Chars];
+        }
+        $valid_email = !$UserObj->ValidateEmail('anon_' . $number . '@example.com');
+    }
+    $UserObj->SetName('anon_' . $number);
+    $UserObj->SetEmailAddress('anon_' . $number . '@example.com');
+}
+
 # This code does automatic redirection if any updates happen.
 MaybeRedirectForResults(
     Actions   => \@results,
diff --git a/share/html/Elements/RT__Transaction/ColumnMap b/share/html/Elements/RT__Transaction/ColumnMap
index 620befc2d..d295fdc5b 100644
--- a/share/html/Elements/RT__Transaction/ColumnMap
+++ b/share/html/Elements/RT__Transaction/ColumnMap
@@ -105,6 +105,11 @@ my $COLUMN_MAP = {
             }
         },
     },
+    Content => {
+        title       => 'Content', # loc
+        Attribute   => 'Content', # loc
+        value       => sub { return $_[0]->Content },
+    },
 };
 
 
diff --git a/share/html/Elements/Tabs b/share/html/Elements/Tabs
index 0b6742db0..8ebf4f73a 100644
--- a/share/html/Elements/Tabs
+++ b/share/html/Elements/Tabs
@@ -982,7 +982,7 @@ my $build_main_nav = sub {
 
             my $more = $current_search_menu->child( more => title => loc('Feeds') );
 
-            $more->child( spreadsheet => title => loc('Spreadsheet'), path => "/Search/Results.tsv$args" );
+            $more->child( spreadsheet => title => loc('Spreadsheet'), path => "/Helpers/TSVExport$args" );
 
             my %rss_data = map {
                 $_ => $QueryArgs->{$_} || $fallback_query_args{$_} || '' }
diff --git a/share/html/Elements/UserRelatedInfo b/share/html/Elements/UserRelatedInfo
new file mode 100644
index 000000000..e2a819d94
--- /dev/null
+++ b/share/html/Elements/UserRelatedInfo
@@ -0,0 +1,101 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2017 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 }}}
+
+% if ( $Anonymize ) {
+% $m->redirect('/test');
+test
+% }
+<div class="modal" id="user-info-modal">
+    <p>Are you sure you want to update <% $count %> attachment records to remove <% $user->EmailAddress ? $user->EmailAddress : $user_name %></p>
+    <a href="?id=<% $id %>&UserId=<% $id %>&Anonymize=1"><button>Ok</button></a>
+    <a href="#" rel="modal:close"><button>Cancel</button></a>
+</div>
+<&|/Widgets/TitleBox,
+    class => 'user-related-info',
+    title => loc("User related info"),
+&>
+
+<div class="row">
+    <div class="col-md-12">
+        <a href="/Helpers/TSVExport?Type=User&UserId=<% $UserId %>"><button>Download My User Info</button></a>
+        <a href="/Helpers/TSVExport?<% $query_tickets %>"><button>Download My Tickets</button></a>
+        <a href="/Helpers/TSVExport?Type=Transaction&UserId=<% $UserId %>"><button>Download My Transaction Info</button></a>
+
+        <a href="#user-info-modal" rel="modal:open"><button>Anonymize User</button></a>
+        <a href="/Admin/Tools/Shredder/index.html?Plugin=Users&Objects%3Alimit=&Objects%3AACE=&Objects%3AAttachment=&Objects%3ACachedGroupMember=&Objects%3ACustomField=&Objects%3ACustomFieldValue=&Objects%3AGroupMember=&Objects%3AGroup=&Objects%3ALink=&Objects%3APrincipal=&Objects%3AQueue=&Objects%3AScrip=&Objects%3AScripAction=&Objects%3AScripCondition=&Objects%3ATemplate=&Objects%3AObjectCustomFieldValue=&Objects%3ATicket=&Objects%3ATransaction=&Objects%3AUser=&Attachments%3Alimit=&Attachments%3Afiles_only=&Attachments%3Afile=&Attachments%3Alonger=&Users%3Alimit=&Users%3Astatus=Enabled&Users%3Aname=<% $user_name %>&Users%3Aemail=&Users%3Amember_of=&Users%3Anot_member_of=&Users%3Areplace_relations=<% $user_name %>&Users%3Ano_tickets=&Users%3Ano_ticket_transactions=&Tickets%3Alimit=&Tickets%3Aquery=&Tickets%3Awith_linked=&Tickets%3Aapply_query_to_linked=&Search=Search"><button>Replace User Information</button></a>
+        <a href="/Admin/Tools/Shredder/index.html?Plugin=Users&Objects%3Alimit=&Objects%3AACE=&Objects%3AAttachment=&Objects%3ACachedGroupMember=&Objects%3ACustomField=&Objects%3ACustomFieldValue=&Objects%3AGroupMember=&Objects%3AGroup=&Objects%3ALink=&Objects%3APrincipal=&Objects%3AQueue=&Objects%3AScrip=&Objects%3AScripAction=&Objects%3AScripCondition=&Objects%3ATemplate=&Objects%3AObjectCustomFieldValue=&Objects%3ATicket=&Objects%3ATransaction=&Objects%3AUser=&Users%3Alimit=&Users%3Astatus=enabled&Users%3Aname=<% $user_name %>&Users%3Aemail=&Users%3Amember_of=&Users%3Anot_member_of=&Users%3Areplace_relations=&Users%3Ano_tickets=&Users%3Ano_ticket_transactions=&Attachments%3Alimit=&Attachments%3Afiles_only=&Attachments%3Afile=&Attachments%3Alonger=&Tickets%3Alimit=&Tickets%3Aquery=&Tickets%3Awith_linked=&Tickets%3Aapply_query_to_linked=&Search=Search"><button>Remove User Information</button></a>
+    </div>
+</div>
+</&>
+<%init>
+my $user_name;
+my $user = RT::User->new($session{'CurrentUser'});
+if ( $UserId ) {
+    $user->Load( $UserId );
+} else {
+    $user->Load($session{'CurrentUser'});
+}
+
+$user_name = $user->Name;
+
+# Build Ticket Query
+my $query_tickets = "Query=Requestor.id = " . $user->Id;
+
+# Number of records that will be Anonymized
+my $count = 0;
+foreach my $method ( qw(Creator LastUpdatedBy) ) {
+    next unless $user->_Accessible( $method => 'read' );
+    $count += 1;
+}
+</%init>
+
+<%ARGS>
+$Anonymize => undef
+$UserId    => undef
+$id        => undef
+</%ARGS>
diff --git a/share/html/Elements/RT__Transaction/ColumnMap b/share/html/Helpers/Shredder
similarity index 50%
copy from share/html/Elements/RT__Transaction/ColumnMap
copy to share/html/Helpers/Shredder
index 620befc2d..255c67d15 100644
--- a/share/html/Elements/RT__Transaction/ColumnMap
+++ b/share/html/Helpers/Shredder
@@ -45,71 +45,10 @@
 %# those contributions and any derivatives thereof.
 %#
 %# END BPS TAGGED BLOCK }}}
+<form id="shredder-search-form" action="<% RT->Config->Get('WebPath') %>/Admin/Tools/Shredder/" method="GET">
+<div id="shredder-select-plugin">
+<div id="shredder-submit-button" class="Users">
 <%ARGS>
-$Name
-$Attr => undef
 </%ARGS>
-<%ONCE>
-my $COLUMN_MAP = {
-    ObjectType => {
-        title     => 'Object Type', # loc
-        attribute => 'ObjectType',
-        value     => sub { return $_[0]->ObjectType() },
-    },
-    ObjectId => {
-        title     => 'Object Id', # loc
-        attribute => 'ObjectId',
-        value     => sub { return $_[0]->ObjectId() },
-    },
-    Type => {
-        title     => 'Type', # loc
-        attribute => 'Type',
-        value     => sub { return $_[0]->Type() },
-    },
-    Field => {
-        title     => 'Field', # loc
-        attribute => 'Field',
-        value     => sub { return $_[0]->Field() },
-    },
-    OldValue => {
-        title     => 'Old Value', # loc
-        attribute => 'OldValue',
-        value     => sub { return $_[0]->OldValue() },
-    },
-    NewValue => {
-        title     => 'New Value', # loc
-        attribute => 'NewValue',
-        value     => sub { return $_[0]->NewValue() },
-    },
-    TimeTaken => {
-        title     => 'Time Taken', # loc
-        attribute => 'TimeTaken',
-        value     => sub { return $_[0]->TimeTaken() },
-    },
-    Description => {
-        title     => 'Description', # loc
-        value     => sub { my $html = $_[0]->BriefDescriptionAsHTML(); return \$html },
-    },
-    ObjectName => {
-        title     => 'Name', # loc
-        value     => sub {
-            # Since ->can() is unreliable (due to AUTOLOAD), use
-            # _Accessible to check for methods
-            my $object = $_[0]->Object;
-            if ( $object->_Accessible('Name','read') ) {
-                return $object->Name;
-            } elsif ( $object->_Accessible('Subject','read') ) {
-                return $object->Subject;
-            } else {
-                return loc('No Name');
-            }
-        },
-    },
-};
-
-
-</%ONCE>
 <%INIT>
-$m->callback( COLUMN_MAP => $COLUMN_MAP, CallbackName => 'ColumnMap', CallbackOnce => 1 );
-return GetColumnMapEntry( Map => $COLUMN_MAP, Name => $Name, Attribute => $Attr );
-</%INIT>
+</%INIT>
\ No newline at end of file
diff --git a/share/html/Helpers/TSVExport b/share/html/Helpers/TSVExport
new file mode 100644
index 000000000..6b488b478
--- /dev/null
+++ b/share/html/Helpers/TSVExport
@@ -0,0 +1,107 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2017 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>
+$Format           => undef
+$Query            => ''
+$OrderBy          => 'id'
+$Order            => 'ASC'
+$PreserveNewLines => 0
+$Type             => undef
+$UserId           => undef
+</%ARGS>
+
+<%INIT>
+my $Collection;
+
+if ( !$Type ) {
+    $Collection = RT::Tickets->new( $session{'CurrentUser'} );
+    $Format = RT->Config->Get('UserDataSearchResultFormat') || RT->Config->Get('DefaultSearchResultFormat');
+    $Collection->FromSQL( $Query );
+    if ( $OrderBy =~ /\|/ ) {
+        # Multiple Sorts
+        my @OrderBy = split /\|/, $OrderBy;
+        my @Order   = split /\|/, $Order;
+        $Collection->OrderByCols(
+            map { { FIELD => $OrderBy[$_], ORDER => $Order[$_] } }
+            ( 0 .. $#OrderBy )
+        );
+    }
+    else {
+        $Collection->OrderBy( FIELD => $OrderBy, ORDER => $Order );
+    }
+} elsif ( $Type eq 'User' ) {
+    $Format = RT->Config->Get('UserDataSearchResultFormat') || "'__id__', '__Name__', '__EmailAddress__', '__RealName__', '__NickName__', '__Organization__', '__HomePhone__', '__WorkPhone__','__MobilePhone__', '__PagerPhone__', '__Address1__', '__Address2__', '__City__', '__State__','__Zip__', '__Country__', '__Gecos__', '__Lang__', '__FreeFormContactInfo__'";
+
+    my $user = RT::User->new($session{'CurrentUser'});
+    if ( $UserId ) {
+        $user->Load( $UserId );
+    } else {
+        $user->Load($session{'CurrentUser'});
+    }
+
+    $Collection = RT::Users->new($session{'CurrentUser'});
+    $Collection->Limit( FIELD => 'id', VALUE => $user->Id );
+
+} elsif ( $Type eq 'Transaction' ) {
+    $Format = RT->Config->Get('UserDataSearchResultFormat') || "'__ObjectId__', '__id__', '__Created__', '__Description__', '__OldValue__', '__NewValue__', '__Content__'";
+
+    my $user = RT::User->new($session{'CurrentUser'});
+    if ( $UserId ) {
+        $user->Load( $UserId );
+    } else {
+        $user->Load($session{'CurrentUser'});
+    }
+
+    $Collection = RT::Transactions->new($session{'CurrentUser'});
+    $Collection->Limit( FIELD => 'Creator', VALUE => $user->Id );
+    $Collection->Limit( FIELD => 'Type', VALUE => 'Correspondence' );
+    $Collection->Limit( FIELD => 'Type', VALUE => 'Comment' );
+}
+
+$m->comp( "/Elements/TSVExport", Collection => $Collection, Format => $Format, PreserveNewLines => $PreserveNewLines );
+</%INIT>

-----------------------------------------------------------------------


More information about the rt-commit mailing list