Extension talk:RT

Latest comment: 10 years ago by Kghbln in topic Updated for mysql on 1.23

Hey-

Looks like this only works with RT using Postgres, not with MySQL? It doesn't explicitly say so anywhere, but it appears the pg_connect is only for postgres, and hence this extension is limited to Postgres based RT installations.

quick / dirty mysql hack to RT.php

edit

i just did this to quickly get things going for our site and will improve later.

  • all my comments are marked with erikvw
  • original code remains in the file, just commented out with ####
  • requires a few extra parameters instead of just the $wgRequestTracker_DBconn
    • $wgRequestTracker_DBhost=
    • $wgRequestTracker_DBuser=
    • $wgRequestTracker_DBpasswd=
    • $wgRequestTracker_DBdbname=
  • mostly change pg_ to mysql_, mysql does not have mysql_fetch_all, mysql can be case sensitive on table names
  • use date_format() and concat() in main sql string instead.

--Erikvw 12:14, 1 June 2009 (UTC)Reply

<?php
/**
 * RT (Request Tracker) extension for MediaWiki
 *
 * @file
 * @ingroup Extensions
 *
 * Usage: Add the following three lines to LocalSettings.php:
 * require_once( "$IP/extensions/RT/RT.php" );
 * $wgRequestTracker_URL = 'https://rt.example.com/Ticket/Display.html?id';
 * $wgRequestTracker_DBconn = 'user=rt dbname=rt';
 *
 * For other options, please see the complete documentation
 *
 * @author Greg Sabino Mullane <greg@endpoint.com>
 * @license MIT <http://www.opensource.org/licenses/mit-license.php>
 * @version 1.8
 * @link http://www.mediawiki.org/wiki/Extension:RT
 */

$rt_uri = 'http://www.mediawiki.org/wiki/Extension:RT';

## Default values: Override in LocalSettings.php, not here!
$wgRequestTracker_URL         = 'http://rt.example.com/Ticket/Display.html?id';
####$wgRequestTracker_DBconn      = 'user=rt dbname=rt';
$wgRequestTracker_Formats     = array();
$wgRequestTracker_Cachepage   = 0;
$wgRequestTracker_Useballoons = 1;
$wgRequestTracker_Active      = 1;

// added erikvw - begin
$wgRequestTracker_DBuser     = 'rtuser';
$wgRequestTracker_DBpasswd     = 'wibble';
$wgRequestTracker_DBdbname      = 'rtdb';
$wgRequestTracker_DBhost      = 'rt.example.com';
####$wgRequestTracker_DBconn      = array('rt.example.com','rtuser', 'wibble');
// added erikvw - end


## Time formatting
## Example formats:
## FMHH:MI AM FMMon DD, YYYY => 2:42 PM Jan 23, 2009
## HH:MI FMMonth DD, YYYY => 14:42 January 23, 2009
## YYYY/MM/DD => 2009/01/23
## For a more complete list of possibilities, please visit:
## http://www.postgresql.org/docs/current/interactive/functions-formatting.html
//removed erikvw - begin
####$wgRequestTracker_TIMEFORMAT_LASTUPDATED  = 'FMHH:MI AM FMMonth DD, YYYY';
####$wgRequestTracker_TIMEFORMAT_LASTUPDATED2 = 'FMMonth DD, YYYY';
####$wgRequestTracker_TIMEFORMAT_CREATED      = 'FMHH:MI AM FMMonth DD, YYYY';
####$wgRequestTracker_TIMEFORMAT_CREATED2     = 'FMMonth DD, YYYY';
####$wgRequestTracker_TIMEFORMAT_RESOLVED     = 'FMHH:MI AM FMMonth DD, YYYY';
####$wgRequestTracker_TIMEFORMAT_RESOLVED2    = 'FMMonth DD, YYYY';
####$wgRequestTracker_TIMEFORMAT_NOW          = 'FMHH:MI AM FMMonth DD, YYYY';
//removed erikvw - end

// added erikvw - begin
## For a more complete list of possibilities, please visit:
##http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html#function_date-format
$wgRequestTracker_TIMEFORMAT_LASTUPDATED  = '%h:%i %p %b %D, %Y';
$wgRequestTracker_TIMEFORMAT_LASTUPDATED2 = '%D %b %Y';
$wgRequestTracker_TIMEFORMAT_CREATED      = '%h:%i %p %b %D, %Y';
$wgRequestTracker_TIMEFORMAT_CREATED2     = '%D %b %Y';
$wgRequestTracker_TIMEFORMAT_RESOLVED     = '%h:%i %p %b %D, %Y';
$wgRequestTracker_TIMEFORMAT_RESOLVED2    = '%D %b %Y';
$wgRequestTracker_TIMEFORMAT_NOW          = '%h:%i %p %b %D, %Y';
// added erikvw - end


// Ensure nothing is done unless run via MediaWiki
if ( !defined( 'MEDIAWIKI' ) ) {
	echo( "This is an extension to the MediaWiki package and cannot be run standalone.\n" );
	echo( "Please visit $rt_uri\n" );
	die( -1 );
}

