Index: trunk/extensions/RT/RT.i18n.php |
— | — | @@ -0,0 +1,29 @@ |
| 2 | +<?php |
| 3 | +/** |
| 4 | + * Internationalisation file for the RT extension. |
| 5 | + * |
| 6 | + * @ingroup Extensions |
| 7 | + */ |
| 8 | + |
| 9 | +/** |
| 10 | + * Get all extension messages |
| 11 | + * |
| 12 | + * @return array |
| 13 | + */ |
| 14 | + |
| 15 | +$messages = array(); |
| 16 | + |
| 17 | +/** English |
| 18 | + * Greg Sabino Mullane <greg@endpoint.com> |
| 19 | + */ |
| 20 | +$messages['en'] = array( |
| 21 | + 'rt-desc' => 'Fancy interface to RT (Request Tracker)', |
| 22 | + 'rt-inactive' => 'The RT extension is not active', |
| 23 | + 'rt-badquery' => 'The RT extension encountered an error when talking to the RT database', |
| 24 | + 'rt-badlimit' => 'Invalid LIMIT (l) arg: must be a number. You tried: <b>$1</b>', |
| 25 | + 'rt-badorderby' => 'Invalid ORDER BY (ob) arg: must be a standard field (see documentation). You tried: <b>$1</b>', |
| 26 | + 'rt-badstatus' => 'Invalid status (s) arg: must be a standard field (see documentation). You tried: <b>$1</b>', |
| 27 | + 'rt-badqueue' => 'Invalid queue (q) arg: must be a simple word. You tried: <b>$1</b>', |
| 28 | + 'rt-badowner' => 'Invalid owner (o) arg: must be a valud username. You tried: <b>$1</b>', |
| 29 | + 'rt-nomatches' => 'No matching RT tickets were found', |
| 30 | +); |
Property changes on: trunk/extensions/RT/RT.i18n.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 31 | + native |
Index: trunk/extensions/RT/RT.php |
— | — | @@ -0,0 +1,487 @@ |
| 2 | +<?php |
| 3 | +/** |
| 4 | + * RT (Request Tracker) extension for MediaWiki |
| 5 | + * |
| 6 | + * @file |
| 7 | + * @ingroup Extensions |
| 8 | + * |
| 9 | + * Usage: Add the following three lines to LocalSettings.php: |
| 10 | + * require_once( "$IP/extensions/RT/RT.php" ); |
| 11 | + * $wgRequestTracker_URL = 'https://rt.example.com/Ticket/Display.html?id'; |
| 12 | + * $wgRequestTracker_DBconn = 'user=rt dbname=rt'; |
| 13 | + * |
| 14 | + * For other options, please see the complete documentation |
| 15 | + * |
| 16 | + * @author Greg Sabino Mullane <greg@endpoint.com> |
| 17 | + * @license MIT <http://www.opensource.org/licenses/mit-license.php> |
| 18 | + * @version 1.8 |
| 19 | + * @link http://www.mediawiki.org/wiki/Extension:RT |
| 20 | + */ |
| 21 | + |
| 22 | +$rt_uri = 'http://www.mediawiki.org/wiki/Extension:RT'; |
| 23 | + |
| 24 | +## Default values: Override in LocalSettings.php, not here! |
| 25 | +$wgRequestTracker_URL = 'http://rt.example.com/Ticket/Display.html?id'; |
| 26 | +$wgRequestTracker_DBconn = 'user=rt dbname=rt'; |
| 27 | +$wgRequestTracker_Formats = array(); |
| 28 | +$wgRequestTracker_Cachepage = 0; |
| 29 | +$wgRequestTracker_Useballoons = 1; |
| 30 | +$wgRequestTracker_Active = 1; |
| 31 | + |
| 32 | +## Time formatting |
| 33 | +## Example formats: |
| 34 | +## FMHH:MI AM FMMon DD, YYYY => 2:42 PM Jan 23, 2009 |
| 35 | +## HH:MI FMMonth DD, YYYY => 14:42 January 23, 2009 |
| 36 | +## YYYY/MM/DD => 2009/01/23 |
| 37 | +## For a more complete list of possibilities, please visit: |
| 38 | +## http://www.postgresql.org/docs/current/interactive/functions-formatting.html |
| 39 | +$wgRequestTracker_TIMEFORMAT_LASTUPDATED = 'FMHH:MI AM FMMonth DD, YYYY'; |
| 40 | +$wgRequestTracker_TIMEFORMAT_LASTUPDATED2 = 'FMMonth DD, YYYY'; |
| 41 | +$wgRequestTracker_TIMEFORMAT_CREATED = 'FMHH:MI AM FMMonth DD, YYYY'; |
| 42 | +$wgRequestTracker_TIMEFORMAT_CREATED2 = 'FMMonth DD, YYYY'; |
| 43 | +$wgRequestTracker_TIMEFORMAT_RESOLVED = 'FMHH:MI AM FMMonth DD, YYYY'; |
| 44 | +$wgRequestTracker_TIMEFORMAT_RESOLVED2 = 'FMMonth DD, YYYY'; |
| 45 | +$wgRequestTracker_TIMEFORMAT_NOW = 'FMHH:MI AM FMMonth DD, YYYY'; |
| 46 | + |
| 47 | +// Ensure nothing is done unless run via MediaWiki |
| 48 | +if ( !defined( 'MEDIAWIKI' ) ) { |
| 49 | + echo( "This is an extension to the MediaWiki package and cannot be run standalone.\n" ); |
| 50 | + echo( "Please visit $rt_uri\n" ); |
| 51 | + die( -1 ); |
| 52 | +} |
| 53 | + |
| 54 | +// Credits for Special:Version |
| 55 | +$wgExtensionCredits['parserhook'][] = array( |
| 56 | + 'name' => 'RT', |
| 57 | + 'version' => '1.8', |
| 58 | + 'author' => array( 'Greg Sabino Mullane' ), |
| 59 | + 'description' => 'Fancy interface to RT (Request Tracker)', |
| 60 | + 'descriptionmsg' => 'rt-desc', |
| 61 | + 'url' => $rt_uri, |
| 62 | + ); |
| 63 | + |
| 64 | +// Pull in the Internationalization file |
| 65 | +$wgExtensionMessagesFiles['RT'] = dirname( __FILE__ ) . '/RT.i18n.php'; |
| 66 | + |
| 67 | +// Use a hook to enable control of parsing <rt>...</rt> content |
| 68 | +$wgExtensionFunctions[] = 'efRT_Setup'; |
| 69 | +function efRT_Setup() { |
| 70 | + |
| 71 | + global $wgParser, $wgUploadDirectory, $wgCommandLineMode; |
| 72 | + |
| 73 | + wfLoadExtensionMessages( 'RT' ); |
| 74 | + |
| 75 | + if ( $wgCommandLineMode ) { |
| 76 | + return true; |
| 77 | + } |
| 78 | + |
| 79 | + $wgParser->setHook( 'rt', 'rtRender' ); |
| 80 | + |
| 81 | + return true; |
| 82 | +} |
| 83 | + |
| 84 | + |
| 85 | +// This is called to process <rt>...</rt> within a page |
| 86 | +function rtRender( $input, $args=array(), $parser=null ) { |
| 87 | + |
| 88 | + global $wgRequestTracker_Cachepage, $wgRequestTracker_Active, $wgRequestTracker_DBconn, |
| 89 | + $wgRequestTracker_TIMEFORMAT_LASTUPDATED, |
| 90 | + $wgRequestTracker_TIMEFORMAT_LASTUPDATED2, |
| 91 | + $wgRequestTracker_TIMEFORMAT_CREATED, |
| 92 | + $wgRequestTracker_TIMEFORMAT_CREATED2, |
| 93 | + $wgRequestTracker_TIMEFORMAT_RESOLVED, |
| 94 | + $wgRequestTracker_TIMEFORMAT_RESOLVED2, |
| 95 | + $wgRequestTracker_TIMEFORMAT_NOW; |
| 96 | + |
| 97 | + // Grab the number if one was given between the <tr> tags |
| 98 | + $ticketnum = 0; |
| 99 | + $matches = array(); |
| 100 | + if ( preg_match( '/^\s*(\d+)\s*$/', $input, $matches ) ) { |
| 101 | + $ticketnum = $matches[0]; |
| 102 | + } |
| 103 | + |
| 104 | + // Disable all caching unless told not to |
| 105 | + if ( !$wgRequestTracker_Cachepage ) { |
| 106 | + $parser->disableCache(); |
| 107 | + } |
| 108 | + |
| 109 | + // Try and connect to the database if we are active |
| 110 | + if ( $wgRequestTracker_Active ) { |
| 111 | + global $wgUser; |
| 112 | + $dbh = pg_connect( $wgRequestTracker_DBconn ); |
| 113 | + if ( $dbh == false ) { |
| 114 | + wfDebug( "DB connection error\n" ); |
| 115 | + wfDebug( "Connection string: $wgRequestTracker_DBconn\n" ); |
| 116 | + $wgRequestTracker_Active = 0; |
| 117 | + } |
| 118 | + $tz = $wgUser->getOption( 'timecorrection' ); |
| 119 | + if ( $tz ) { |
| 120 | + $found = array(); |
| 121 | + if ( preg_match ( '/((-?\d\d?):(\d\d))/', $tz, $found ) ) { |
| 122 | + if ( $found[3] === '00' ) { |
| 123 | + pg_query( "SET TIME ZONE $found[2]" ); |
| 124 | + } |
| 125 | + else { |
| 126 | + print( "SET TIME ZONE INTERVAL '$found[1]' HOUR TO MINUTE" ); |
| 127 | + } |
| 128 | + } |
| 129 | + } |
| 130 | + } |
| 131 | + |
| 132 | + // If we are not 'active', we leave right away, with minimal output |
| 133 | + if ( !$wgRequestTracker_Active ) { |
| 134 | + if ( $ticketnum ) { |
| 135 | + return "<span class='rt-ticket-inactive'>RT #$ticketnum</span>"; |
| 136 | + } |
| 137 | + $msg = wfMsg( 'rt-inactive' ); |
| 138 | + return "<table class='rt-table-inactive' border='1'><tr><td>$msg</td></tr></table>"; |
| 139 | + } |
| 140 | + |
| 141 | + // Standard info we gather |
| 142 | + $TZ = "AT TIME ZONE 'GMT'"; |
| 143 | + $ticketinfo = 't.id, t.subject, t.priority, INITCAP(t.status) AS status, q.name AS queue,' |
| 144 | + . ' COALESCE(u.realname, u.name) AS owner,' |
| 145 | + . ' u.name AS username,' |
| 146 | + . ' COALESCE(u2.realname, u2.name) AS creator,' |
| 147 | + . " TO_CHAR(t.lastupdated $TZ, '$wgRequestTracker_TIMEFORMAT_LASTUPDATED'::text) AS lastupdated," |
| 148 | + . " TO_CHAR(t.lastupdated $TZ, '$wgRequestTracker_TIMEFORMAT_LASTUPDATED2'::text) AS lastupdated2," |
| 149 | + . " TO_CHAR(now() $TZ, '$wgRequestTracker_TIMEFORMAT_NOW'::text) AS nowtime," |
| 150 | + . " TO_CHAR(t.created $TZ, '$wgRequestTracker_TIMEFORMAT_CREATED'::text) AS created," |
| 151 | + . " TO_CHAR(t.created $TZ, '$wgRequestTracker_TIMEFORMAT_CREATED2'::text) AS created2," |
| 152 | + . " TO_CHAR(t.resolved $TZ, '$wgRequestTracker_TIMEFORMAT_RESOLVED'::text) AS resolved," |
| 153 | + . " TO_CHAR(t.resolved $TZ, '$wgRequestTracker_TIMEFORMAT_RESOLVED2'::text) AS resolved2," |
| 154 | + . " CASE WHEN (now() $TZ - t.created) <= '1 second'::interval THEN '1 second' ELSE" |
| 155 | + . " CASE WHEN (now() $TZ - t.created) <= '2 minute'::interval THEN EXTRACT(seconds FROM now() $TZ - t.created) || ' seconds' ELSE" |
| 156 | + . " CASE WHEN (now() $TZ - t.created) <= '2 hour'::interval THEN EXTRACT(minutes FROM now() $TZ - t.created) || ' minutes' ELSE" |
| 157 | + . " CASE WHEN (now() $TZ - t.created) <= '2 day'::interval THEN EXTRACT(hours FROM now() $TZ - t.created) || ' hours' ELSE" |
| 158 | + . " EXTRACT(days FROM now() $TZ - t.created) || ' days' END END END END AS age"; |
| 159 | + |
| 160 | + // The standard query |
| 161 | + $ticketquery = "SELECT $ticketinfo FROM tickets t" |
| 162 | + . ' JOIN users u ON t.owner = u.id' |
| 163 | + . ' JOIN users u2 ON t.creator = u2.id' |
| 164 | + . ' JOIN queues q ON t.queue = q.id'; |
| 165 | + |
| 166 | + // If just a single number, treat it as <rt>#</rt> |
| 167 | + if ( 1 === count( $args ) ) { |
| 168 | + if ( preg_match( '/^\d+$/', key($args) ) ) { |
| 169 | + $ticketnum = key($args); |
| 170 | + } |
| 171 | + } |
| 172 | + |
| 173 | + // Look up a single ticket number |
| 174 | + if ( $ticketnum ) { |
| 175 | + $SQL = "$ticketquery AND t.id = $ticketnum"; |
| 176 | + $res = pg_query( $dbh, $SQL ); |
| 177 | + if ( !$res ) { |
| 178 | + die ( wfMsg( 'rt-badquery' ) ); |
| 179 | + } |
| 180 | + $info = pg_fetch_array( $res ); |
| 181 | + if ( !$info ) { |
| 182 | + return "<span class='rt-nosuchticket'>RT #$ticketnum</span>"; |
| 183 | + } |
| 184 | + return rtFancyLink( $info, $args, $parser, 0 ); |
| 185 | + } |
| 186 | + |
| 187 | + // Add in a LIMIT clause if l=xx was used |
| 188 | + $limit = ''; |
| 189 | + if ( array_key_exists( 'l', $args ) ) { |
| 190 | + $limit = trim( $args['l'] ); |
| 191 | + if ( !preg_match( '/^ *\d+ *$/', $limit ) ) { |
| 192 | + die ( wfMsg ( 'rt-badlimit', $limit ) ); |
| 193 | + } |
| 194 | + $limit = " LIMIT $limit"; |
| 195 | + } |
| 196 | + |
| 197 | + // Change the default ORDER BY clause if ob=xx was used |
| 198 | + $orderby = 'ORDER BY t.lastupdated DESC, t.id'; |
| 199 | + $valid_orderby = array |
| 200 | + ( |
| 201 | + 'id' => 't.id', |
| 202 | + 'subject' => 't.subject', |
| 203 | + 'priority' => 't.priority', |
| 204 | + 'status' => 't.status', |
| 205 | + 'queue' => 'q.name', |
| 206 | + 'owner' => 'COALESCE(u.realname, u.name)', |
| 207 | + 'creator' => 'COALESCE(u2.realname, u2.name)', |
| 208 | + 'lastupdated' => 't.lastupdated', |
| 209 | + 'created' => 't.created', |
| 210 | + 'resolved' => 't.resolved', |
| 211 | + ); |
| 212 | + if ( array_key_exists( 'ob', $args ) ) { |
| 213 | + $orderby = 'ORDER BY'; |
| 214 | + $orderbyargs = trim( strtolower( $args['ob'] ) ); |
| 215 | + foreach ( preg_split( '/\s*,\s*/', $orderbyargs ) as $word ) { |
| 216 | + $oldlen = strlen( $word ); |
| 217 | + $word = ltrim( $word, '!' ); |
| 218 | + $mod = $oldlen !== strlen( $word ) ? ' DESC' : ''; |
| 219 | + if ( !preg_match( '/^\w+$/', $word ) ) { |
| 220 | + die ( wfMsg ( 'rt-badorderby', $word ) ); |
| 221 | + } |
| 222 | + if ( array_key_exists( $word, $valid_orderby ) ) { |
| 223 | + $word = $valid_orderby[$word]; |
| 224 | + } |
| 225 | + else if ( !preg_match ('/^\d+$/', $word ) ) { |
| 226 | + die ( wfMsg ( 'rt-badorderby', $word ) ); |
| 227 | + } |
| 228 | + $orderby .= " $word$mod,"; |
| 229 | + } |
| 230 | + $orderby = rtrim( $orderby, ',' ); |
| 231 | + } |
| 232 | + |
| 233 | + // Determine what status to use. Default is new and open: |
| 234 | + $searchstatus = "AND t.status IN ('new','open')"; |
| 235 | + $valid_status = array( 'new', 'open', 'resolved', 'deleted', 'stalled', 'rejected' ); |
| 236 | + if ( array_key_exists( 's', $args ) ) { |
| 237 | + $statusargs = trim( strtolower( $args['s'] ) ); |
| 238 | + if ( $statusargs === 'all' ) { |
| 239 | + $searchstatus = ''; |
| 240 | + } |
| 241 | + else { |
| 242 | + $searchstatus = 'AND t.status IN ('; |
| 243 | + foreach ( preg_split( '/\s*,\s*/', $statusargs ) as $word ) { |
| 244 | + if ( !in_array( $word, $valid_status ) ) { |
| 245 | + die ( wfMsg ( 'rt-badstatus', $word ) ); |
| 246 | + } |
| 247 | + $searchstatus .= "'$word',"; |
| 248 | + } |
| 249 | + $searchstatus = preg_replace( '/.$/', ')', $searchstatus ); |
| 250 | + } |
| 251 | + } |
| 252 | + |
| 253 | + // See if we are limiting to one or more queues |
| 254 | + $searchq = ''; |
| 255 | + if ( array_key_exists('q', $args ) ) { |
| 256 | + $qargs = trim( strtolower( $args['q'] ) ); |
| 257 | + $searchq = 'AND LOWER(q.name) IN ('; |
| 258 | + foreach ( preg_split( '/\s*,\s*/', $qargs ) as $word ) { |
| 259 | + $word = trim( $word ); |
| 260 | + if ( !preg_match( '/^[\w \.-]+$/', $word ) ) { |
| 261 | + die ( wfMsg ( 'rt-badqueue', $word ) ); |
| 262 | + } |
| 263 | + $searchq .= "'$word',"; |
| 264 | + } |
| 265 | + $searchq = preg_replace( '/.$/', ')', $searchq ); |
| 266 | + } |
| 267 | + |
| 268 | + // See if we are limiting to one or more owners |
| 269 | + $searchowner = ''; |
| 270 | + if ( array_key_exists('o', $args ) ) { |
| 271 | + $oargs = trim( strtolower( $args['o'] ) ); |
| 272 | + $searchowner = 'AND LOWER(u.name) IN ('; |
| 273 | + foreach ( preg_split( '/\s*,\s*/', $oargs ) as $word ) { |
| 274 | + $word = trim( $word ); |
| 275 | + if ( !preg_match( '/^[\w\@\.\-\:\/]+$/', $word ) ) { |
| 276 | + die ( wfMsg ( 'rt-badowner', $word ) ); |
| 277 | + } |
| 278 | + $searchowner .= "'$word',"; |
| 279 | + } |
| 280 | + $searchowner = preg_replace( '/.$/', ')', $searchowner ); |
| 281 | + } |
| 282 | + |
| 283 | + // Build and run the final query |
| 284 | + $SQL = "$ticketquery $searchq $searchowner $searchstatus $orderby $limit"; |
| 285 | + $res = pg_query( $dbh, $SQL ); |
| 286 | + if ( !$res ) { |
| 287 | + die ( wfMsg( 'rt-badquery' ) ); |
| 288 | + } |
| 289 | + $info = pg_fetch_all( $res ); |
| 290 | + if ( !$info ) { |
| 291 | + $msg = wfMsg( 'rt-nomatches' ); |
| 292 | + return "<table class='rt-table-empty' border='1'><tr><th>$msg</th><tr></table>"; |
| 293 | + } |
| 294 | + |
| 295 | + // Figure out what columns to show |
| 296 | + // Anything specifically requested is shown |
| 297 | + // Everything else is either on or off by default, but can be overidden |
| 298 | + $output = ''; |
| 299 | + |
| 300 | + // The queue: show by default unless searching a single queue |
| 301 | + $showqueue = 1; |
| 302 | + if ( array_key_exists('noqueue', $args ) |
| 303 | + || ($searchq |
| 304 | + && false === strpos( $searchq, ',' ) |
| 305 | + && !array_key_exists( 'queue', $args ) ) ) { |
| 306 | + $showqueue = 0; |
| 307 | + } |
| 308 | + |
| 309 | + // The owner: show by default unless searching a single owner |
| 310 | + $showowner = 1; |
| 311 | + if ( array_key_exists( 'noowner', $args ) |
| 312 | + || ( $searchowner |
| 313 | + && false === strpos( $searchowner, ',' ) |
| 314 | + && !array_key_exists( 'owner', $args ) ) ) { |
| 315 | + $showowner = 0; |
| 316 | + } |
| 317 | + |
| 318 | + // The status: show by default unless searching a single status |
| 319 | + $showstatus = 1; |
| 320 | + if ( array_key_exists( 'nostatus', $args ) |
| 321 | + || ( false === strpos($searchstatus, ',' ) |
| 322 | + && !array_key_exists( 'status', $args ) ) ) { |
| 323 | + $showstatus = 0; |
| 324 | + } |
| 325 | + |
| 326 | + // Things we always show unless told not to: |
| 327 | + $showsubject = ! array_key_exists( 'nosubject', $args ); |
| 328 | + $showupdated = ! array_key_exists( 'noupdated', $args ); |
| 329 | + $showticket = ! array_key_exists( 'noticket', $args ); |
| 330 | + |
| 331 | + // Things we don't show unless asked to: |
| 332 | + $showpriority = array_key_exists( 'priority', $args ); |
| 333 | + $showupdated2 = array_key_exists( 'updated2', $args ); |
| 334 | + $showcreated = array_key_exists( 'created', $args ); |
| 335 | + $showcreated2 = array_key_exists( 'created2', $args ); |
| 336 | + $showresolved = array_key_exists( 'resolved', $args ); |
| 337 | + $showresolved2 = array_key_exists( 'resolved2', $args ); |
| 338 | + $showage = array_key_exists( 'age', $args ); |
| 339 | + |
| 340 | + // Unless 'tablerows' has been set, output the table and header tags |
| 341 | + if ( !array_key_exists( 'tablerows',$args ) ) { |
| 342 | + |
| 343 | + $output = "<table class='rt-table' border='1'><tr>"; |
| 344 | + |
| 345 | + if ( $showticket ) { $output .= '<th>Ticket</th>'; } |
| 346 | + if ( $showqueue ) { $output .= '<th>Queue</th>'; } |
| 347 | + if ( $showsubject ) { $output .= '<th>Subject</th>'; } |
| 348 | + if ( $showstatus ) { $output .= '<th>Status</th>'; } |
| 349 | + if ( $showpriority ) { $output .= '<th>Priority</th>'; } |
| 350 | + if ( $showowner ) { $output .= '<th>Owner</th>'; } |
| 351 | + if ( $showupdated ) { $output .= '<th>Last updated</th>'; } |
| 352 | + if ( $showupdated2 ) { $output .= '<th>Last updated</th>'; } |
| 353 | + if ( $showcreated ) { $output .= '<th>Created</th>'; } |
| 354 | + if ( $showcreated2 ) { $output .= '<th>Created</th>'; } |
| 355 | + if ( $showresolved ) { $output .= '<th>Resolved</th>'; } |
| 356 | + if ( $showresolved2 ) { $output .= '<th>Resolved</th>'; } |
| 357 | + if ( $showage ) { $output .= '<th>Age</th>'; } |
| 358 | + |
| 359 | + $output .= '</tr>'; |
| 360 | + } |
| 361 | + |
| 362 | + foreach ( $info as $row ) { |
| 363 | + |
| 364 | + if ( $showticket ) { |
| 365 | + $id = rtFancyLink( $row, $args, $parser, 1 ); |
| 366 | + $output .= "<td style='white-space: nowrap'>$id</td>"; |
| 367 | + } |
| 368 | + if ( $showqueue ) { $output .= '<td>' . htmlspecialchars( $row['queue'] ) . '</td>'; } |
| 369 | + if ( $showsubject ) { $output .= '<td>' . htmlspecialchars( $row['subject'] ) . '</td>'; } |
| 370 | + if ( $showstatus ) { $output .= '<td>' . htmlspecialchars( $row['status'] ) . '</td>'; } |
| 371 | + if ( $showpriority ) { $output .= '<td>' . htmlspecialchars( $row['priority'] ). '</td>'; } |
| 372 | + if ( $showowner ) { $output .= '<td>' . htmlspecialchars( $row['owner'] ) . '</td>'; } |
| 373 | + if ( $showupdated ) { $output .= '<td>' . $row['lastupdated'] . '</td>'; } |
| 374 | + if ( $showupdated2 ) { $output .= '<td>' . $row['lastupdated2'] . '</td>'; } |
| 375 | + if ( $showcreated ) { $output .= '<td>' . $row['created'] . '</td>'; } |
| 376 | + if ( $showcreated2 ) { $output .= '<td>' . $row['created2'] . '</td>'; } |
| 377 | + if ( $showresolved ) { $output .= '<td>' . $row['resolved'] . '</td>'; } |
| 378 | + if ( $showresolved2 ) { $output .= '<td>' . $row['resolved2'] . '</td>'; } |
| 379 | + if ( $showage ) { $output .= '<td>' . $row['age'] . '</td>'; } |
| 380 | + $output .= '<tr>'; |
| 381 | + } |
| 382 | + |
| 383 | + if ( !array_key_exists( 'tablerows',$args ) ) { |
| 384 | + $output .= '</table>'; |
| 385 | + } |
| 386 | + |
| 387 | + return $output; |
| 388 | +} |
| 389 | + |
| 390 | + |
| 391 | +function rtFancyLink( $row, $args, $parser, $istable ) { |
| 392 | + |
| 393 | + global $wgRequestTracker_URL, $wgRequestTracker_Formats, $wgRequestTracker_Useballoons; |
| 394 | + |
| 395 | + $ticketnum = $row['id']; |
| 396 | + $ret = "[$wgRequestTracker_URL=$ticketnum RT #$ticketnum]"; |
| 397 | + |
| 398 | + ## Check for any custom format args in the rt tag. |
| 399 | + ## If any are found, use that and ignore any other args |
| 400 | + $foundformat = 0; |
| 401 | + foreach ( array_keys( $args ) as $val ) { |
| 402 | + if ( array_key_exists( $val, $wgRequestTracker_Formats ) ) { |
| 403 | + $format = $wgRequestTracker_Formats[$val]; |
| 404 | + foreach ( array_keys( $row ) as $rev ) { |
| 405 | + $format = str_replace( "?$rev?", "$row[$rev]", $format ); |
| 406 | + } |
| 407 | + $ret .= " $format"; |
| 408 | + $foundformat = 1; |
| 409 | + break; |
| 410 | + } |
| 411 | + } |
| 412 | + |
| 413 | + ## Process any column-based args to the rt tag |
| 414 | + if ( !$foundformat and !$istable ) { |
| 415 | + foreach ( array_keys( $args ) as $val ) { |
| 416 | + if ( array_key_exists( $val, $row ) ) { |
| 417 | + $format = $args[$val]; |
| 418 | + if ( false === strpos( $format, '?' ) ) { |
| 419 | + $showname = $val === 'lastupdated' ? 'Last updated' : ucfirst( $val ); |
| 420 | + $ret .= " $showname: $row[$val]"; |
| 421 | + } |
| 422 | + else { |
| 423 | + $ret .= " " . str_replace( '?', $row[$val], $format ); |
| 424 | + } |
| 425 | + } |
| 426 | + } |
| 427 | + } |
| 428 | + |
| 429 | + $ret = $parser->recursiveTagParse( $ret ); |
| 430 | + |
| 431 | + // Not using balloons? Just return the current text |
| 432 | + if ( !$wgRequestTracker_Useballoons || array_key_exists( 'noballoon', $args ) ) { |
| 433 | + return "<span class='rt-ticket-noballoon'>$ret</span>"; |
| 434 | + } |
| 435 | + |
| 436 | + $safesub = preg_replace( '/\"/', '\"', $row['subject'] ); |
| 437 | + $safesub = preg_replace( '/\'/', "\'", $safesub ); |
| 438 | + $safesub = htmlspecialchars( $safesub ); |
| 439 | + |
| 440 | + $safeowner = $row['owner']; |
| 441 | + if ($row['owner'] !== $row['username']) { |
| 442 | + $safeowner .= " ($row[username])"; |
| 443 | + } |
| 444 | + $safeowner = preg_replace( '/\"/', '\"', $safeowner ); |
| 445 | + $safeowner = preg_replace( '/\'/', "\'", $safeowner ); |
| 446 | + $safeowner = htmlspecialchars( $safeowner ); |
| 447 | + |
| 448 | + $safeq = preg_replace( '/\"/', '\"', $row['queue'] ); |
| 449 | + $safeq = preg_replace( '/\'/', "\'", $safeq ); |
| 450 | + $safeq = htmlspecialchars( $safeq ); |
| 451 | + |
| 452 | + $text = "RT #<b>$ticketnum</b>"; |
| 453 | + $text .= "<br />Status: <b>$row[status]</b>"; |
| 454 | + $text .= "<br />Subject: <b>$safesub</b>"; |
| 455 | + $text .= "<br />Owner: <b>$safeowner</b>"; |
| 456 | + $text .= "<br />Queue: <b>$safeq</b>"; |
| 457 | + $text .= "<br />Created: <b>$row[created]</b>"; |
| 458 | + if ( $row['status'] === 'Resolved' ) { |
| 459 | + $text .= "<br />Resolved: <b>$row[resolved]</b>"; |
| 460 | + } |
| 461 | + else { |
| 462 | + $text .= "<br />Last updated: <b>$row[lastupdated]</b>"; |
| 463 | + } |
| 464 | + |
| 465 | + ## Prepare some balloon-tek |
| 466 | + $link = isset( $args['link'] ) ? $args['link'] : ''; |
| 467 | + $target = isset( $args['target'] ) ? $args['target'] : ''; |
| 468 | + $sticky = isset( $args['sticky'] ) ? $args['sticky'] : '0'; |
| 469 | + $width = isset( $args['width'] ) ? $args['width'] : '0'; |
| 470 | + |
| 471 | + $event = isset( $args['click'] ) && $args['click'] && !$link ? 'onclick' : 'onmouseover'; |
| 472 | + $event2 = ''; |
| 473 | + $event = "$event=\"balloon.showTooltip(event,'${text}',${sticky},${width})\""; |
| 474 | + |
| 475 | + if ( preg_match( '/onclick/',$event ) && $args['hover'] ) { |
| 476 | + $event2 = " onmouseover=\"balloon.showTooltip(event,'" . $args['hover'] . "',0,${width})\""; |
| 477 | + } |
| 478 | + |
| 479 | + $has_style = isset( $args['style'] ) && $args['style']; |
| 480 | + $style = "style=\"" . ($has_style ? $args['style'] . ";cursor:pointer\"" : "cursor:pointer\""); |
| 481 | + $target = $target ? "target=${target}" : ''; |
| 482 | + $output = "<span class='rt-ticket' ${event} ${event2} ${style}>$ret</span>"; |
| 483 | + |
| 484 | + return $output; |
| 485 | +} |
| 486 | + |
| 487 | +$rtDate = gmdate( 'YmdHis', @filemtime( __FILE__ ) ); |
| 488 | +$wgCacheEpoch = max( $wgCacheEpoch, $rtDate ); |
Property changes on: trunk/extensions/RT/RT.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 489 | + native |
Index: trunk/extensions/RT/README |
— | — | @@ -0,0 +1,127 @@ |
| 2 | +This is the RequestTracker (RT) extension for MediaWiki. It allows |
| 3 | +you to view information from RT tickets on your wiki in real time. |
| 4 | + |
| 5 | +Written by Greg Sabino Mullane, End Point Corporation <greg@endpoint.com> |
| 6 | + |
| 7 | +This is only a quick overview. Full documentation can be found at: |
| 8 | + |
| 9 | +http://www.mediawiki.org/wiki/Extension:RT |
| 10 | + |
| 11 | + |
| 12 | +PREREQUISITES |
| 13 | +============= |
| 14 | + |
| 15 | +This module requires the use of the balloons extension: |
| 16 | + |
| 17 | +http://www.mediawiki.org/wiki/Extension:Balloons |
| 18 | + |
| 19 | + |
| 20 | +INSTALLATION |
| 21 | +============ |
| 22 | + |
| 23 | +To install the RT module, add this line to your LocalSettings.php file: |
| 24 | + |
| 25 | +require_once("$IP/extensions/RT/RT.php"); |
| 26 | + |
| 27 | +You should also add the global configuration values directly below |
| 28 | +this line. Each is described below by example below: |
| 29 | + |
| 30 | +$wgRequestTrackerURL = 'https://rt.endpoint.com/Ticket/Display.html?id'; |
| 31 | + |
| 32 | +This should be the URL to your RT instance, and is used for generating links. |
| 33 | + |
| 34 | + |
| 35 | +$wgRequestTrackerDBconn = 'user=rt dbname=rt'; |
| 36 | + |
| 37 | +This is the connection string to get to your RT database. This is passed directly |
| 38 | +to pg_connect, so you can also use 'port' and 'host' as needed. |
| 39 | + |
| 40 | +$wgRequestTrackerFormats = array |
| 41 | + ( |
| 42 | + 'normal' => "Subject: <b>?subject?</b> Status:?status?", |
| 43 | + ); |
| 44 | + |
| 45 | +This is a list of specific named formats you wish to use. Items to be |
| 46 | +replaced as placeholders should be surrounded by question marks. |
| 47 | + |
| 48 | + |
| 49 | +USAGE |
| 50 | +===== |
| 51 | + |
| 52 | +To use this extension, simply add <rt></rt> tags to the source of |
| 53 | +your wiki. To view specific tickets, simply put the number between |
| 54 | +the tags like so: |
| 55 | + |
| 56 | +<rt>1234</rt> |
| 57 | + |
| 58 | +This will create an external link to ticket 1234. If the ticket does |
| 59 | +not exist, the non-hyperlinked text "RT #1234" will appear. |
| 60 | + |
| 61 | +You can also control what information is displayed. The best way to do |
| 62 | +this is to create a global format by adding entries to the |
| 63 | +$wgRequestTrackerFormats array. Placeholders should be entered as words |
| 64 | +with question marks on both sides of them. Current information that |
| 65 | +can be displayed is: |
| 66 | + |
| 67 | +subject |
| 68 | +status |
| 69 | +lastupdated |
| 70 | +created |
| 71 | +owner |
| 72 | +queue |
| 73 | +priority |
| 74 | +resolved |
| 75 | + |
| 76 | +The RT links also have a balloon mouseover effect that gives all of the current |
| 77 | +information (as of the last page load) for the ticket. This allows you to save |
| 78 | +valuable screen real-estate. |
| 79 | + |
| 80 | +You can also generate a list of tickets, either by owner, or by queue. To view |
| 81 | +a table of all new and open tickets by a specific user, use this syntax: |
| 82 | + |
| 83 | +<rt o="Greg"></rt> |
| 84 | + |
| 85 | +By default, the table will show columns with the RT ticket id, the subject, |
| 86 | +the status, and the queue. Each of these can be turned off by adding noxxx |
| 87 | +to the inside of the rt tag. You can also add in the additional fields |
| 88 | +'update' and 'create'. For example, the following line will show a table with |
| 89 | +all new or open tickets owned by the user 'Greg', with no status, and |
| 90 | +an additional column showing the last update time: |
| 91 | + |
| 92 | +<rt o="greg" nostatus update></rt> |
| 93 | + |
| 94 | +The owner name is case-insensitive. Multiple owners can be separated by commas, |
| 95 | +in which case an additional column named "Owner" will be added. This can be turned |
| 96 | +off by adding "noowner", or forced on by using "owner". |
| 97 | + |
| 98 | +Rows are ordered with the most recently updated tickets at the top. |
| 99 | + |
| 100 | +In a similar way to the 'owner' query, you can search by the name of an RT queue. |
| 101 | +The following line will show all open or new tickets in the sales queue: |
| 102 | + |
| 103 | +<rt q="sales"></rt> |
| 104 | + |
| 105 | +The queue name is case-insensitive. Multiple queues can be separated by commas, |
| 106 | +in which case an additional column named "Queue" will be added. This can be turned |
| 107 | +off by adding "noqueue", or forced on by using "queue". |
| 108 | + |
| 109 | +For table outputs, the status criteria can be changed from the default of |
| 110 | +'new and open' by adding a "c" argument to tell it what criteria to use |
| 111 | +for the status. For example, to view all resolved RT ticket in the queue 'Postgres': |
| 112 | + |
| 113 | +<rt q="postgres" c="resolved"></rt> |
| 114 | + |
| 115 | +To add a limit to any table output, add a "l" argument as the maximum number of |
| 116 | +sorted rows to show. Remember that rows sort with the most recently updated tickets |
| 117 | +at the top. |
| 118 | + |
| 119 | + |
| 120 | +WARNING |
| 121 | +======= |
| 122 | + |
| 123 | +These tags will query the RT database on every page load, so don't put these on |
| 124 | +very highly trafficed page unless your database can handle the load. |
| 125 | + |
| 126 | +Use of these tags is akin to making your entire RT database available to anyone who |
| 127 | +can edit the wiki. Hopefully, those communities have a complete overlap. |
| 128 | + |