From: Ilmari Karonen Date: Tue, 28 Nov 2006 03:22:12 +0000 (+0000) Subject: New special page Special:Deletedcontribs for browsing deleted edits made by a user. X-Git-Tag: 1.31.0-rc.0~55054 X-Git-Url: https://git.cyclocoop.org/%242?a=commitdiff_plain;h=c1c00f2bd6e2f5de088755642203bea5e67a6ef2;p=lhc%2Fweb%2Fwiklou.git New special page Special:Deletedcontribs for browsing deleted edits made by a user. NOTENOTENOTE: This is a one-night hack; feel free to tweak it mercilessly. It has a lot of common code with Special:Contributions that should be factored out. There is no link to this new page anywhere in the UI yet; I'd like people to test and review this a bit before officially announcing it. The page is currently only available to users with deletedhistory privileges; a possible additional feature might be to allow all users to list (but not view) their _own_ deleted edits. --- diff --git a/RELEASE-NOTES b/RELEASE-NOTES index b967a76c12..5e6bb05ce6 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -224,6 +224,7 @@ it from source control: http://www.mediawiki.org/wiki/Download_from_SVN This is triggered by including &undo=revid in an edit URL. A link to a URL that will undo a given edit is shown on NEW NON-CURRENT revision headers on diff pages. * Fix display of link in "already rolled back" message for image/category pages +* New special page Special:Deletedcontribs for browsing deleted edits made by a user. == Languages updated == diff --git a/includes/AutoLoader.php b/includes/AutoLoader.php index f826ec1c2c..1916f20d27 100644 --- a/includes/AutoLoader.php +++ b/includes/AutoLoader.php @@ -164,6 +164,7 @@ function __autoload($className) { 'EmailConfirmation' => 'includes/SpecialConfirmemail.php', 'ContribsFinder' => 'includes/SpecialContributions.php', 'DeadendPagesPage' => 'includes/SpecialDeadendpages.php', + 'DeletedContribsFinder' => 'includes/SpecialDeletedcontribs.php', 'DisambiguationsPage' => 'includes/SpecialDisambiguations.php', 'DoubleRedirectsPage' => 'includes/SpecialDoubleRedirects.php', 'EmailUserForm' => 'includes/SpecialEmailuser.php', diff --git a/includes/SpecialDeletedcontribs.php b/includes/SpecialDeletedcontribs.php new file mode 100644 index 0000000000..1eae5b904c --- /dev/null +++ b/includes/SpecialDeletedcontribs.php @@ -0,0 +1,290 @@ +getUserCond(); + $nscond = $this->getNamespaceCond(); + $use_index = $this->dbr->useIndexClause( $index ); + list( $archive ) = $this->dbr->tableNamesN( 'archive' ); + $sql = "SELECT ar_timestamp " . + " FROM $archive $use_index " . + " WHERE $usercond $nscond" . + " ORDER BY ar_timestamp $dir LIMIT 1"; + + $res = $this->dbr->query( $sql, __METHOD__ ); + $row = $this->dbr->fetchObject( $res ); + if ( $row ) { + return $row->ar_timestamp; + } else { + return false; + } + } + + function getUserCond() { + $condition = ' ar_user_text=' . $this->dbr->addQuotes( $this->username ); + $index = 'usertext_timestamp'; + return array( $index, $condition ); + } + + function getNamespaceCond() { + if ( $this->namespace !== false ) + return ' AND ar_namespace = ' . (int)$this->namespace; + return ''; + } + + # The following functions share a lot of code with each other and with + # their counterparts in ContribsFinder. Some refactoring should help. + + function getPreviousOffsetForPaging() { + list( $index, $usercond ) = $this->getUserCond(); + $nscond = $this->getNamespaceCond(); + + $use_index = $this->dbr->useIndexClause( $index ); + list( $archive ) = $this->dbr->tableNamesN( 'archive' ); + + $sql = "SELECT ar_timestamp FROM $archive $use_index " . + "WHERE ar_timestamp > '" . $this->offset . "' AND " . + $usercond . $nscond; + $sql .= " ORDER BY ar_timestamp ASC"; + $sql = $this->dbr->limitResult( $sql, $this->limit, 0 ); + $res = $this->dbr->query( $sql ); + + $numRows = $this->dbr->numRows( $res ); + if ( $numRows ) { + $this->dbr->dataSeek( $res, $numRows - 1 ); + $row = $this->dbr->fetchObject( $res ); + $offset = $row->ar_timestamp; + } else { + $offset = false; + } + $this->dbr->freeResult( $res ); + return $offset; + } + + function getFirstOffsetForPaging() { + list( $index, $usercond ) = $this->getUserCond(); + $use_index = $this->dbr->useIndexClause( $index ); + list( $archive ) = $this->dbr->tableNamesN( 'archive' ); + $nscond = $this->getNamespaceCond(); + $sql = "SELECT ar_timestamp FROM $archive $use_index " . + "WHERE " . + $usercond . $nscond; + $sql .= " ORDER BY ar_timestamp ASC"; + $sql = $this->dbr->limitResult( $sql, $this->limit, 0 ); + $res = $this->dbr->query( $sql ); + + $numRows = $this->dbr->numRows( $res ); + if ( $numRows ) { + $this->dbr->dataSeek( $res, $numRows - 1 ); + $row = $this->dbr->fetchObject( $res ); + $offset = $row->ar_timestamp; + } else { + $offset = false; + } + $this->dbr->freeResult( $res ); + return $offset; + } + + /* private */ function makeSql() { + $offsetQuery = ''; + + list( $archive ) = $this->dbr->tableNamesN( 'archive' ); + list( $index, $userCond ) = $this->getUserCond(); + + if ( $this->offset ) + $offsetQuery = "AND ar_timestamp <= '{$this->offset}'"; + + $nscond = $this->getNamespaceCond(); + $use_index = $this->dbr->useIndexClause( $index ); + $sql = "SELECT + ar_namespace,ar_title, + ar_rev_id,ar_text_id,ar_timestamp,ar_comment,ar_minor_edit,ar_user,ar_user_text + FROM $archive $use_index + WHERE $userCond $nscond $offsetQuery + ORDER BY ar_timestamp DESC"; + $sql = $this->dbr->limitResult( $sql, $this->limit, 0 ); + return $sql; + } +}; + +/** + * Special page "deleted contributions". + * Shows a list of the deleted contributions of a user. + * + * XXX This is almost and exact duplicate of the code in + * XXX SpecialContributions.php and should be merged with it. + * + * @return none + * @param $par String: (optional) user name of the user for which to show the contributions + */ +function wfSpecialDeletedcontribs( $par = null ) { + global $wgUser, $wgOut, $wgLang, $wgRequest; + + $target = isset( $par ) ? $par : $wgRequest->getVal( 'target' ); + if ( !strlen( $target ) ) { + $wgOut->showErrorPage( 'notargettitle', 'notargettext' ); + return; + } + + $nt = Title::newFromURL( $target ); + if ( !$nt ) { + $wgOut->showErrorPage( 'notargettitle', 'notargettext' ); + return; + } + + $options = array(); + + list( $options['limit'], $options['offset']) = wfCheckLimits(); + $options['offset'] = $wgRequest->getVal( 'offset' ); + /* Offset must be an integral. */ + if ( !strlen( $options['offset'] ) || !preg_match( '/^[0-9]+$/', $options['offset'] ) ) + $options['offset'] = ''; + + $title = SpecialPage::getTitleFor( 'Deletedcontribs' ); + $options['target'] = $target; + + $nt =& Title::makeTitle( NS_USER, $nt->getDBkey() ); + $finder = new DeletedContribsFinder( $nt->getText() ); + $finder->setLimit( $options['limit'] ); + $finder->setOffset( $options['offset'] ); + + if ( ( $ns = $wgRequest->getVal( 'namespace', null ) ) !== null && $ns !== '' ) { + $options['namespace'] = intval( $ns ); + $finder->setNamespace( $options['namespace'] ); + } else { + $options['namespace'] = ''; + } + + if ( $wgRequest->getText( 'go' ) == 'prev' ) { + $offset = $finder->getPreviousOffsetForPaging(); + if ( $offset !== false ) { + $options['offset'] = $offset; + $prevurl = $title->getLocalURL( wfArrayToCGI( $options ) ); + $wgOut->redirect( $prevurl ); + return; + } + } + + if ( $wgRequest->getText( 'go' ) == 'first' ) { + $offset = $finder->getFirstOffsetForPaging(); + if ( $offset !== false ) { + $options['offset'] = $offset; + $prevurl = $title->getLocalURL( wfArrayToCGI( $options ) ); + $wgOut->redirect( $prevurl ); + return; + } + } + + $wgOut->setSubtitle( wfMsgHtml( 'contribsub', contributionsSub( $nt ) ) ); + + $id = User::idFromName( $nt->getText() ); + wfRunHooks( 'SpecialDeletedcontribsBeforeMainOutput', $id ); + + $wgOut->addHTML( contributionsForm( $options) ); + + $contribs = $finder->find(); + + if ( count( $contribs ) == 0) { + $wgOut->addWikiText( wfMsg( 'nocontribs' ) ); + return; + } + + list( $early, $late ) = $finder->getEditLimits(); + $lastts = count( $contribs ) ? $contribs[count( $contribs ) - 1]->ar_timestamp : 0; + $atstart = ( !count( $contribs ) || $late == $contribs[0]->ar_timestamp ); + $atend = ( !count( $contribs ) || $early == $lastts ); + + // These four are defaults + $newestlink = wfMsgHtml( 'sp-contributions-newest' ); + $oldestlink = wfMsgHtml( 'sp-contributions-oldest' ); + $newerlink = wfMsgHtml( 'sp-contributions-newer', $options['limit'] ); + $olderlink = wfMsgHtml( 'sp-contributions-older', $options['limit'] ); + + if ( !$atstart ) { + $stuff = $title->escapeLocalURL( wfArrayToCGI( array( 'offset' => '' ), $options ) ); + $newestlink = "$newestlink"; + $stuff = $title->escapeLocalURL( wfArrayToCGI( array( 'go' => 'prev' ), $options ) ); + $newerlink = "$newerlink"; + } + + if ( !$atend ) { + $stuff = $title->escapeLocalURL( wfArrayToCGI( array( 'go' => 'first' ), $options ) ); + $oldestlink = "$oldestlink"; + $stuff = $title->escapeLocalURL( wfArrayToCGI( array( 'offset' => $lastts ), $options ) ); + $olderlink = "$olderlink"; + } + + $firstlast = "($newestlink | $oldestlink)"; + + $urls = array(); + foreach ( array( 20, 50, 100, 250, 500 ) as $num ) { + $stuff = $title->escapeLocalURL( wfArrayToCGI( array( 'limit' => $num ), $options ) ); + $urls[] = "".$wgLang->formatNum( $num ).""; + } + $bits = implode( $urls, ' | ' ); + + $prevnextbits = $firstlast .' '. wfMsgHtml( 'viewprevnext', $newerlink, $olderlink, $bits ); + + $wgOut->addHTML( "