// Credits for Special:Version
$wgExtensionCredits['parserhook'][] = array(
	'name'           => 'RT',
	'version'        => '1.8',
	'author'         => array( 'Greg Sabino Mullane' ),
	'description'    => 'Fancy interface to RT (Request Tracker)',
	'descriptionmsg' => 'rt-desc',
	'url'            => $rt_uri,
	);

// Pull in the Internationalization file
$wgExtensionMessagesFiles['RT'] =  dirname( __FILE__ ) . '/RT.i18n.php';

// Use a hook to enable control of parsing <rt>...</rt> content
$wgExtensionFunctions[] = 'efRT_Setup';
function efRT_Setup() {

	global $wgParser, $wgUploadDirectory, $wgCommandLineMode;

	wfLoadExtensionMessages( 'RT' );

	if ( $wgCommandLineMode ) {
		return true;
	}

	$wgParser->setHook( 'rt', 'rtRender' );

	return true;
}

 
// This is called to process <rt>...</rt> within a page
function rtRender( $input, $args=array(), $parser=null ) {

	global $wgRequestTracker_Cachepage, $wgRequestTracker_Active, 
		$wgRequestTracker_DBconn,
		// added erikvw - begin
		$wgRequestTracker_DBuser,
		$wgRequestTracker_DBpasswd,
		$wgRequestTracker_DBdbname,
		$wgRequestTracker_DBhost,
		// added erikvw - begin
		$wgRequestTracker_TIMEFORMAT_LASTUPDATED,
		$wgRequestTracker_TIMEFORMAT_LASTUPDATED2,
		$wgRequestTracker_TIMEFORMAT_CREATED,
		$wgRequestTracker_TIMEFORMAT_CREATED2,
		$wgRequestTracker_TIMEFORMAT_RESOLVED,
		$wgRequestTracker_TIMEFORMAT_RESOLVED2,
		$wgRequestTracker_TIMEFORMAT_NOW;

	// Grab the number if one was given between the <tr> tags
	$ticketnum = 0;
	$matches = array();
	if ( preg_match( '/^\s*(\d+)\s*$/', $input, $matches ) ) {
		$ticketnum = $matches[0];
	}

	// Disable all caching unless told not to
	if ( !$wgRequestTracker_Cachepage ) {
		$parser->disableCache();
	}

	// Try and connect to the database if we are active
	if ( $wgRequestTracker_Active ) {
		global $wgUser;

		//added erikvw - begin
		####$dbh = pg_connect( $wgRequestTracker_DBconn );
		$dbh = mysql_connect( $wgRequestTracker_DBhost, $wgRequestTracker_DBuser,$wgRequestTracker_DBpasswd  );
		$db_selected = mysql_select_db($wgRequestTracker_DBdbname, $dbh);
		//added erikvw - end

		if ( $dbh == false ) {
			wfDebug( "DB connection error\n" );
			wfDebug( "Connection string: $wgRequestTracker_DBconn\n" );
			$wgRequestTracker_Active = 0;
		}
		$tz = $wgUser->getOption( 'timecorrection' );
		if ( $tz ) {
			$found = array();
			if ( preg_match ( '/((-?\d\d?):(\d\d))/', $tz, $found ) ) {
				if ( $found[3] === '00' ) {
					####pg_query( "SET TIME ZONE $found[2]" );
					// added erikvw - begin
					mysql_query( "SET TIME ZONE $found[2]" );
					// added erikvw - end
				}
				else {
					print( "SET TIME ZONE INTERVAL '$found[1]' HOUR TO MINUTE" );
				}
			}
		}
	}

	// If we are not 'active', we leave right away, with minimal output	
	if ( !$wgRequestTracker_Active ) {
		if ( $ticketnum ) {
			return "<span class='rt-ticket-inactive'>RT #$ticketnum</span>";
		}
		$msg = wfMsg( 'rt-inactive' );
		return "<table class='rt-table-inactive' border='1'><tr><td>$msg</td></tr></table>";
	}

	// Standard info we gather
	$TZ = "AT TIME ZONE 'GMT'";
	// removed erikvw - begin
	/* #### $ticketinfo = 't.id, t.subject, t.priority, concat(ucase(left(t.status,1)),substring(t.status,2)) AS status, q.name AS queue,'
		. ' COALESCE(u.realname, u.name) AS owner,'
		. ' u.name AS username,'
		. ' COALESCE(u2.realname, u2.name) AS creator,'
		. " TO_CHAR(t.lastupdated $TZ, '$wgRequestTracker_TIMEFORMAT_LASTUPDATED'::text) AS lastupdated,"
		. " TO_CHAR(t.lastupdated $TZ, '$wgRequestTracker_TIMEFORMAT_LASTUPDATED2'::text) AS lastupdated2,"
		. " TO_CHAR(now() $TZ, '$wgRequestTracker_TIMEFORMAT_NOW'::text) AS nowtime,"
		. " TO_CHAR(t.created $TZ, '$wgRequestTracker_TIMEFORMAT_CREATED'::text) AS created,"
		. " TO_CHAR(t.created $TZ, '$wgRequestTracker_TIMEFORMAT_CREATED2'::text) AS created2,"
		. " TO_CHAR(t.resolved $TZ, '$wgRequestTracker_TIMEFORMAT_RESOLVED'::text) AS resolved,"
		. " TO_CHAR(t.resolved $TZ, '$wgRequestTracker_TIMEFORMAT_RESOLVED2'::text) AS resolved2,"
		. "	CASE WHEN (now() $TZ - t.created) <= '1 second'::interval THEN '1 second' ELSE"
		. " CASE WHEN (now() $TZ - t.created) <= '2 minute'::interval THEN EXTRACT(seconds FROM now() $TZ - t.created) || ' seconds' ELSE"
		. " CASE WHEN (now() $TZ - t.created) <= '2 hour'::interval THEN EXTRACT(minutes FROM now() $TZ - t.created) || ' minutes' ELSE"
		. " CASE WHEN (now() $TZ - t.created) <= '2 day'::interval THEN EXTRACT(hours FROM now() $TZ - t.created) || ' hours' ELSE"
		. " EXTRACT(days FROM now() $TZ - t.created) || ' days' END END END END AS age";
	*/
	// removed erikvw - end
	
	// added erikvw - begin
	// warning: i have REALLY simplified this, e.g. the age calc and dropped timezone issues...
	$ticketinfo = 't.id, t.subject, t.priority, concat(ucase(left(t.status,1)),substring(t.status,2)) AS status, q.name AS queue,'
                . ' COALESCE(u.realname, u.name) AS owner,'
                . ' u.name AS username,'
                . ' COALESCE(u2.realname, u2.name) AS creator,'
                . " date_format(t.lastupdated, '$wgRequestTracker_TIMEFORMAT_LASTUPDATED') AS lastupdated,"
                . " date_format(t.lastupdated, '$wgRequestTracker_TIMEFORMAT_LASTUPDATED2') AS lastupdated2,"
                . " date_format(now(), '$wgRequestTracker_TIMEFORMAT_NOW') AS nowtime,"
                . " date_format(t.created, '$wgRequestTracker_TIMEFORMAT_CREATED') AS created,"
                . " date_format(t.created, '$wgRequestTracker_TIMEFORMAT_CREATED2') AS created2,"
                . " date_format(t.resolved, '$wgRequestTracker_TIMEFORMAT_RESOLVED') AS resolved,"
                . " date_format(t.resolved, '$wgRequestTracker_TIMEFORMAT_RESOLVED2') AS resolved2,"
                . " datediff(now(),t.created) AS age";
	// added erikvw - end


	// removed erikvw - begin
	// The standard query
	//$ticketquery = "SELECT $ticketinfo FROM tickets t"
	//	. ' JOIN users u ON t.owner = u.id'
	//	. ' JOIN users u2 ON t.creator = u2.id'
	//	. ' JOIN queues q ON t.queue = q.id';
	// removed erikvw - end

	// added erikvw - begin
	// mysql is case sensistive to the table names (rt3.6)
        $ticketquery = "SELECT $ticketinfo FROM Tickets t"
                . ' JOIN Users u ON t.owner = u.id'
                . ' JOIN Users u2 ON t.creator = u2.id'
                . ' JOIN Queues q ON t.queue = q.id';
	// added erikvw - end


	// If just a single number, treat it as <rt>#</rt>
	if ( 1 === count( $args ) ) {
		if ( preg_match( '/^\d+$/', key($args) ) ) {
			$ticketnum = key($args);
		}
	}

	// Look up a single ticket number
	if ( $ticketnum ) {
		$SQL = "$ticketquery AND t.id = $ticketnum";
		#$res = pg_query( $dbh, $SQL );
		$res = mysql_query( $SQL );
		if ( !$res ) {
			die ( wfMsg( 'rt-badquery' ) );
		}
		$info = mysql_fetch_array( $res );
		if ( !$info ) {
			return "<span class='rt-nosuchticket'>RT #$ticketnum</span>";
		}
		return rtFancyLink( $info, $args, $parser, 0 );
	}

	// Add in a LIMIT clause if l=xx was used
	$limit = '';
	if ( array_key_exists( 'l', $args ) ) {
		$limit = trim( $args['l'] );
		if ( !preg_match( '/^ *\d+ *$/', $limit ) ) {
			die ( wfMsg ( 'rt-badlimit', $limit ) );
		}
		$limit = " LIMIT $limit";
	}

	// Change the default ORDER BY clause if ob=xx was used
	$orderby = 'ORDER BY t.lastupdated DESC, t.id';
	$valid_orderby = array
		(
		 'id'          => 't.id',
		 'subject'     => 't.subject',
		 'priority'    => 't.priority',
		 'status'      => 't.status',
		 'queue'       => 'q.name',
		 'owner'       => 'COALESCE(u.realname, u.name)',
		 'creator'     => 'COALESCE(u2.realname, u2.name)',
		 'lastupdated' => 't.lastupdated',
		 'created'     => 't.created',
		 'resolved'    => 't.resolved',
		 );
	if ( array_key_exists( 'ob', $args ) ) {
		$orderby = 'ORDER BY';
		$orderbyargs = trim( strtolower( $args['ob'] ) );
		foreach ( preg_split( '/\s*,\s*/', $orderbyargs ) as $word ) {
			$oldlen = strlen( $word );
			$word = ltrim( $word, '!' );
			$mod = $oldlen !== strlen( $word ) ? ' DESC' : '';
			if ( !preg_match( '/^\w+$/', $word ) ) {
				die ( wfMsg ( 'rt-badorderby', $word ) );
			}
			if ( array_key_exists( $word, $valid_orderby ) ) {
				$word = $valid_orderby[$word];
			}
			else if ( !preg_match ('/^\d+$/', $word ) ) {
				die ( wfMsg ( 'rt-badorderby', $word ) );
			}
			$orderby .= " $word$mod,";
		}
		$orderby = rtrim( $orderby, ',' );
	}

	// Determine what status to use. Default is new and open:
	$searchstatus = "AND t.status IN ('new','open')";
	$valid_status = array( 'new', 'open', 'resolved', 'deleted', 'stalled', 'rejected' );
	if ( array_key_exists( 's', $args ) ) {
		$statusargs = trim( strtolower( $args['s'] ) );
		if ( $statusargs === 'all' ) {
			$searchstatus = '';
		}
		else {
			$searchstatus = 'AND t.status IN (';
			foreach ( preg_split( '/\s*,\s*/', $statusargs ) as $word ) {
				if ( !in_array( $word, $valid_status ) ) {
					die ( wfMsg ( 'rt-badstatus', $word ) );
				}
				$searchstatus .= "'$word',";
			}
			$searchstatus = preg_replace( '/.$/', ')', $searchstatus );
		}
	}

	// See if we are limiting to one or more queues
	$searchq = '';
	if ( array_key_exists('q', $args ) ) {
		$qargs = trim( strtolower( $args['q'] ) );
		$searchq = 'AND LOWER(q.name) IN (';
		foreach ( preg_split( '/\s*,\s*/', $qargs ) as $word ) {
			$word = trim( $word );
			if ( !preg_match( '/^[\w \.-]+$/', $word ) ) {
				die ( wfMsg ( 'rt-badqueue', $word ) );
			}
			$searchq .= "'$word',";
		}
		$searchq = preg_replace( '/.$/', ')', $searchq );
	}

	// See if we are limiting to one or more owners
	$searchowner = '';
	if ( array_key_exists('o', $args ) ) {
		$oargs = trim( strtolower( $args['o'] ) );
		$searchowner = 'AND LOWER(u.name) IN (';
		foreach ( preg_split( '/\s*,\s*/', $oargs ) as $word ) {
			$word = trim( $word );
			if ( !preg_match( '/^[\w\@\.\-\:\/]+$/', $word ) ) {
				die ( wfMsg ( 'rt-badowner', $word ) );
			}
			$searchowner .= "'$word',";
		}
		$searchowner = preg_replace( '/.$/', ')', $searchowner );
	}

	// Build and run the final query
	$SQL = "$ticketquery $searchq $searchowner $searchstatus $orderby $limit";
	####$res = pg_query( $dbh, $SQL );
	// erikvw add mysql_query - begin
	$res = mysql_query( $SQL );
	// erikvw add mysql_query - begin

	if ( !$res ) {
		die ( wfMsg( 'rt-badquery' ) );
	}
	####$info = pg_fetch_all( $res );
	// erikvw add mysql fetch - begin
        ####$info = mysql_fetch_all( $res );
	while ($row = mysql_fetch_array($res,  MYSQL_ASSOC)) {
	  $info[] = $row;
          $i++;
	}
	// erikvw add mysql fetch - end


	if ( !$info ) {
		$msg = wfMsg( 'rt-nomatches' );
		return "<table class='rt-table-empty' border='1'><tr><th>$msg</th><tr></table>";
	}

	// Figure out what columns to show
	// Anything specifically requested is shown
	// Everything else is either on or off by default, but can be overidden
	$output = '';

	// The queue: show by default unless searching a single queue
	$showqueue = 1;
	if ( array_key_exists('noqueue', $args )
		|| ($searchq
			&& false === strpos( $searchq, ',' )
			&& !array_key_exists( 'queue', $args ) ) ) {
		$showqueue = 0;
	}

	// The owner: show by default unless searching a single owner
	$showowner = 1;
	if ( array_key_exists( 'noowner', $args )
		|| ( $searchowner
			&& false === strpos( $searchowner, ',' )
			&& !array_key_exists( 'owner', $args ) ) ) {
		$showowner = 0;
	}

	// The status: show by default unless searching a single status
	$showstatus = 1;
	if ( array_key_exists( 'nostatus', $args )
		|| ( false === strpos($searchstatus, ',' )
			&& !array_key_exists( 'status', $args ) ) ) {
		$showstatus = 0;
	}

	// Things we always show unless told not to:
	$showsubject = ! array_key_exists( 'nosubject', $args );
	$showupdated = ! array_key_exists( 'noupdated', $args );
	$showticket  = ! array_key_exists( 'noticket',  $args );

	// Things we don't show unless asked to:
	$showpriority  = array_key_exists( 'priority',  $args );
	$showupdated2  = array_key_exists( 'updated2',  $args );
	$showcreated   = array_key_exists( 'created',   $args );
	$showcreated2  = array_key_exists( 'created2',  $args );
	$showresolved  = array_key_exists( 'resolved',  $args );
	$showresolved2 = array_key_exists( 'resolved2', $args );
	$showage       = array_key_exists( 'age',       $args );

	// Unless 'tablerows' has been set, output the table and header tags
	if ( !array_key_exists( 'tablerows',$args ) ) {

		//changed erikvw. like wikitable and sortable  #### $output = "<table class='rt-table' border='1'><tr>";
		$output = "<table class='wikitable sortable' border='1'><tr>";

		if ( $showticket )    { $output .= '<th>Ticket</th>';       }
		if ( $showqueue )     { $output .= '<th>Queue</th>';        }
		if ( $showsubject )   { $output .= '<th>Subject</th>';      }
		if ( $showstatus )    { $output .= '<th>Status</th>';       }
		if ( $showpriority )  { $output .= '<th>Priority</th>';     }
		if ( $showowner )     { $output .= '<th>Owner</th>';        }
		if ( $showupdated )   { $output .= '<th>Last updated</th>'; }
		if ( $showupdated2 )  { $output .= '<th>Last updated</th>'; }
		if ( $showcreated )   { $output .= '<th>Created</th>';      }
		if ( $showcreated2 )  { $output .= '<th>Created</th>';      }
		if ( $showresolved )  { $output .= '<th>Resolved</th>';     }
		if ( $showresolved2 ) { $output .= '<th>Resolved</th>';     }
		if ( $showage )       { $output .= '<th>Age</th>';          }

		$output .= '</tr>';
	}

	foreach ( $info as $row ) {

		if ( $showticket )  {
			$id = rtFancyLink( $row, $args, $parser, 1 );
			$output .= "<td style='white-space: nowrap'>$id</td>"; 
		}
		if ( $showqueue )     { $output .= '<td>' . htmlspecialchars( $row['queue'] )   . '</td>'; }
		if ( $showsubject )   { $output .= '<td>' . htmlspecialchars( $row['subject'] ) . '</td>'; }
		if ( $showstatus )    { $output .= '<td>' . htmlspecialchars( $row['status'] )  . '</td>'; }
		if ( $showpriority )  { $output .= '<td>' . htmlspecialchars( $row['priority'] ). '</td>'; }
		if ( $showowner )     { $output .= '<td>' . htmlspecialchars( $row['owner'] )   . '</td>'; }
		if ( $showupdated )   { $output .= '<td>' . $row['lastupdated']                 . '</td>'; }
		if ( $showupdated2 )  { $output .= '<td>' . $row['lastupdated2']                . '</td>'; }
		if ( $showcreated )   { $output .= '<td>' . $row['created']                     . '</td>'; }
		if ( $showcreated2 )  { $output .= '<td>' . $row['created2']                    . '</td>'; }
		if ( $showresolved )  { $output .= '<td>' . $row['resolved']                    . '</td>'; }
		if ( $showresolved2 ) { $output .= '<td>' . $row['resolved2']                   . '</td>'; }
		if ( $showage )       { $output .= '<td>' . $row['age']                         . '</td>'; }
		$output .= '<tr>';
	}

	if ( !array_key_exists( 'tablerows',$args ) ) {
		$output .= '</table>';
	}

	return $output;
}


