Merge branch 'master' of git.freeside.biz:/home/git/freeside
authorMark Wells <mark@freeside.biz>
Sun, 12 Jun 2016 06:22:47 +0000 (23:22 -0700)
committerMark Wells <mark@freeside.biz>
Sun, 12 Jun 2016 06:22:47 +0000 (23:22 -0700)
FS/FS/TicketSystem/RT_Internal.pm
httemplate/elements/freeside-menu.css
httemplate/elements/freeside.css
httemplate/elements/notify-tickets.html
httemplate/elements/table-tickets.html
rt/lib/RT/Search/UnrepliedTickets.pm
rt/lib/RT/Ticket_Vendor.pm
rt/share/html/Elements/CollectionAsTable/Row
rt/share/html/Elements/QueueSummaryByLifecycle
rt/share/static/css/freeside3/ticket-lists.css
rt/share/static/css/freeside4/ticket-lists.css

index 1c4513e..ffee484 100644 (file)
@@ -240,7 +240,8 @@ sub service_tickets  {
 sub _ticket_info {
   # Takes an RT::Ticket; returns a hashref of the ticket's fields, including 
   # custom fields.  Also returns custom and selfservice priority values as 
-  # _custom_priority and _selfservice_priority.
+  # _custom_priority and _selfservice_priority, and the IsUnreplied property
+  # as is_unreplied.
   my $t = shift;
 
   my $custom_priority = 
@@ -265,6 +266,7 @@ sub _ticket_info {
   if ( $ss_priority ) {
     $ticket_info{'_selfservice_priority'} = $ticket_info{"CF.{$ss_priority}"};
   }
+  $ticket_info{'is_unreplied'} = $t->IsUnreplied;
   my $svcnums = [ 
     map { $_->Target =~ /cust_svc\/(\d+)/; $1 } 
         @{ $t->Services->ItemsArrayRef }
index a66ebc0..365b9d4 100644 (file)
@@ -142,3 +142,13 @@ a:visited:hover.fsdarkbutton {
          overflow:visible;
 }
 
+
+/* elements/notify-tickets.html is in the menu area */
+.dot {
+  border-radius: 50%;
+  border: 1px solid black;
+  width: 1ex;
+  height: 1ex;
+  display: inline-block;
+  margin-top: 0.3ex;
+}
index 7bf374c..fb5e7d9 100644 (file)
@@ -341,3 +341,5 @@ div.package-marker-change_from {
   border-left: solid #bbffbb 30px;
   display: inline-block;
 }
+
+
index faf998e..e661737 100644 (file)
@@ -1,13 +1,4 @@
 % 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">
index d722c9d..b322a5f 100644 (file)
@@ -14,6 +14,7 @@ View
 
 <THEAD>
 <TR>
+  <TH CLASS="grid" BGCOLOR="#cccccc"></TH>
   <TH CLASS="grid" BGCOLOR="#cccccc"><% mt('#') |h %></TH>
   <TH CLASS="grid" BGCOLOR="#cccccc"><% mt('Subject') |h %></TH>
   <TH CLASS="grid" BGCOLOR="#cccccc"><% mt('Status') |h %></TH>
@@ -42,6 +43,16 @@ View
   <TR>
   
     <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
+%     if ( $ticket->{is_unreplied} ) {
+        <A CLASS="dot" STYLE="background-color: green" HREF=<%$href%>>
+%     } else {
+%       # placeholder
+        <A CLASS="dot" STYLE="visibility: hidden" HREF=<%$href%>>
+%     }
+      </A>
+    </TD>
+
+    <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
       <A HREF=<%$href%>><% $ticket->{id} %></A>
     </TD>
   
index a996901..12d847a 100644 (file)
@@ -6,9 +6,9 @@
 
 =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).
+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
 
@@ -30,14 +30,23 @@ sub Prepare  {
   my $self = shift;
 
   my $TicketsObj = $self->TicketsObj;
+  # if SystemUser does this search (as in QueueSummaryByLifecycle), they
+  # should get all tickets regardless of ownership
+  if ($TicketsObj->CurrentUser->id != RT->SystemUser->id) {
+    $TicketsObj->Limit(
+      FIELD => 'Owner',
+      VALUE => $TicketsObj->CurrentUser->id
+    );
+  }
   $TicketsObj->Limit(
-    FIELD => 'Owner',
-    VALUE => $TicketsObj->CurrentUser->id
+    FIELD => 'Status',
+    OPERATOR => '!=',
+    VALUE => 'resolved'
   );
   $TicketsObj->Limit(
     FIELD => 'Status',
     OPERATOR => '!=',
-    VALUE => 'resolved'
+    VALUE => 'rejected',
   );
   my $txn_alias = $TicketsObj->JoinTransactions;
   $TicketsObj->Limit(
index a55bb7b..4a78838 100644 (file)
@@ -92,5 +92,28 @@ sub WillResolveAsString {
   return $self->WillResolveObj->AsString();
 }
 
+=head2 IsUnreplied
+
+Returns true if there's a Correspond or Create transaction more recent than
+the Told date of this ticket (or the ticket has no Told date) and the ticket
+is not rejected or resolved.
+
+=cut
+
+sub IsUnreplied {
+  my $self = shift;
+  return 0 if $self->Status eq 'resolved'
+           or $self->Status eq 'rejected';
+
+  my $Told = $self->Told || '1970-01-01';
+  my $Txns = $self->Transactions;
+  $Txns->Limit(FIELD => 'Type',
+               OPERATOR => 'IN',
+               VALUE => [ 'Correspond', 'Create' ]);
+  $Txns->Limit(FIELD => 'Created',
+               OPERATOR => '>',
+               VALUE => $Told);
+  $Txns->Count ? 1 : 0;
+}
 
 1;
index deaa312..4b2cfae 100644 (file)
@@ -57,6 +57,11 @@ $Class     => 'RT__Ticket'
 $Classes => ''
 </%ARGS>
 <%init>
+# it's a hack, but it has to be applied in every ticket search regardless
+# of format, so...
+if ( $record and $record->isa('RT::Ticket') and $record->IsUnreplied ) {
+  $Classes .= ' unreplied-ticket';
+}
 $m->out(  '<tr class="' . $Classes . ' '
         . ( $Warning ? 'warnline' : $i % 2 ? 'oddline' : 'evenline' ) . '" >'
         . "\n" );
index f21cb20..d6ed1e6 100644 (file)
@@ -66,9 +66,13 @@ for my $queue (@queues) {
     next if lc($queue->{Lifecycle} || '') ne lc $lifecycle->Name;
 
     $i++;
+
+    my $classes = $i%2 ? 'oddline' : 'evenline';
+    $classes .= ' unreplied-ticket' if $queue->{Unreplied} > 0;
 </%PERL>
-<tr class="<% $i%2 ? 'oddline' : 'evenline'%>" >
+<tr class="<% $classes %>">
 
+<td></td>
 <td>
     <a href="<% $link_all->($queue, \@cur_statuses) %>" title="<% $queue->{Description} %>"><% $queue->{Name} %></a>
 </td>
@@ -134,6 +138,24 @@ for my $queue (@queues) {
     $lifecycle{ lc $cycle->Name } = $cycle;
 }
 
+use RT::Search::UnrepliedTickets;
+my $Tickets = RT::Tickets->new( RT->SystemUser );
+my $Search = RT::Search::UnrepliedTickets->new( TicketsObj => $Tickets );
+$Search->Prepare;
+
+for my $queue (@queues) {
+    my $cycle = RT::Lifecycle->Load( Name => $queue->{'Lifecycle'} );
+    $lifecycle{ lc $cycle->Name } = $cycle;
+
+    # show whether there are unreplied tickets
+    # somewhat inefficient but we only use the count query
+    my $tix = $Tickets->Clone;
+    $tix->Limit(FIELD => 'Queue',
+                OPERATOR => '=',
+                VALUE => $queue->{id});
+    $queue->{Unreplied} = $tix->Count;
+}
+
 unless (@statuses) {
     my %seen;
     foreach my $set ( 'initial', 'active' ) {
index 84c9a92..257cf3b 100644 (file)
@@ -99,8 +99,18 @@ tr.collection-as-table+tr.collection-as-table th {
 
 }
 
-
-
+tr.unreplied-ticket > :first-child::before {
+  /* green dot */
+    border: 1px solid black;
+    border-radius: 50%;
+    display: inline-block;
+    height: 1ex;
+    width: 1ex;
+    float: left;
+    content: '';
+    margin-top: 1ex;
+    background-color: green;
+}
 
 table.queue-summary td {
   background: #efefef;
index cdf1019..3d4706f 100644 (file)
@@ -81,6 +81,19 @@ table.collection-as-table.chart th {
     border-bottom: 2px solid #ccc
 }
 
+tr.unreplied-ticket > :first-child::before {
+  /* green dot */
+    border: 1px solid black;
+    border-radius: 50%;
+    display: inline-block;
+    height: 1ex;
+    width: 1ex;
+    float: left;
+    content: '';
+    margin-right: 1ex;
+    background-color: green;
+}
+
 table.queue-summary td {
     background: #efefef;
     border-bottom: 1px solid #ccc;