]> scripts.mit.edu Git - autoinstallsdev/mediawiki.git/blob - includes/RecentChange.php
MediaWiki 1.15.0
[autoinstallsdev/mediawiki.git] / includes / RecentChange.php
1 <?php
2
3 /**
4  * Utility class for creating new RC entries
5  * mAttribs:
6  *      rc_id           id of the row in the recentchanges table
7  *      rc_timestamp    time the entry was made
8  *      rc_cur_time     timestamp on the cur row
9  *      rc_namespace    namespace #
10  *      rc_title        non-prefixed db key
11  *      rc_type         is new entry, used to determine whether updating is necessary
12  *      rc_minor        is minor
13  *      rc_cur_id       page_id of associated page entry
14  *      rc_user         user id who made the entry
15  *      rc_user_text    user name who made the entry
16  *      rc_comment      edit summary
17  *      rc_this_oldid   rev_id associated with this entry (or zero)
18  *      rc_last_oldid   rev_id associated with the entry before this one (or zero)
19  *      rc_bot          is bot, hidden
20  *      rc_ip           IP address of the user in dotted quad notation
21  *      rc_new          obsolete, use rc_type==RC_NEW
22  *      rc_patrolled    boolean whether or not someone has marked this edit as patrolled
23  *      rc_old_len      integer byte length of the text before the edit
24  *      rc_new_len      the same after the edit
25   *     rc_deleted              partial deletion
26  *      rc_logid                the log_id value for this log entry (or zero)
27  *  rc_log_type         the log type (or null)
28  *      rc_log_action   the log action (or null)
29  *  rc_params           log params
30  *
31  * mExtra:
32  *      prefixedDBkey   prefixed db key, used by external app via msg queue
33  *      lastTimestamp   timestamp of previous entry, used in WHERE clause during update
34  *      lang            the interwiki prefix, automatically set in save()
35  *  oldSize         text size before the change
36  *  newSize         text size after the change
37  *
38  * temporary:           not stored in the database
39  *      notificationtimestamp
40  *      numberofWatchingusers
41  *
42  * @todo document functions and variables
43  */
44 class RecentChange
45 {
46         var $mAttribs = array(), $mExtra = array();
47         var $mTitle = false, $mMovedToTitle = false;
48         var $numberofWatchingusers = 0 ; # Dummy to prevent error message in SpecialRecentchangeslinked
49
50         # Factory methods
51
52         public static function newFromRow( $row ) {
53                 $rc = new RecentChange;
54                 $rc->loadFromRow( $row );
55                 return $rc;
56         }
57
58         public static function newFromCurRow( $row ) {
59                 $rc = new RecentChange;
60                 $rc->loadFromCurRow( $row );
61                 $rc->notificationtimestamp = false;
62                 $rc->numberofWatchingusers = false;
63                 return $rc;
64         }
65
66         /**
67          * Obtain the recent change with a given rc_id value
68          *
69          * @param $rcid rc_id value to retrieve
70          * @return RecentChange
71          */
72         public static function newFromId( $rcid ) {
73                 $dbr = wfGetDB( DB_SLAVE );
74                 $res = $dbr->select( 'recentchanges', '*', array( 'rc_id' => $rcid ), __METHOD__ );
75                 if( $res && $dbr->numRows( $res ) > 0 ) {
76                         $row = $dbr->fetchObject( $res );
77                         $dbr->freeResult( $res );
78                         return self::newFromRow( $row );
79                 } else {
80                         return NULL;
81                 }
82         }
83
84         /**
85          * Find the first recent change matching some specific conditions
86          *
87          * @param array $conds Array of conditions
88          * @param mixed $fname Override the method name in profiling/logs
89          * @return RecentChange
90          */
91         public static function newFromConds( $conds, $fname = false ) {
92                 if( $fname === false )
93                         $fname = __METHOD__;
94                 $dbr = wfGetDB( DB_SLAVE );
95                 $res = $dbr->select(
96                         'recentchanges',
97                         '*',
98                         $conds,
99                         $fname
100                 );
101                 if( $res instanceof ResultWrapper && $res->numRows() > 0 ) {
102                         $row = $res->fetchObject();
103                         $res->free();
104                         return self::newFromRow( $row );
105                 }
106                 return null;
107         }
108
109         # Accessors
110
111         public function setAttribs( $attribs ) {
112                 $this->mAttribs = $attribs;
113         }
114
115         public function setExtra( $extra ) {
116                 $this->mExtra = $extra;
117         }
118
119         public function &getTitle() {
120                 if( $this->mTitle === false ) {
121                         $this->mTitle = Title::makeTitle( $this->mAttribs['rc_namespace'], $this->mAttribs['rc_title'] );
122                 }
123                 return $this->mTitle;
124         }
125
126         public function getMovedToTitle() {
127                 if( $this->mMovedToTitle === false ) {
128                         $this->mMovedToTitle = Title::makeTitle( $this->mAttribs['rc_moved_to_ns'],
129                                 $this->mAttribs['rc_moved_to_title'] );
130                 }
131                 return $this->mMovedToTitle;
132         }
133
134         # Writes the data in this object to the database
135         public function save() {
136                 global $wgLocalInterwiki, $wgPutIPinRC, $wgRC2UDPAddress, $wgRC2UDPOmitBots;
137                 $fname = 'RecentChange::save';
138
139                 $dbw = wfGetDB( DB_MASTER );
140                 if( !is_array($this->mExtra) ) {
141                         $this->mExtra = array();
142                 }
143                 $this->mExtra['lang'] = $wgLocalInterwiki;
144
145                 if( !$wgPutIPinRC ) {
146                         $this->mAttribs['rc_ip'] = '';
147                 }
148
149                 # If our database is strict about IP addresses, use NULL instead of an empty string
150                 if( $dbw->strictIPs() and $this->mAttribs['rc_ip'] == '' ) {
151                         unset( $this->mAttribs['rc_ip'] );
152                 }
153
154                 # Fixup database timestamps
155                 $this->mAttribs['rc_timestamp'] = $dbw->timestamp($this->mAttribs['rc_timestamp']);
156                 $this->mAttribs['rc_cur_time'] = $dbw->timestamp($this->mAttribs['rc_cur_time']);
157                 $this->mAttribs['rc_id'] = $dbw->nextSequenceValue( 'rc_rc_id_seq' );
158
159                 ## If we are using foreign keys, an entry of 0 for the page_id will fail, so use NULL
160                 if( $dbw->cascadingDeletes() and $this->mAttribs['rc_cur_id']==0 ) {
161                         unset ( $this->mAttribs['rc_cur_id'] );
162                 }
163
164                 # Insert new row
165                 $dbw->insert( 'recentchanges', $this->mAttribs, $fname );
166
167                 # Set the ID
168                 $this->mAttribs['rc_id'] = $dbw->insertId();
169                 
170                 # Notify extensions
171                 wfRunHooks( 'RecentChange_save', array( &$this ) );
172
173                 # Notify external application via UDP
174                 if( $wgRC2UDPAddress && ( !$this->mAttribs['rc_bot'] || !$wgRC2UDPOmitBots ) ) {
175                         self::sendToUDP( $this->getIRCLine() );
176                 }
177
178                 # E-mail notifications
179                 global $wgUseEnotif, $wgShowUpdatedMarker, $wgUser;
180                 if( $wgUseEnotif || $wgShowUpdatedMarker ) {
181                         // Users
182                         if( $this->mAttribs['rc_user'] ) {
183                                 $editor = ($wgUser->getId() == $this->mAttribs['rc_user']) ? 
184                                         $wgUser : User::newFromID( $this->mAttribs['rc_user'] );
185                         // Anons
186                         } else {
187                                 $editor = ($wgUser->getName() == $this->mAttribs['rc_user_text']) ? 
188                                         $wgUser : User::newFromName( $this->mAttribs['rc_user_text'], false );
189                         }
190                         # FIXME: this would be better as an extension hook
191                         $enotif = new EmailNotification();
192                         $title = Title::makeTitle( $this->mAttribs['rc_namespace'], $this->mAttribs['rc_title'] );
193                         $enotif->notifyOnPageChange( $editor, $title,
194                                 $this->mAttribs['rc_timestamp'],
195                                 $this->mAttribs['rc_comment'],
196                                 $this->mAttribs['rc_minor'],
197                                 $this->mAttribs['rc_last_oldid'] );
198                 }
199         }
200         
201         public function notifyRC2UDP() {
202                 global $wgRC2UDPAddress, $wgRC2UDPOmitBots;
203                 # Notify external application via UDP
204                 if( $wgRC2UDPAddress && ( !$this->mAttribs['rc_bot'] || !$wgRC2UDPOmitBots ) ) {
205                         self::sendToUDP( $this->getIRCLine() );
206                 }
207         }
208
209         /**
210          * Send some text to UDP
211          * @param string $line
212          * @param string $prefix
213          * @param string $address
214          * @return bool success
215          */
216         public static function sendToUDP( $line, $address = '', $prefix = '' ) {
217                 global $wgRC2UDPAddress, $wgRC2UDPPrefix, $wgRC2UDPPort;
218                 # Assume default for standard RC case
219                 $address = $address ? $address : $wgRC2UDPAddress;
220                 $prefix = $prefix ? $prefix : $wgRC2UDPPrefix;
221                 # Notify external application via UDP
222                 if( $address ) {
223                         $conn = socket_create( AF_INET, SOCK_DGRAM, SOL_UDP );
224                         if( $conn ) {
225                                 $line = $prefix . $line;
226                                 wfDebug( __METHOD__ . ": sending UDP line: $line\n" );
227                                 socket_sendto( $conn, $line, strlen($line), 0, $address, $wgRC2UDPPort );
228                                 socket_close( $conn );
229                                 return true;
230                         } else {
231                                 wfDebug( __METHOD__ . ": failed to create UDP socket\n" );
232                         }
233                 }
234                 return false;
235         }
236         
237         /**
238          * Remove newlines, carriage returns and decode html entites
239          * @param string $line
240          * @return string
241          */
242         public static function cleanupForIRC( $text ) {
243                 return Sanitizer::decodeCharReferences( str_replace( array( "\n", "\r" ), array( "", "" ), $text ) );
244         }
245
246         /**
247          * Mark a given change as patrolled
248          *
249          * @param mixed $change RecentChange or corresponding rc_id
250          * @param bool $auto for automatic patrol
251          * @return See doMarkPatrolled(), or null if $change is not an existing rc_id
252          */
253         public static function markPatrolled( $change, $auto = false ) {
254                 $change = $change instanceof RecentChange
255                         ? $change
256                         : RecentChange::newFromId($change);
257                 if( !$change instanceof RecentChange ) {
258                         return null;
259                 }
260                 return $change->doMarkPatrolled( $auto );
261         }
262         
263         /**
264          * Mark this RecentChange as patrolled
265          *
266          * NOTE: Can also return 'rcpatroldisabled', 'hookaborted' and 'markedaspatrollederror-noautopatrol' as errors
267          * @param bool $auto for automatic patrol
268          * @return array of permissions errors, see Title::getUserPermissionsErrors()
269          */
270         public function doMarkPatrolled( $auto = false ) {
271                 global $wgUser, $wgUseRCPatrol, $wgUseNPPatrol;
272                 $errors = array();
273                 // If recentchanges patrol is disabled, only new pages
274                 // can be patrolled
275                 if( !$wgUseRCPatrol && ( !$wgUseNPPatrol || $this->getAttribute('rc_type') != RC_NEW ) ) {
276                         $errors[] = array('rcpatroldisabled');
277                 }
278                 // Automatic patrol needs "autopatrol", ordinary patrol needs "patrol"
279                 $right = $auto ? 'autopatrol' : 'patrol';
280                 $errors = array_merge( $errors, $this->getTitle()->getUserPermissionsErrors( $right, $wgUser ) );
281                 if( !wfRunHooks('MarkPatrolled', array($this->getAttribute('rc_id'), &$wgUser, false)) ) {
282                         $errors[] = array('hookaborted');
283                 }
284                 // Users without the 'autopatrol' right can't patrol their
285                 // own revisions
286                 if( $wgUser->getName() == $this->getAttribute('rc_user_text') && !$wgUser->isAllowed('autopatrol') ) {
287                         $errors[] = array('markedaspatrollederror-noautopatrol');
288                 }
289                 if( $errors ) {
290                         return $errors;
291                 }
292                 // If the change was patrolled already, do nothing
293                 if( $this->getAttribute('rc_patrolled') ) {
294                         return array();
295                 }
296                 // Actually set the 'patrolled' flag in RC
297                 $this->reallyMarkPatrolled();
298                 // Log this patrol event
299                 PatrolLog::record( $this, $auto );
300                 wfRunHooks( 'MarkPatrolledComplete', array($this->getAttribute('rc_id'), &$wgUser, false) );
301                 return array();
302         }
303         
304         /**
305          * Mark this RecentChange patrolled, without error checking
306          * @return int Number of affected rows
307          */
308         public function reallyMarkPatrolled() {
309                 $dbw = wfGetDB( DB_MASTER );
310                 $dbw->update(
311                         'recentchanges',
312                         array(
313                                 'rc_patrolled' => 1
314                         ),
315                         array(
316                                 'rc_id' => $this->getAttribute('rc_id')
317                         ),
318                         __METHOD__
319                 );
320                 return $dbw->affectedRows();
321         }
322
323         # Makes an entry in the database corresponding to an edit
324         public static function notifyEdit( $timestamp, &$title, $minor, &$user, $comment, $oldId,
325                 $lastTimestamp, $bot, $ip='', $oldSize=0, $newSize=0, $newId=0, $patrol=0 )
326         {
327                 if( !$ip ) {
328                         $ip = wfGetIP();
329                         if( !$ip ) $ip = '';
330                 }
331
332                 $rc = new RecentChange;
333                 $rc->mAttribs = array(
334                         'rc_timestamp'  => $timestamp,
335                         'rc_cur_time'   => $timestamp,
336                         'rc_namespace'  => $title->getNamespace(),
337                         'rc_title'      => $title->getDBkey(),
338                         'rc_type'       => RC_EDIT,
339                         'rc_minor'      => $minor ? 1 : 0,
340                         'rc_cur_id'     => $title->getArticleID(),
341                         'rc_user'       => $user->getId(),
342                         'rc_user_text'  => $user->getName(),
343                         'rc_comment'    => $comment,
344                         'rc_this_oldid' => $newId,
345                         'rc_last_oldid' => $oldId,
346                         'rc_bot'        => $bot ? 1 : 0,
347                         'rc_moved_to_ns'        => 0,
348                         'rc_moved_to_title'     => '',
349                         'rc_ip'         => $ip,
350                         'rc_patrolled'  => intval($patrol),
351                         'rc_new'        => 0,  # obsolete
352                         'rc_old_len'    => $oldSize,
353                         'rc_new_len'    => $newSize,
354                         'rc_deleted'    => 0,
355                         'rc_logid'              => 0,
356                         'rc_log_type'   => null,
357                         'rc_log_action' => '',
358                         'rc_params'             => ''
359                 );
360
361                 $rc->mExtra =  array(
362                         'prefixedDBkey' => $title->getPrefixedDBkey(),
363                         'lastTimestamp' => $lastTimestamp,
364                         'oldSize'       => $oldSize,
365                         'newSize'       => $newSize,
366                 );
367                 $rc->save();
368                 return $rc;
369         }
370
371         /**
372          * Makes an entry in the database corresponding to page creation
373          * Note: the title object must be loaded with the new id using resetArticleID()
374          * @todo Document parameters and return
375          */
376         public static function notifyNew( $timestamp, &$title, $minor, &$user, $comment, $bot,
377                 $ip='', $size=0, $newId=0, $patrol=0 )
378         {
379                 if( !$ip ) {
380                         $ip = wfGetIP();
381                         if( !$ip ) $ip = '';
382                 }
383
384                 $rc = new RecentChange;
385                 $rc->mAttribs = array(
386                         'rc_timestamp'      => $timestamp,
387                         'rc_cur_time'       => $timestamp,
388                         'rc_namespace'      => $title->getNamespace(),
389                         'rc_title'          => $title->getDBkey(),
390                         'rc_type'           => RC_NEW,
391                         'rc_minor'          => $minor ? 1 : 0,
392                         'rc_cur_id'         => $title->getArticleID(),
393                         'rc_user'           => $user->getId(),
394                         'rc_user_text'      => $user->getName(),
395                         'rc_comment'        => $comment,
396                         'rc_this_oldid'     => $newId,
397                         'rc_last_oldid'     => 0,
398                         'rc_bot'            => $bot ? 1 : 0,
399                         'rc_moved_to_ns'    => 0,
400                         'rc_moved_to_title' => '',
401                         'rc_ip'             => $ip,
402                         'rc_patrolled'      => intval($patrol),
403                         'rc_new'                => 1, # obsolete
404                         'rc_old_len'        => 0,
405                         'rc_new_len'        => $size,
406                         'rc_deleted'            => 0,
407                         'rc_logid'                      => 0,
408                         'rc_log_type'           => null,
409                         'rc_log_action'         => '',
410                         'rc_params'                     => ''
411                 );
412
413                 $rc->mExtra =  array(
414                         'prefixedDBkey' => $title->getPrefixedDBkey(),
415                         'lastTimestamp' => 0,
416                         'oldSize' => 0,
417                         'newSize' => $size
418                 );
419                 $rc->save();
420                 return $rc;     
421         }
422
423         # Makes an entry in the database corresponding to a rename
424         public static function notifyMove( $timestamp, &$oldTitle, &$newTitle, &$user, $comment, $ip='', $overRedir = false )
425         {
426                 global $wgRequest;
427                 if( !$ip ) {
428                         $ip = wfGetIP();
429                         if( !$ip ) $ip = '';
430                 }
431
432                 $rc = new RecentChange;
433                 $rc->mAttribs = array(
434                         'rc_timestamp'  => $timestamp,
435                         'rc_cur_time'   => $timestamp,
436                         'rc_namespace'  => $oldTitle->getNamespace(),
437                         'rc_title'      => $oldTitle->getDBkey(),
438                         'rc_type'       => $overRedir ? RC_MOVE_OVER_REDIRECT : RC_MOVE,
439                         'rc_minor'      => 0,
440                         'rc_cur_id'     => $oldTitle->getArticleID(),
441                         'rc_user'       => $user->getId(),
442                         'rc_user_text'  => $user->getName(),
443                         'rc_comment'    => $comment,
444                         'rc_this_oldid' => 0,
445                         'rc_last_oldid' => 0,
446                         'rc_bot'        => $user->isAllowed( 'bot' ) ? $wgRequest->getBool( 'bot' , true ) : 0,
447                         'rc_moved_to_ns'        => $newTitle->getNamespace(),
448                         'rc_moved_to_title'     => $newTitle->getDBkey(),
449                         'rc_ip'         => $ip,
450                         'rc_new'        => 0, # obsolete
451                         'rc_patrolled'  => 1,
452                         'rc_old_len'    => NULL,
453                         'rc_new_len'    => NULL,
454                         'rc_deleted'    => 0,
455                         'rc_logid'              => 0, # notifyMove not used anymore
456                         'rc_log_type'   => null,
457                         'rc_log_action' => '',
458                         'rc_params'             => ''
459                 );
460
461                 $rc->mExtra = array(
462                         'prefixedDBkey' => $oldTitle->getPrefixedDBkey(),
463                         'lastTimestamp' => 0,
464                         'prefixedMoveTo'        => $newTitle->getPrefixedDBkey()
465                 );
466                 $rc->save();
467         }
468
469         public static function notifyMoveToNew( $timestamp, &$oldTitle, &$newTitle, &$user, $comment, $ip='' ) {
470                 RecentChange::notifyMove( $timestamp, $oldTitle, $newTitle, $user, $comment, $ip, false );
471         }
472
473         public static function notifyMoveOverRedirect( $timestamp, &$oldTitle, &$newTitle, &$user, $comment, $ip='' ) {
474                 RecentChange::notifyMove( $timestamp, $oldTitle, $newTitle, $user, $comment, $ip, true );
475         }
476
477         public static function notifyLog( $timestamp, &$title, &$user, $actionComment, $ip='', $type, 
478                 $action, $target, $logComment, $params, $newId=0 )
479         {
480                 global $wgLogRestrictions;
481                 # Don't add private logs to RC!
482                 if( isset($wgLogRestrictions[$type]) && $wgLogRestrictions[$type] != '*' ) {
483                         return false;
484                 }
485                 $rc = self::newLogEntry( $timestamp, $title, $user, $actionComment, $ip, $type, $action,
486                         $target, $logComment, $params, $newId );
487                 $rc->save();
488                 return true;
489         }
490
491         public static function newLogEntry( $timestamp, &$title, &$user, $actionComment, $ip='',
492                 $type, $action, $target, $logComment, $params, $newId=0 )
493         {
494                 global $wgRequest;
495                 if( !$ip ) {
496                         $ip = wfGetIP();
497                         if( !$ip ) $ip = '';
498                 }
499
500                 $rc = new RecentChange;
501                 $rc->mAttribs = array(
502                         'rc_timestamp'  => $timestamp,
503                         'rc_cur_time'   => $timestamp,
504                         'rc_namespace'  => $target->getNamespace(),
505                         'rc_title'      => $target->getDBkey(),
506                         'rc_type'       => RC_LOG,
507                         'rc_minor'      => 0,
508                         'rc_cur_id'     => $target->getArticleID(),
509                         'rc_user'       => $user->getId(),
510                         'rc_user_text'  => $user->getName(),
511                         'rc_comment'    => $logComment,
512                         'rc_this_oldid' => 0,
513                         'rc_last_oldid' => 0,
514                         'rc_bot'        => $user->isAllowed( 'bot' ) ? $wgRequest->getBool( 'bot', true ) : 0,
515                         'rc_moved_to_ns'        => 0,
516                         'rc_moved_to_title'     => '',
517                         'rc_ip' => $ip,
518                         'rc_patrolled' => 1,
519                         'rc_new'        => 0, # obsolete
520                         'rc_old_len'    => NULL,
521                         'rc_new_len'    => NULL,
522                         'rc_deleted'    => 0,
523                         'rc_logid'              => $newId,
524                         'rc_log_type'   => $type,
525                         'rc_log_action' => $action,
526                         'rc_params'             => $params
527                 );
528                 $rc->mExtra =  array(
529                         'prefixedDBkey' => $title->getPrefixedDBkey(),
530                         'lastTimestamp' => 0,
531                         'actionComment' => $actionComment, // the comment appended to the action, passed from LogPage
532                 );
533                 return $rc;
534         }
535
536         # Initialises the members of this object from a mysql row object
537         public function loadFromRow( $row ) {
538                 $this->mAttribs = get_object_vars( $row );
539                 $this->mAttribs['rc_timestamp'] = wfTimestamp(TS_MW, $this->mAttribs['rc_timestamp']);
540                 $this->mAttribs['rc_deleted'] = $row->rc_deleted; // MUST be set
541         }
542
543         # Makes a pseudo-RC entry from a cur row
544         public function loadFromCurRow( $row ) {
545                 $this->mAttribs = array(
546                         'rc_timestamp' => wfTimestamp(TS_MW, $row->rev_timestamp),
547                         'rc_cur_time' => $row->rev_timestamp,
548                         'rc_user' => $row->rev_user,
549                         'rc_user_text' => $row->rev_user_text,
550                         'rc_namespace' => $row->page_namespace,
551                         'rc_title' => $row->page_title,
552                         'rc_comment' => $row->rev_comment,
553                         'rc_minor' => $row->rev_minor_edit ? 1 : 0,
554                         'rc_type' => $row->page_is_new ? RC_NEW : RC_EDIT,
555                         'rc_cur_id' => $row->page_id,
556                         'rc_this_oldid' => $row->rev_id,
557                         'rc_last_oldid' => isset($row->rc_last_oldid) ? $row->rc_last_oldid : 0,
558                         'rc_bot'        => 0,
559                         'rc_moved_to_ns'        => 0,
560                         'rc_moved_to_title'     => '',
561                         'rc_ip' => '',
562                         'rc_id' => $row->rc_id,
563                         'rc_patrolled' => $row->rc_patrolled,
564                         'rc_new' => $row->page_is_new, # obsolete
565                         'rc_old_len' => $row->rc_old_len,
566                         'rc_new_len' => $row->rc_new_len,
567                         'rc_params' => isset($row->rc_params) ? $row->rc_params : '',
568                         'rc_log_type' => isset($row->rc_log_type) ? $row->rc_log_type : null,
569                         'rc_log_action' => isset($row->rc_log_action) ? $row->rc_log_action : null,
570                         'rc_log_id' => isset($row->rc_log_id) ? $row->rc_log_id: 0,
571                         'rc_deleted' => $row->rc_deleted // MUST be set
572                 );
573         }
574
575         /**
576          * Get an attribute value
577          *
578          * @param $name Attribute name
579          * @return mixed
580          */
581         public function getAttribute( $name ) {
582                 return isset( $this->mAttribs[$name] ) ? $this->mAttribs[$name] : NULL;
583         }
584
585         /**
586          * Gets the end part of the diff URL associated with this object
587          * Blank if no diff link should be displayed
588          */
589         public function diffLinkTrail( $forceCur ) {
590                 if( $this->mAttribs['rc_type'] == RC_EDIT ) {
591                         $trail = "curid=" . (int)($this->mAttribs['rc_cur_id']) .
592                                 "&oldid=" . (int)($this->mAttribs['rc_last_oldid']);
593                         if( $forceCur ) {
594                                 $trail .= '&diff=0' ;
595                         } else {
596                                 $trail .= '&diff=' . (int)($this->mAttribs['rc_this_oldid']);
597                         }
598                 } else {
599                         $trail = '';
600                 }
601                 return $trail;
602         }
603
604         public function getIRCLine() {
605                 global $wgUseRCPatrol, $wgUseNPPatrol, $wgRC2UDPInterwikiPrefix, $wgLocalInterwiki;
606
607                 // FIXME: Would be good to replace these 2 extract() calls with something more explicit
608                 // e.g. list ($rc_type, $rc_id) = array_values ($this->mAttribs); [or something like that]
609                 extract($this->mAttribs);
610                 extract($this->mExtra);
611
612                 if( $rc_type == RC_LOG ) {
613                         $titleObj = Title::newFromText( "Log/$rc_log_type", NS_SPECIAL );
614                 } else {
615                         $titleObj =& $this->getTitle();
616                 }
617                 $title = $titleObj->getPrefixedText();
618                 $title = self::cleanupForIRC( $title );
619
620                 if( $rc_type == RC_LOG ) {
621                         $url = '';
622                 } else {
623                         if( $rc_type == RC_NEW ) {
624                                 $url = "oldid=$rc_this_oldid";
625                         } else {
626                                 $url = "diff=$rc_this_oldid&oldid=$rc_last_oldid";
627                         }
628                         if( $wgUseRCPatrol || ($rc_type == RC_NEW && $wgUseNPPatrol) ) {
629                                 $url .= "&rcid=$rc_id";
630                         }
631                         // XXX: *HACK* this should use getFullURL(), hacked for SSL madness --brion 2005-12-26
632                         // XXX: *HACK^2* the preg_replace() undoes much of what getInternalURL() does, but we 
633                         // XXX: need to call it so that URL paths on the Wikimedia secure server can be fixed
634                         // XXX: by a custom GetInternalURL hook --vyznev 2008-12-10
635                         $url = preg_replace( '/title=[^&]*&/', '', $titleObj->getInternalURL( $url ) );
636                 }
637
638                 if( isset( $oldSize ) && isset( $newSize ) ) {
639                         $szdiff = $newSize - $oldSize;
640                         if($szdiff < -500) {
641                                 $szdiff = "\002$szdiff\002";
642                         } elseif($szdiff >= 0) {
643                                 $szdiff = '+' . $szdiff ;
644                         }
645                         $szdiff = '(' . $szdiff . ')' ;
646                 } else {
647                         $szdiff = '';
648                 }
649
650                 $user = self::cleanupForIRC( $rc_user_text );
651
652                 if( $rc_type == RC_LOG ) {
653                         $targetText = $this->getTitle()->getPrefixedText();
654                         $comment = self::cleanupForIRC( str_replace("[[$targetText]]","[[\00302$targetText\00310]]",$actionComment) );
655                         $flag = $rc_log_action;
656                 } else {
657                         $comment = self::cleanupForIRC( $rc_comment );
658                         $flag = '';
659                         if( !$rc_patrolled && ($wgUseRCPatrol || $rc_new && $wgUseNPPatrol) ) {
660                                 $flag .= '!';
661                         }
662                         $flag .= ($rc_new ? "N" : "") . ($rc_minor ? "M" : "") . ($rc_bot ? "B" : "");
663                 }
664
665                 if ( $wgRC2UDPInterwikiPrefix === true ) {
666                         $prefix = $wgLocalInterwiki;
667                 } elseif ( $wgRC2UDPInterwikiPrefix ) {
668                         $prefix = $wgRC2UDPInterwikiPrefix;
669                 } else {
670                         $prefix = false;
671                 }
672                 if ( $prefix !== false ) {
673                         $titleString = "\00314[[\00303$prefix:\00307$title\00314]]";
674                 } else {
675                         $titleString = "\00314[[\00307$title\00314]]";
676                 }
677                 
678                 # see http://www.irssi.org/documentation/formats for some colour codes. prefix is \003,
679                 # no colour (\003) switches back to the term default
680                 $fullString = "$titleString\0034 $flag\00310 " .
681                               "\00302$url\003 \0035*\003 \00303$user\003 \0035*\003 $szdiff \00310$comment\003\n";
682                         
683                 return $fullString;
684         }
685
686         /**
687          * Returns the change size (HTML).
688          * The lengths can be given optionally.
689          */
690         public function getCharacterDifference( $old = 0, $new = 0 ) {
691                 if( $old === 0 ) {
692                         $old = $this->mAttribs['rc_old_len'];
693                 }
694                 if( $new === 0 ) {
695                         $new = $this->mAttribs['rc_new_len'];
696                 }
697                 if( $old === NULL || $new === NULL ) {
698                         return '';
699                 }
700                 return ChangesList::showCharacterDifference( $old, $new );
701         }
702 }