[freeside-commits] branch master updated. dd82a27357402466390044d001824657f6617626

Mark Wells mark at 420.am
Thu May 26 10:04:52 PDT 2016


The branch, master has been updated
       via  dd82a27357402466390044d001824657f6617626 (commit)
      from  a6d8b1c76fa2c592e3d6e0c19659c0ba6fc464b0 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit dd82a27357402466390044d001824657f6617626
Author: Mark Wells <mark at freeside.biz>
Date:   Wed May 25 16:29:05 2016 -0700

    indicator on the top bar for new activity on tickets, #41670

diff --git a/httemplate/elements/header-full.html b/httemplate/elements/header-full.html
index 699f82c..db38eaf 100644
--- a/httemplate/elements/header-full.html
+++ b/httemplate/elements/header-full.html
@@ -67,6 +67,9 @@ Example:
         <td align=left BGCOLOR="#ffffff"> <!-- valign="top" -->
           <font size=6><% $company_name || 'ExampleCo' %></font>
         </td>
+        <td align="right" BGCOLOR="#ffffff">
+          <& notify-tickets.html &>
+        </td>
         <td align=right valign=top BGCOLOR="#ffffff"><FONT SIZE="-1">Logged in as <b><% $FS::CurrentUser::CurrentUser->username |h %> </b> <FONT SIZE="-2"><a href="<%$fsurl%>loginout/logout.html">logout</a></FONT><br></FONT><FONT SIZE="-2"><a href="<%$fsurl%>pref/pref.html" STYLE="color: #000000">Preferences</a>
 %         if ( $conf->config("ticket_system")
 %              && FS::TicketSystem->access_right(\%session, 'ModifySelf') ) {
diff --git a/httemplate/elements/notify-tickets.html b/httemplate/elements/notify-tickets.html
new file mode 100644
index 0000000..f7db52e
--- /dev/null
+++ b/httemplate/elements/notify-tickets.html
@@ -0,0 +1,37 @@
+% if ($enabled) {
+<style>
+.dot {
+  border-radius: 50%;
+  border: 1px solid black;
+  width: 1ex;
+  height: 1ex;
+  display: inline-block;
+}
+</style>
+<div style="font-weight: bold; vertical-align: bottom; text-align: left">
+%   if ( $UnrepliedTickets->Count > 0 ) {
+  <a href="<% $fsurl %>rt/Search/UnrepliedTickets.html">
+    <div class="dot" style="background-color: green"></div>
+    <% emt('New activity on [quant,_1,ticket]', $UnrepliedTickets->Count) %>
+  </a>
+%   } else {
+  <% emt('No new activity on tickets') %>
+%   }
+</div>
+% }
+<%init>
+use Class::Load 'load_class';
+
+my $enabled = $FS::TicketSystem::system eq 'RT_Internal';
+my $UnrepliedTickets;
+if ($enabled) {
+  my $class = 'RT::Search::UnrepliedTickets';
+  load_class($class);
+  my $session = FS::TicketSystem->session;
+  my $CurrentUser = $session->{CurrentUser};
+  $UnrepliedTickets = RT::Tickets->new($CurrentUser);
+  my $search = $class->new(TicketsObj => $UnrepliedTickets);
+warn Dumper $search;
+  $search->Prepare;
+}
+</%init>
diff --git a/rt/lib/RT/Search/UnrepliedTickets.pm b/rt/lib/RT/Search/UnrepliedTickets.pm
new file mode 100644
index 0000000..a996901
--- /dev/null
+++ b/rt/lib/RT/Search/UnrepliedTickets.pm
@@ -0,0 +1,62 @@
+=head1 NAME
+
+  RT::Search::UnrepliedTickets
+
+=head1 SYNOPSIS
+
+=head1 DESCRIPTION
+
+Find all unresolved tickets owned by the current user where the last correspondence
+from a requestor (or ticket creation) is more recent than the last
+correspondence from a non-requestor (if there is any).
+
+=head1 METHODS
+
+=cut
+
+package RT::Search::UnrepliedTickets;
+
+use strict;
+use warnings;
+use base qw(RT::Search);
+
+
+sub Describe  {
+  my $self = shift;
+  return ($self->loc("Tickets awaiting a reply"));
+}
+
+sub Prepare  {
+  my $self = shift;
+
+  my $TicketsObj = $self->TicketsObj;
+  $TicketsObj->Limit(
+    FIELD => 'Owner',
+    VALUE => $TicketsObj->CurrentUser->id
+  );
+  $TicketsObj->Limit(
+    FIELD => 'Status',
+    OPERATOR => '!=',
+    VALUE => 'resolved'
+  );
+  my $txn_alias = $TicketsObj->JoinTransactions;
+  $TicketsObj->Limit(
+    ALIAS => $txn_alias,
+    FIELD => 'Created',
+    OPERATOR => '>',
+    VALUE => 'COALESCE(main.Told,\'1970-01-01\')',
+    QUOTEVALUE => 0,
+  );
+  $TicketsObj->Limit(
+    ALIAS => $txn_alias,
+    FIELD => 'Type',
+    OPERATOR => 'IN',
+    VALUE => [ 'Correspond', 'Create' ],
+  );
+
+  return(1);
+}
+
+RT::Base->_ImportOverlays();
+
+1;
diff --git a/rt/share/html/Search/UnrepliedTickets.html b/rt/share/html/Search/UnrepliedTickets.html
new file mode 100755
index 0000000..37f94e0
--- /dev/null
+++ b/rt/share/html/Search/UnrepliedTickets.html
@@ -0,0 +1,156 @@
+%# false laziness with Results.html; basically this is the same thing but with
+%# a hardcoded RT::Tickets object instead of a Query param
+
+<& /Elements/Header, Title => $title,
+    Refresh => $refresh,
+    LinkRel => \%link_rel &>
+
+% $m->callback( ARGSRef => \%ARGS, Format => \$Format, CallbackName => 'BeforeResults' );
+
+<& /Elements/CollectionList, 
+    Class => 'RT::Tickets',
+    Collection => $session{tickets},
+    TotalFound => $ticketcount,
+    AllowSorting => 1,
+    OrderBy => $OrderBy,
+    Order => $Order,
+    Rows => $Rows,
+    Page => $Page,
+    Format => $Format,
+    BaseURL => $BaseURL,
+    SavedSearchId => $ARGS{'SavedSearchId'},
+    SavedChartSearchId => $ARGS{'SavedChartSearchId'},
+    PassArguments => [qw(Format Rows Page Order OrderBy SavedSearchId SavedChartSearchId)],
+&>
+% $m->callback( ARGSRef => \%ARGS, CallbackName => 'AfterResults' );
+
+% my %hiddens = (Format => $Format, Rows => $Rows, OrderBy => $OrderBy, Order => $Order, HideResults => $HideResults, Page => $Page, SavedChartSearchId => $SavedChartSearchId );
+<div align="right" class="refresh">
+<form method="get" action="<%RT->Config->Get('WebPath')%>/Search/UnrepliedTickets.html">
+% foreach my $key (keys(%hiddens)) {
+<input type="hidden" class="hidden" name="<%$key%>" value="<% defined($hiddens{$key})?$hiddens{$key}:'' %>" />
+% }
+<& /Elements/Refresh, Name => 'TicketsRefreshInterval', Default => $session{'tickets_refresh_interval'}||RT->Config->Get('SearchResultsRefreshInterval', $session{'CurrentUser'}) &>
+<input type="submit" class="button" value="<&|/l&>Change</&>" />
+</form>
+</div>
+<%INIT>
+$m->callback( ARGSRef => \%ARGS, CallbackName => 'Initial' );
+
+# Read from user preferences
+my $prefs = $session{'CurrentUser'}->UserObj->Preferences("SearchDisplay") || {};
+
+# These variables are what define a search_hash; this is also
+# where we give sane defaults.
+$Format      ||= $prefs->{'Format'} || RT->Config->Get('DefaultSearchResultFormat');
+$Order       ||= $prefs->{'Order'} || RT->Config->Get('DefaultSearchResultOrder');
+$OrderBy     ||= $prefs->{'OrderBy'} || RT->Config->Get('DefaultSearchResultOrderBy');
+
+# In this case the search UI isn't available, so trust the defaults.
+
+# Some forms pass in "RowsPerPage" rather than "Rows"
+# We call it RowsPerPage everywhere else.
+
+if ( defined $prefs->{'RowsPerPage'} ) {
+    $Rows = $prefs->{'RowsPerPage'};
+} else {
+    $Rows = 50;
+}
+$Page = 1 unless $Page && $Page > 0;
+
+use RT::Search::UnrepliedTickets;
+
+$session{'i'}++;
+$session{'tickets'} = RT::Tickets->new($session{'CurrentUser'}) ;
+my $search = RT::Search::UnrepliedTickets->new( TicketsObj => $session{'tickets'} );
+$search->Prepare;
+
+if ($OrderBy =~ /\|/) {
+    # Multiple Sorts
+    my @OrderBy = split /\|/,$OrderBy;
+    my @Order = split /\|/,$Order;
+    $session{'tickets'}->OrderByCols(
+        map { { FIELD => $OrderBy[$_], ORDER => $Order[$_] } } ( 0
+        .. $#OrderBy ) );; 
+} else {
+    $session{'tickets'}->OrderBy(FIELD => $OrderBy, ORDER => $Order); 
+}
+$session{'tickets'}->RowsPerPage( $Rows ) if $Rows;
+$session{'tickets'}->GotoPage( $Page - 1 );
+
+# use this to set a CSRF token applying to the search, so that the user can come
+# back to this page without triggering a referrer check
+$session{'CurrentSearchHash'} = {
+    Format      => $Format,
+    Page        => $Page,
+    Order       => $Order,
+    OrderBy     => $OrderBy,
+    RowsPerPage => $Rows
+};
+
+
+my $ticketcount = $session{tickets}->CountAll();
+my $title = loc('New activity on [quant,_1,ticket,tickets]', $ticketcount);
+
+# pass this through on pagination links
+my $QueryString = "?".$m->comp('/Elements/QueryString',
+                               Format => $Format,
+                               Rows => $Rows,
+                               OrderBy => $OrderBy,
+                               Order => $Order,
+                               Page => $Page);
+
+if ($ARGS{'TicketsRefreshInterval'}) {
+    $session{'tickets_refresh_interval'} = $ARGS{'TicketsRefreshInterval'};
+}
+
+my $refresh = $session{'tickets_refresh_interval'}
+    || RT->Config->Get('SearchResultsRefreshInterval', $session{'CurrentUser'} );
+
+# Check $m->request_args, not $DECODED_ARGS, to avoid creating a new CSRF token on each refresh
+if (RT->Config->Get('RestrictReferrer') and $refresh and not $m->request_args->{CSRF_Token}) {
+    my $token = RT::Interface::Web::StoreRequestToken( $session{'CurrentSearchHash'} );
+    $m->notes->{RefreshURL} = RT->Config->Get('WebURL')
+        . "Search/UnrepliedTickets.html?CSRF_Token="
+            . $token;
+}
+
+my %link_rel;
+my $genpage = sub {
+    return $m->comp(
+        '/Elements/QueryString',
+        Format  => $Format,
+        Rows    => $Rows,
+        OrderBy => $OrderBy,
+        Order   => $Order,
+        Page    => shift(@_),
+    );
+};
+
+if ( RT->Config->Get('SearchResultsAutoRedirect') && $ticketcount == 1 &&
+    $session{tickets}->First ) {
+# $ticketcount is not always precise unless $UseSQLForACLChecks is set to true,
+# check $session{tickets}->First here is to make sure the ticket is there.
+    RT::Interface::Web::Redirect( RT->Config->Get('WebURL')
+            ."Ticket/Display.html?id=". $session{tickets}->First->id );
+}
+
+my $BaseURL = RT->Config->Get('WebPath')."/Search/UnrepliedTickets.html?";
+$link_rel{first} = $BaseURL . $genpage->(1)         if $Page > 1;
+$link_rel{prev}  = $BaseURL . $genpage->($Page - 1) if $Page > 1;
+$link_rel{next}  = $BaseURL . $genpage->($Page + 1) if ($Page * $Rows) < $ticketcount;
+$link_rel{last}  = $BaseURL . $genpage->(POSIX::ceil($ticketcount/$Rows)) if $Rows and ($Page * $Rows) < $ticketcount;
+</%INIT>
+<%CLEANUP>
+$session{'tickets'}->PrepForSerialization();
+</%CLEANUP>
+<%ARGS>
+$HideResults => 0
+$Rows => undef
+$Page => 1
+$OrderBy => undef
+$Order => undef
+$SavedSearchId => undef
+$SavedChartSearchId => undef
+$Format => undef
+</%ARGS>

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

Summary of changes:
 httemplate/elements/header-full.html               |    3 +
 httemplate/elements/notify-tickets.html            |   37 +++++++
 rt/lib/RT/Search/UnrepliedTickets.pm               |   62 +++++++++++
 .../Search/{Results.html => UnrepliedTickets.html} |  117 +++++---------------
 4 files changed, 130 insertions(+), 89 deletions(-)
 create mode 100644 httemplate/elements/notify-tickets.html
 create mode 100644 rt/lib/RT/Search/UnrepliedTickets.pm
 copy rt/share/html/Search/{Results.html => UnrepliedTickets.html} (53%)




More information about the freeside-commits mailing list