function rtFancyLink( $row, $args, $parser, $istable ) {

	global $wgRequestTracker_URL, $wgRequestTracker_Formats, $wgRequestTracker_Useballoons;

	$ticketnum = $row['id'];
	//changed erikvw. i like it a bit simpler #### $ret = "[$wgRequestTracker_URL=$ticketnum RT #$ticketnum]";
	$ret = "[$wgRequestTracker_URL=$ticketnum $ticketnum]";

	## Check for any custom format args in the rt tag.
	## If any are found, use that and ignore any other args
	$foundformat = 0;
	foreach ( array_keys( $args ) as $val ) {
		if ( array_key_exists( $val, $wgRequestTracker_Formats ) ) {
			$format = $wgRequestTracker_Formats[$val];
			foreach ( array_keys( $row ) as $rev ) {
				$format = str_replace( "?$rev?", "$row[$rev]", $format );
			}
			$ret .= " $format";
			$foundformat = 1;
			break;
		}
	}

	## Process any column-based args to the rt tag
	if ( !$foundformat and !$istable ) {
		foreach ( array_keys( $args ) as $val ) {
			if ( array_key_exists( $val, $row ) ) {
				$format = $args[$val];
				if ( false === strpos( $format, '?' ) ) {
					$showname = $val === 'lastupdated' ? 'Last updated' : ucfirst( $val );
					$ret .= " $showname: $row[$val]";
				}
				else {
					$ret .= " " . str_replace( '?', $row[$val], $format );
				}
			}
		}
	}

	$ret = $parser->recursiveTagParse( $ret );

	// Not using balloons? Just return the current text
	if ( !$wgRequestTracker_Useballoons || array_key_exists( 'noballoon', $args ) ) {
		return "<span class='rt-ticket-noballoon'>$ret</span>";
	}

	$safesub = preg_replace( '/\"/', '\"', $row['subject'] );
	$safesub = preg_replace( '/\'/', "\'", $safesub );
	$safesub = htmlspecialchars( $safesub );

	$safeowner = $row['owner'];
	if ($row['owner'] !== $row['username']) {
		$safeowner .= " ($row[username])";
	}
	$safeowner = preg_replace( '/\"/', '\"', $safeowner );
	$safeowner = preg_replace( '/\'/', "\'", $safeowner );
	$safeowner = htmlspecialchars( $safeowner );

	$safeq = preg_replace( '/\"/', '\"', $row['queue'] );
	$safeq = preg_replace( '/\'/', "\'", $safeq );
	$safeq = htmlspecialchars( $safeq );

	$text = "RT #<b>$ticketnum</b>";
	$text .= "<br />Status: <b>$row[status]</b>";
	$text .= "<br />Subject: <b>$safesub</b>";
	$text .= "<br />Owner: <b>$safeowner</b>";
	$text .= "<br />Queue: <b>$safeq</b>";
	$text .= "<br />Created: <b>$row[created]</b>";
	if ( $row['status'] === 'Resolved' ) {
		$text .= "<br />Resolved: <b>$row[resolved]</b>";
	}
	else {
		$text .= "<br />Last updated: <b>$row[lastupdated]</b>";
	}

	## Prepare some balloon-tek
	$link   = isset( $args['link'] )   ? $args['link']   : '';
	$target = isset( $args['target'] ) ? $args['target'] : '';
	$sticky = isset( $args['sticky'] ) ? $args['sticky'] : '0';
	$width  = isset( $args['width'] )  ? $args['width']  : '0';

	$event  = isset( $args['click'] ) && $args['click'] && !$link ? 'onclick' : 'onmouseover';
	$event2 = '';
	$event  = "$event=\"balloon.showTooltip(event,'${text}',${sticky},${width})\"";

	if ( preg_match( '/onclick/',$event ) && $args['hover'] ) {
		$event2 = " onmouseover=\"balloon.showTooltip(event,'" . $args['hover'] . "',0,${width})\"";
	}

	$has_style = isset( $args['style'] ) && $args['style'];
	$style  = "style=\"" . ($has_style ? $args['style'] . ";cursor:pointer\"" : "cursor:pointer\"");
	$target = $target ? "target=${target}" : '';
	$output = "<span class='rt-ticket' ${event} ${event2} ${style}>$ret</span>";

	return $output;
}

