Jgreen has submitted this change and it was merged.

Change subject: redeploy OTRS idle_agent_report
......................................................................


redeploy OTRS idle_agent_report

Change-Id: Ie5151001d03dc276c8588995b97b2f9e4905d5de
---
A files/otrs/idle_agent_report
M manifests/role/otrs.pp
2 files changed, 253 insertions(+), 0 deletions(-)

Approvals:
  Jgreen: Checked; Looks good to me, approved
  jenkins-bot: Verified



diff --git a/files/otrs/idle_agent_report b/files/otrs/idle_agent_report
new file mode 100755
index 0000000..7e69598
--- /dev/null
+++ b/files/otrs/idle_agent_report
@@ -0,0 +1,247 @@
+#!/usr/bin/perl
+use strict;
+use CGI;
+use FindBin qw($Bin); # use ../ as lib location
+use lib "$Bin/../..";
+use lib "$Bin/../../Kernel/cpan-lib";
+use Kernel::Config;
+use Kernel::System::Time;
+use Kernel::System::Log;
+use Kernel::System::Main;
+use Kernel::System::Encode;
+use Kernel::System::User;
+use Kernel::System::Group;
+use Kernel::System::DB;
+use Kernel::System::Auth;
+use Kernel::System::AuthSession;
+
+# if any of these queries returns >5K rows we have a problem
+my $rowlimit = 5000;
+
+
+# http://dev.otrs.org/2.4/Kernel/System/Group.html
+# http://meta.wikimedia.org/wiki/OTRS#OTRS_administrators
+my %RoleOK2View = (
+       'OTRS admins' => 1,
+       'Wikimedia Technical Staff' => 1,
+);
+
+# reporting queries provided by Philippe, simply numbered for easy access from 
URI query string
+# stab otrs db libs for lacking fetchrow hashref
+my $query = {
+       1 => {
+               'desc' => 'Last login over 6 months',
+               'cols' => [ qw(login id email lastlogin) ],
+               'sql' => 'SELECT login, id, eml.preferences_value, 
DATE_FORMAT(FROM_UNIXTIME(ull.preferences_value), "%Y-%m-%d") FROM users LEFT 
JOIN user_preferences AS ull ON (ull.user_id = users.id) LEFT JOIN 
user_preferences AS eml ON (eml.user_id = users.id) WHERE ull.preferences_key = 
"UserLastLogin" AND eml.preferences_key = "UserEmail" AND valid_id = 1 AND 
FROM_UNIXTIME(ull.preferences_value) < DATE_SUB(CURDATE(), INTERVAL 6 MONTH) 
ORDER BY FROM_UNIXTIME(ull.preferences_value)',
+       },
+       2 => {
+               'desc' => 'No login one month, no ticket three months',
+               'cols' => [ qw(login id email lastlogin lastticket) ],
+               'sql' => 'SELECT u.login, u.id, eml.preferences_value, 
DATE_FORMAT(FROM_UNIXTIME(ull.preferences_value), "%Y-%m-%d"), 
DATE_FORMAT(last_ticket, "%Y-%m-%d") FROM users AS u LEFT JOIN user_preferences 
AS ull ON (ull.user_id = u.id) LEFT JOIN user_preferences AS eml ON 
(eml.user_id = u.id) LEFT JOIN (SELECT MAX(change_time) AS last_ticket, user_id 
FROM ticket GROUP BY user_id) AS t ON (t.user_id = u.id) WHERE 
ull.preferences_key = "UserLastLogin" AND u.valid_id = 1 AND 
eml.preferences_key = "UserEmail" AND FROM_UNIXTIME(ull.preferences_value) < 
DATE_SUB(CURDATE(), INTERVAL 1 MONTH) AND FROM_UNIXTIME(ull.preferences_value) 
>= DATE_SUB(CURDATE(), INTERVAL 6 MONTH) AND last_ticket < DATE_SUB(CURDATE(), 
INTERVAL 3 MONTH) GROUP BY u.id ORDER BY last_ticket',
+       },
+       3 => {
+               'desc' => 'Inactive users who occasionally log in',
+               'cols' => [ qw(login id email lastlogin lastticket) ],
+               'sql' => 'SELECT u.login, u.id, eml.preferences_value, 
DATE_FORMAT(FROM_UNIXTIME(ull.preferences_value), "%Y-%m-%d"), 
DATE_FORMAT(last_ticket, "%Y-%m-%d") FROM users AS u LEFT JOIN user_preferences 
AS ull ON (ull.user_id = u.id) LEFT JOIN user_preferences AS eml ON 
(eml.user_id = u.id) LEFT JOIN (SELECT MAX(change_time) AS last_ticket, user_id 
FROM ticket GROUP BY user_id) AS t ON (t.user_id = u.id) WHERE 
ull.preferences_key = "UserLastLogin" AND u.valid_id = 1 AND 
eml.preferences_key = "UserEmail" AND last_ticket < DATE_SUB(CURDATE(), 
INTERVAL 120 DAY) GROUP BY u.id ORDER BY last_ticket', 
+
+       },
+};
+
+# I use CGI.pm here instead of the built in stuff to avoid tangling with the
+# OTRS UI libaries. CGI.pm is quick and dirty way to select query and (below)
+# whether or not to output a csv file
+my $q = CGI->new;
+my $params = $q->Vars;
+my $qn = (defined $query->{$params->{'qn'}}) ? $params->{'qn'} : 1;
+
+# essentially boilerplate to create common OTRS objects we'll use for database
+# calls and client authentication
+my %CommonObject = ();
+$CommonObject{'ConfigObject'} = Kernel::Config->new();
+$CommonObject{'LogObject'}    = Kernel::System::Log->new(
+       'LogPrefix' => 'idle_agent_report',
+       'ConfigObject' => $CommonObject{'ConfigObject'},
+);
+$CommonObject{'MainObject'}    = Kernel::System::Main->new(%CommonObject);
+$CommonObject{'EncodeObject'}  = Kernel::System::Encode->new(%CommonObject);
+$CommonObject{'TimeObject'}    = Kernel::System::Time->new(%CommonObject);
+$CommonObject{'DBObject'}      = Kernel::System::DB->new(%CommonObject);
+$CommonObject{'UserObject'}    = Kernel::System::User->new(%CommonObject);
+$CommonObject{'GroupObject'}   = Kernel::System::Group->new(%CommonObject);
+$CommonObject{'AuthObject'}    = Kernel::System::Auth->new(%CommonObject);
+$CommonObject{'SessionObject'} = 
Kernel::System::AuthSession->new(%CommonObject);
+
+# OTRS-authenticated sessions should have a valid Session cookie which returns
+# UserID etc. We retrieve the cookie and check against OTRS auth API.
+# see http://dev.otrs.org/2.4/Kernel/System/AuthSession.html
+# I do not bother to update stored session data for fear of breaking something.
+my (%SessionData,$RoleAuthOk);
+my $UserRoles;
+my $ClientSessionID = $q->cookie('Session');
+if (defined $ClientSessionID) {
+       my $ValidSessionID = $CommonObject{'SessionObject'}->CheckSessionID(
+               'SessionID' => $ClientSessionID,
+       );
+       # use SessionData UserID to get Role membership, then check against 
%RoleOK2View
+       # there must be a better way but the Group API seems utterly lame and 
broken
+       if ($ValidSessionID) {
+               %SessionData = $CommonObject{'SessionObject'}->GetSessionIDData(
+                       'SessionID' => $ClientSessionID,
+               );
+               my %Roles = $CommonObject{'GroupObject'}->RoleList(
+                       'Valid' => 1,
+               );
+               my %UserRoles = 
$CommonObject{'GroupObject'}->GroupUserRoleMemberList(
+                       'UserID' => $SessionData{'UserID'},
+                       'Result' => 'HASH',
+               );
+               for my $RoleID (keys %UserRoles) {
+                       $UserRoles .= "$RoleID-$Roles{$RoleID}|";
+                       if (defined $RoleOK2View{$Roles{$RoleID}}) {
+                               $SessionData{'RoleAuthenticated'} = 1;
+                       }
+               }
+               chop $UserRoles;
+       }
+}
+
+# Various possible output outcomes
+if ( (! defined %SessionData) or (! defined $SessionData{'RoleAuthenticated'}) 
or (! defined $CommonObject{'DBObject'}) ) {
+
+       # failure modes
+       print "Content-type: text/html\n\n" .
+               "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 
Transitional//EN\" " .
+               "\"http://www.w3.org/TR/html4/loose.dtd\";>\n" .
+               "<html><head>\n" .
+               "<meta HTTP-EQUIV='REFRESH' content='5; url=/'>\n" .
+               "</head><body>\n"; 
+
+       if ( ! defined %SessionData ) {
+               # No valid session data
+               print "<b>You need to log in to OTRS.</b><p>\n";
+       } elsif ( ! defined $SessionData{'RoleAuthenticated'} ) {
+               # Logged in, but not in an approved Role
+               print "<b>Sorry, this report is only available to 
administrators.</b><p>\n";
+               $CommonObject{LogObject}->Log(
+                       Priority => 'error',
+            Message  => "$SessionData{'UserID'}: $UserRoles",
+        );
+       } elsif ( ! defined $CommonObject{'DBObject'}) {
+               # Db connection fail
+               print "<b>Database connection failed!</b></p>\n"
+       }
+
+       print "You should be redirected to the OTRS main page in a few 
seconds.<br>\n" .
+               "If not, <a href='/'>[click here]</a><p>\n" .
+               "</body></html>";
+
+} elsif (defined $params->{'csv'}) {
+
+       # Valid session, group membership, and successful db connection.
+       # CGI request for CSV format. Proceed.
+
+       # suggest document name
+       my $filename = 'OTRS-' . $query->{$qn}->{'desc'};
+       $filename =~ s/\s+/_/g;
+
+       print "Content-type: text/csv\n" .
+               "Content-Disposition: attachment; filename=" . $filename . 
".csv\n\n" .
+               join("\t", (@{$query->{$qn}->{'cols'}},'queues')) . "\n";
+
+       # fetch and format the user data
+       my $Users = GetUsers();
+       for my $UserID (sort { $Users->{$a}->{'row'} <=> $Users->{$b}->{'row'}} 
keys %{$Users}) {
+               for my $c (@{$query->{$qn}->{'cols'}}) {
+                       print "$Users->{$UserID}->{$c}\t";
+               }
+               print join(',', sort keys %{$Users->{$UserID}->{'queues'}}) . 
"\n";
+       }
+
+} else {
+
+       # Valid session, group membership, and successful db connection. 
Proceed.
+       print "Content-type: text/html\n\n" .
+               "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 
Transitional//EN\" " .
+               "\"http://www.w3.org/TR/html4/loose.dtd\";>\n" .
+               "<html><head>\n" .
+               "<title>otrs last login report</title>\n" .
+               "<style type='text/css'>\n" .
+               " body,a,textarea { background-color:white; color:black; 
font:13px sans; }\n" .
+               " td { background-color:white; color:black; font:13px sans; 
vertical-align:top; }\n" .
+               " input, select { background-color:white; color:black; border: 
1px solid gray; font:13px sans; }\n" .
+               " table, td { border-width: 1px 1px 1px 1px; border-collapse: 
collapse; }\n" .
+               " a { color:blue; text-decoration:none; }\n" .
+               "</style>\n" .
+               "</head><body>\n";
+
+       # wee form at the top for report controls
+       print "<form method=get action=''>\n<select name=qn size=1 
onchange=submit()>\n";
+       for my $n (sort keys %{$query}) {
+               print "<option value=$n";
+               print " selected" if ($n == $qn);
+               print ">$query->{$n}->{'desc'}</option>\n";
+       }
+       print "</select>\n<input type=submit value=&gt;>\n" .
+               "<input type=button value='csv' 
onClick=\"parent.location='$ENV{SCRIPT_URI}?qn=$qn&csv=1'\">" .
+               "</form><p>\n";
+
+       # the report table
+       print "<table border=1>\n" .
+               "<tr><td><b>" . join("</b></td><td><b>", 
(@{$query->{$qn}->{'cols'}},'queues')) . "</b></tr>\n";
+       my $Users = GetUsers();
+       for my $UserID (sort { $Users->{$a}->{'row'} <=> $Users->{$b}->{'row'}} 
keys %{$Users}) {
+               print "<tr>";
+               for my $c (@{$query->{$qn}->{'cols'}}) {
+                       print "<td>$Users->{$UserID}->{$c}</td>";
+               }
+               print "<td>" . join(', ', sort keys 
%{$Users->{$UserID}->{'queues'}}) . "</td>\n</tr>\n";
+       }
+
+       print "</table>\n</body></html>";
+
+}
+exit;
+
+
+
+
+# Get list of users matching the main query report, and the queues they can 
read+write
+sub GetUsers {
+
+       # Get list of users
+       my $Users;
+       my $row = 1;
+       $CommonObject{'DBObject'}->Prepare( SQL => $query->{$qn}->{'sql'}, 
Limit => $rowlimit );
+       while (my @row = $CommonObject{'DBObject'}->FetchrowArray()) {  # no 
fetchrow hashref, stab! cry!
+               my $h;
+               map { $h->{${$query->{$qn}->{'cols'}}[$_]} = $row[$_] } (0 .. 
scalar(@row));
+               unless (defined $Users->{$h->{'id'}}->{'row'}) {
+                       $h->{'row'} = $row;
+                       $row++; 
+               }
+               $Users->{$h->{'id'}} = $h;
+       }
+
+       # Get list of queues
+       my $query = "SELECT u.id,q.name " .
+               "FROM users u, role_user ru, group_role gu, queue q, groups g, 
roles r " .
+               "WHERE u.id = ru.user_id " .
+               "AND ru.role_id = r.id " .
+               "AND ru.role_id = gu.role_id " .
+               "AND gu.group_id = q.group_id " .
+               "AND g.id = gu.group_id " .
+               "AND gu.permission_key in ('rw') " .
+               "AND gu.permission_value = 1 " .
+               "AND r.valid_id = 1 " .
+               "AND q.valid_id = 1 " .
+               "AND u.valid_id = 1 " .
+               "AND g.valid_id = 1 " .
+               "AND u.id IN (" . join(',', keys %{$Users}) . ")";
+       $CommonObject{'DBObject'}->Prepare( SQL => $query );
+       while (my @row = $CommonObject{'DBObject'}->FetchrowArray()) {
+               $row[1] =~ s/::.*$//; # no documentation, but OTRS treats :: as 
a delimiter for grouping queues
+               $Users->{$row[0]}->{'queues'}->{$row[1]} = 1;
+       }
+
+       return $Users;
+}
diff --git a/manifests/role/otrs.pp b/manifests/role/otrs.pp
index b8591e1..c5bdcc7 100644
--- a/manifests/role/otrs.pp
+++ b/manifests/role/otrs.pp
@@ -49,6 +49,12 @@
             group => www-data,
             mode => '0755',
             source => 'puppet:///files/otrs/otrs.TicketExport2Mbox.pl';
+        '/opt/otrs/bin/cgi-bin/idle_agent_report':
+            ensure => present,
+            owner => otrs,
+            group => www-data,
+            mode => '0755',
+            source => 'puppet:///files/otrs/idle_agent_report';
         '/usr/local/bin/train_spamassassin':
             ensure => present,
             owner => root,

-- 
To view, visit https://gerrit.wikimedia.org/r/83822
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: Ie5151001d03dc276c8588995b97b2f9e4905d5de
Gerrit-PatchSet: 1
Gerrit-Project: operations/puppet
Gerrit-Branch: production
Gerrit-Owner: Jgreen <jgr...@wikimedia.org>
Gerrit-Reviewer: Jgreen <jgr...@wikimedia.org>
Gerrit-Reviewer: jenkins-bot

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to