Extension talk:RT

From mediawiki.org
Latest comment: 9 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>";