$rtDate = gmdate( 'YmdHis', @filemtime( __FILE__ ) );
$wgCacheEpoch = max( $wgCacheEpoch, $rtDate );

Hack for mysql works great thanks! --198.187.27.5 18:18, 17 June 2011 (UTC)Reply

Callback issue with 1.17.x

edit

I'm using PHP 5.3.2.

I ran into the following error when trying to submit a wiki entry with the rt tag: Tag hook for rt is not callable.

It turns out in RT_body.php, PHP didn't like the way the callback function was passed. Here is the relevant code:

public static function registerHook( &$parser ) {
        #### $parser->setHook( 'rt', array( 'RT::render' ) );
        $parser->setHook( 'rt', array( 'RT', 'render' ) );
        return true;
}

I commented out the offending code. After this change everything worked as expected.

Updated for mysql on 1.23

edit

The newer version of this extension splits RT.php into two files. The RT.php for mysql above seems not to work on Mediawiki 1.23, so here is an update:

https://github.com/erikvw/RT

git clone https://github.com/erikvw/RT

--Erikvw (talk) 19:08, 7 June 2014 (UTC)Reply

This extension seems to be unmaintained. Perhaps it is an idea to take over maintainership and apply for commit access to the WMF repo. Cheers --[[kgh]] (talk) 19:19, 7 June 2014 (UTC)Reply