{$prevnextbits}

\n" ); + + $wgOut->addHTML( "\n" ); + $wgOut->addHTML( "

{$prevnextbits}

\n" ); +} + + +/** + * Generates each row in the deleted contributions list. + * + * @todo This would probably look a lot nicer in a table. + */ +function dcListEdit( $sk, $row ) { + $fname = 'dcListEdit'; + wfProfileIn( $fname ); + + global $wgLang, $wgUser, $wgRequest; + static $messages, $undelete; + if( !isset( $messages ) ) { + foreach( explode( ' ', 'undel minoreditletter' ) as $msg ) { + $messages[$msg] = wfMsgExt( $msg, array( 'escape') ); + } + } + if( !isset( $undelete ) ) { + $undelete =& SpecialPage::getTitleFor( 'Undelete' ); + } + + $page = Title::makeTitle( $row->ar_namespace, $row->ar_title )->getPrefixedText(); + + $ts = wfTimestamp( TS_MW, $row->ar_timestamp ); + $d = $wgLang->timeanddate( $ts, true ); + + $link = $sk->makeKnownLinkObj( $undelete, $page, "target=$page×tamp=$ts" ); + $undellink = '(' . $sk->makeKnownLinkObj( $undelete, $messages['undel'], "target=$page" ) . ')'; + + if( $row->ar_minor_edit ) { + $mflag = '' . $messages['minoreditletter'] . ' '; + } else { + $mflag = ''; + } + + $comment = $sk->commentBlock( $row->ar_comment ); + + $ret = "
  • {$d}{$mflag} {$link} {$undellink} {$comment}
  • "; + wfProfileOut( $fname ); + return $ret; +} + +?> diff --git a/includes/SpecialPage.php b/includes/SpecialPage.php index e8db18b742..7b590d9898 100644 --- a/includes/SpecialPage.php +++ b/includes/SpecialPage.php @@ -127,6 +127,7 @@ class SpecialPage 'Log' => array( 'SpecialPage', 'Log' ), 'Blockip' => array( 'SpecialPage', 'Blockip', 'block' ), 'Undelete' => array( 'SpecialPage', 'Undelete', 'deletedhistory' ), + 'Deletedcontribs' => array( 'SpecialPage', 'Deletedcontribs', 'deletedhistory' ), 'Import' => array( 'SpecialPage', "Import", 'import' ), 'Lockdb' => array( 'SpecialPage', 'Lockdb', 'siteadmin' ), 'Unlockdb' => array( 'SpecialPage', 'Unlockdb', 'siteadmin' ), diff --git a/languages/messages/MessagesEn.php b/languages/messages/MessagesEn.php index 6514ff5b00..de4189fbb3 100644 --- a/languages/messages/MessagesEn.php +++ b/languages/messages/MessagesEn.php @@ -1741,6 +1741,10 @@ revision may have been restored or removed from the archive.", Consult the [[Special:Log/delete|deletion log]] for a record of recent deletions and restorations.", +# Deleted contributions +'deletedcontribs' => 'Deleted contributions', +'undel' => 'undelete', + # Namespace form on various pages 'namespace' => 'Namespace:', 'invert' => 'Invert selection', diff --git a/maintenance/archives/patch-archive-utindex.sql b/maintenance/archives/patch-archive-utindex.sql new file mode 100644 index 0000000000..aa7d2c577d --- /dev/null +++ b/maintenance/archives/patch-archive-utindex.sql @@ -0,0 +1,7 @@ +-- Add an index to archive on ar_user_text, ar_timestamp +-- +-- Added 2006-11-27 +-- + + ALTER TABLE /*$wgDBprefix*/archive +ADD INDEX usertext_timestamp (ar_user_text, ar_timestamp); \ No newline at end of file diff --git a/maintenance/tables.sql b/maintenance/tables.sql index d210179657..1f133c1d6e 100644 --- a/maintenance/tables.sql +++ b/maintenance/tables.sql @@ -351,7 +351,8 @@ CREATE TABLE /*$wgDBprefix*/archive ( -- row upon undeletion. ar_text_id int(8) unsigned, - KEY name_title_timestamp (ar_namespace,ar_title,ar_timestamp) + KEY name_title_timestamp (ar_namespace,ar_title,ar_timestamp), + KEY usertext_timestamp (ar_user_text,ar_timestamp) ) TYPE=InnoDB; diff --git a/maintenance/updaters.inc b/maintenance/updaters.inc index f319cb4dd9..18075ffc62 100644 --- a/maintenance/updaters.inc +++ b/maintenance/updaters.inc @@ -821,6 +821,19 @@ function do_backlinking_indices_update() { } } +function do_archive_indices_update() { + global $wgDatabase; + echo( "Checking for additional archive indices...\n" ); + + $info = $wgDatabase->indexInfo( 'archive', 'usertext_timestamp', __METHOD__ ); + if( !$info ) { + echo( "...index on ( ar_user_text, ar_timestamp ) not found; creating\n" ); + dbsource( archive( 'patch-archive-utindex.sql' ) ); + } else { + echo( "...index on ( ar_user_text, ar_timestamp ) seems to be ok\n" ); + } +} + function do_all_updates( $doShared = false ) { global $wgNewTables, $wgNewFields, $wgRenamedTables, $wgSharedDB, $wgDatabase, $wgDBtype; @@ -889,6 +902,8 @@ function do_all_updates( $doShared = false ) { do_backlinking_indices_update(); flush(); + do_archive_indices_update(); flush(); + initialiseMessages(); flush(); }