PHP 7 compatible MySQL hack

edit
--- C:/temp/RT/mediawiki-extensions-RT-master/RT_body.php	Sat Apr 06 15:27:49 2019
+++ C:/src/rt/RT_body.php	Fri May 17 14:02:06 2019
@@ -15,7 +15,10 @@
 
 		global $wgRequestTracker_Cachepage,
 			$wgRequestTracker_Active,
-			$wgRequestTracker_DBconn,
+			$wgRequestTracker_DBuser,
+			$wgRequestTracker_DBpasswd,
+			$wgRequestTracker_DBdbname,
+			$wgRequestTracker_DBhost,
 			$wgRequestTracker_Sortable,
 			$wgRequestTracker_TIMEFORMAT_LASTUPDATED,
 			$wgRequestTracker_TIMEFORMAT_LASTUPDATED2,
@@ -42,7 +45,7 @@
 		// Try and connect to the database if we are active
 		if ( $wgRequestTracker_Active ) {
 			global $wgUser;
-			$dbh = pg_connect( $wgRequestTracker_DBconn );
+			$dbh = mysqli_connect( $wgRequestTracker_DBhost, $wgRequestTracker_DBuser,$wgRequestTracker_DBpasswd, $wgRequestTracker_DBdbname);
 			if ( $dbh == false ) {
 				wfDebug( "DB connection error\n" );
 				wfDebug( "Connection string: $wgRequestTracker_DBconn\n" );
@@ -53,7 +56,7 @@
 				$found = array();
 				if ( preg_match ( '/((-?\d\d?):(\d\d))/', $tz, $found ) ) {
 					if ( $found[3] === '00' ) {
-						pg_query( "SET TIME ZONE $found[2]" );
+						mysqli_query( $dbh, "SET TIME ZONE $found[2]");
 					}
 					else {
 						print( "SET TIME ZONE INTERVAL '$found[1]' HOUR TO MINUTE" );
@@ -73,27 +76,24 @@
 
 		// Standard info we gather
 		$TZ = "AT TIME ZONE 'GMT'";
-		$ticketinfo = 't.id, t.subject, t.priority, INITCAP(t.status) AS status, q.name AS queue,'
-			. ' COALESCE(u.realname, u.name) AS owner,'
-			. ' u.name AS username,'
-			. ' COALESCE(u2.realname, u2.name) AS creator,'
-			. " TO_CHAR(t.lastupdated $TZ, '$wgRequestTracker_TIMEFORMAT_LASTUPDATED'::text) AS lastupdated,"
-			. " TO_CHAR(t.lastupdated $TZ, '$wgRequestTracker_TIMEFORMAT_LASTUPDATED2'::text) AS lastupdated2,"
-			. " TO_CHAR(now() $TZ, '$wgRequestTracker_TIMEFORMAT_NOW'::text) AS nowtime,"
-			. " TO_CHAR(t.created $TZ, '$wgRequestTracker_TIMEFORMAT_CREATED'::text) AS created,"
-			. " TO_CHAR(t.created $TZ, '$wgRequestTracker_TIMEFORMAT_CREATED2'::text) AS created2,"
-			. " TO_CHAR(t.resolved $TZ, '$wgRequestTracker_TIMEFORMAT_RESOLVED'::text) AS resolved,"
-			. " TO_CHAR(t.resolved $TZ, '$wgRequestTracker_TIMEFORMAT_RESOLVED2'::text) AS resolved2,"
-			. " ROUND(EXTRACT('epoch' FROM t.lastupdated $TZ)) AS lastupdated_epoch,"
-			. " ROUND(EXTRACT('epoch' FROM t.created $TZ)) AS created_epoch,"
-			. " ROUND(EXTRACT('epoch' FROM t.resolved $TZ)) AS resolved_epoch,"
-			. "	CASE WHEN (now() $TZ - t.created) <= '1 second'::interval THEN '1 second' ELSE"
-			. " CASE WHEN (now() $TZ - t.created) <= '2 minute'::interval THEN EXTRACT(seconds FROM now() $TZ - t.created) || ' seconds' ELSE"
-			. " CASE WHEN (now() $TZ - t.created) <= '2 hour'::interval THEN EXTRACT(minutes FROM now() $TZ - t.created) || ' minutes' ELSE"
-			. " CASE WHEN (now() $TZ - t.created) <= '2 day'::interval THEN EXTRACT(hours FROM now() $TZ - t.created) || ' hours' ELSE"
-			. " EXTRACT(days FROM now() $TZ - t.created) || ' days' END END END END AS age";
+		$ticketinfo = 't.id, t.subject, t.priority, concat(ucase(left(t.status,1)),substring(t.status,2)) AS status, q.name AS queue,'
+		. ' COALESCE(u.realname, u.name) AS owner,'
+		. ' u.name AS username,'
+		. ' COALESCE(u2.realname, u2.name) AS creator,'
+		. " date_format(t.lastupdated, '$wgRequestTracker_TIMEFORMAT_LASTUPDATED') AS lastupdated,"
+		. " date_format(t.lastupdated, '$wgRequestTracker_TIMEFORMAT_LASTUPDATED2') AS lastupdated2,"
+		. " date_format(now(), '$wgRequestTracker_TIMEFORMAT_NOW') AS nowtime,"
+		. " date_format(t.created, '$wgRequestTracker_TIMEFORMAT_CREATED') AS created,"
+		. " date_format(t.created, '$wgRequestTracker_TIMEFORMAT_CREATED2') AS created2,"
+		. " date_format(t.resolved, '$wgRequestTracker_TIMEFORMAT_RESOLVED') AS resolved,"
+		. " date_format(t.resolved, '$wgRequestTracker_TIMEFORMAT_RESOLVED2') AS resolved2,"
+		. " datediff(now(),t.created) AS age";
+
+        $ticketquery = "SELECT $ticketinfo FROM Tickets t"
+                . ' JOIN Users u ON t.owner = u.id'
+                . ' JOIN Users u2 ON t.creator = u2.id'
+				. ' JOIN Queues q ON t.queue = q.id';
 
-		$ticketquery = "SELECT $ticketinfo\nFROM tickets t, queues q, users u, users u2";
 		$whereclause = "WHERE t.queue = q.id\nAND t.owner = u.id\nAND t.creator = u2.id";
 
 		// If just a single number, treat it as <rt>#</rt>
@@ -106,11 +106,13 @@
 		// Look up a single ticket number
 		if ( $ticketnum ) {
 			$SQL = "$ticketquery $whereclause\nAND t.id = $ticketnum";
-			$res = pg_query( $dbh, $SQL );
+			$res = mysqli_query( $dbh, $SQL );
 			if ( !$res ) {
+				echo $SQL;
+				echo mysqli_error($dbh);
 				die ( wfMessage( 'rt-badquery' )->escaped() );
 			}
-			$info = pg_fetch_array( $res );
+			$info = mysqli_fetch_array( $res );
 			if ( !$info ) {
 				return "<span class='rt-nosuchticket'>RT #$ticketnum</span>";
 			}
@@ -221,32 +223,39 @@
 			$whereclause .= "\nAND $searchowner";
 		}
 
-		// Allow use of custom fields
-		$searchcustom = '';
-		if ( array_key_exists('custom', $args ) ) {
-			$searchcustom = trim( $args['custom'] );
-			$cfargs = trim( strtolower( $args['custom'] ) );
-			$ticketquery .= ', customfields cf, objectcustomfieldvalues ov';
-			$whereclause .= "\nAND ov.objectid = t.id\nAND ov.customfield=cf.id\nAND ov.disabled = 0";
-			$whereclause .= "\nAND LOWER(cf.name) IN (";
-			foreach ( preg_split( '/\s*,\s*/', $cfargs ) as $word ) {
-				$word = trim( $word );
-				if ( !preg_match( '/^[\w \.-]+$/', $word ) ) {
-					die ( wfmessage ( 'rt-badcfield', $word )->escaped() );
-				}
-				$whereclause .= "'$word',";
-				$ticketquery = preg_replace( '/COALESCE/', "\nov.content AS custom, COALESCE", $ticketquery);
-			}
-			$whereclause = preg_replace( '/.$/', ')', $whereclause );
-		}
+		// // Allow use of custom fields
+		// $searchcustom = '';
+		// if ( array_key_exists('custom', $args ) ) {
+		// 	$searchcustom = trim( $args['custom'] );
+		// 	$cfargs = trim( strtolower( $args['custom'] ) );
+		// 	$ticketquery .= ', customfields cf, objectcustomfieldvalues ov';
+		// 	$whereclause .= "\nAND ov.objectid = t.id\nAND ov.customfield=cf.id\nAND ov.disabled = 0";
+		// 	$whereclause .= "\nAND LOWER(cf.name) IN (";
+		// 	foreach ( preg_split( '/\s*,\s*/', $cfargs ) as $word ) {
+		// 		$word = trim( $word );
+		// 		if ( !preg_match( '/^[\w \.-]+$/', $word ) ) {
+		// 			die ( wfmessage ( 'rt-badcfield', $word )->escaped() );
+		// 		}
+		// 		$whereclause .= "'$word',";
+		// 		$ticketquery = preg_replace( '/COALESCE/', "\nov.content AS custom, COALESCE", $ticketquery);
+		// 	}
+		// 	$whereclause = preg_replace( '/.$/', ')', $whereclause );
+		// }
 
 		// Build and run the final query
 		$SQL = "$ticketquery $whereclause $orderby $limit";
-		$res = pg_query( $dbh, $SQL );
+		$res = mysqli_query( $dbh, $SQL );
 		if ( !$res ) {
+			echo $SQL;
+			echo mysqli_error($dbh);
 			die ( wfmessage( 'rt-badquery' )->escaped() );
 		}
-		$info = pg_fetch_all( $res );
+		$info = mysqli_fetch_all( $res, MYSQLI_ASSOC );
+		// Or try stuff below if you have old PHP without mysqli_fetch_all
+		// while ($row = mysqli_fetch_array($res, MYSQLI_ASSOC)) {
+		// 	$info[] = $row;
+		// 		$i++;
+		//   }
 		if ( !$info ) {
 			$msg = wfmessage( 'rt-nomatches' )->escaped();
 			return "<table class='rt-table-empty' border='1'><tr><th>$msg</th><tr></table>";
Return to "RT" page.