unnecessary hidden UI work when watch/unwatch is performed on edit
* Fixed bogus master fallback in external storage
* (bug 5246) Add speak:none to "hiddenStructure" class in main.css
+* Further work on rev_deleted; changed to a bitfield with several data-hiding
+ options. Not yet ready for production use; Special:Revisiondelete is
+ incomplete, and the flags are not preserved across page deletion/undeletion.
+ To try it; add the 'deleterevision' permission to a privileged group.
+
=== Caveats ===
}
}
- $this->mContent = $revision->getText();
+ // FIXME: Horrible, horrible! This content-loading interface just plain sucks.
+ // We should instead work with the Revision object when we need it...
+ $this->mContent = $revision->userCan( MW_REV_DELETED_TEXT ) ? $revision->getRawText() : "";
+ //$this->mContent = $revision->getText();
$this->mUser = $revision->getUser();
$this->mUserText = $revision->getUserText();
wfProfileOut( $fname );
return;
}
-
+
if ( empty( $oldid ) && $this->checkTouched() ) {
$wgOut->setETag($parserCache->getETag($this, $wgUser));
if ( !empty( $oldid ) ) {
$this->setOldSubtitle( isset($this->mOldId) ? $this->mOldId : $oldid );
- $wgOut->setRobotpolicy( 'noindex,follow' );
+ $wgOut->setRobotpolicy( 'noindex,nofollow' );
+ if( $this->mRevision->isDeleted( MW_REV_DELETED_TEXT ) ) {
+ if( !$this->mRevision->userCan( MW_REV_DELETED_TEXT ) ) {
+ $wgOut->addWikiText( wfMsg( 'rev-deleted-text-permission' ) );
+ $wgOut->setPageTitle( $this->mTitle->getPrefixedText() );
+ return;
+ } else {
+ $wgOut->addWikiText( wfMsg( 'rev-deleted-text-view' ) );
+ // and we are allowed to see...
+ }
+ }
+
}
}
if( !$outputDone ) {
/** Insert links to user page, user talk page and eventually a blocking link */
function insertUserRelatedLinks(&$s, &$rc) {
- $s .= $this->userLink( $rc->mAttribs['rc_user'], $rc->mAttribs['rc_user_text'] );
- $s .= $this->userToolLinks( $rc->mAttribs['rc_user'], $rc->mAttribs['rc_user_text'] );
+ $s .= $this->skin->userLink( $rc->mAttribs['rc_user'], $rc->mAttribs['rc_user_text'] );
+ $s .= $this->skin->userToolLinks( $rc->mAttribs['rc_user'], $rc->mAttribs['rc_user_text'] );
}
/** insert a formatted comment */
( !$wgOnlySysopsCanPatrol || $wgUser->isAllowed( 'patrol' ) );
}
- /**
- * Make user link (or user contributions for unregistered users)
- * @param int $userId
- * @param string $userText
- * @return string HTML fragment
- * @access private
- */
- function userLink( $userId, $userText ) {
- $encName = htmlspecialchars( $userText );
- if( $userId == 0 ) {
- $contribsPage = Title::makeTitle( NS_SPECIAL, 'Contributions' );
- return $this->skin->makeKnownLinkObj( $contribsPage,
- $encName, 'target=' . urlencode( $userText ) );
- } else {
- $userPage = Title::makeTitle( NS_USER, $userText );
- return $this->skin->makeLinkObj( $userPage, $encName );
- }
- }
-
- /**
- * @param int $userId
- * @param string $userText
- * @return string HTML fragment with talk and/or block links
- * @access private
- */
- function userToolLinks( $userId, $userText ) {
- global $wgUser, $wgDisableAnonTalk, $wgSysopUserBans;
- $talkable = !( $wgDisableAnonTalk && 0 == $userId );
- $blockable = ( $wgSysopUserBans || 0 == $userId );
-
- $items = array();
- if( $talkable ) {
- $items[] = $this->userTalkLink( $userId, $userText );
- }
- if( $blockable && $wgUser->isAllowed( 'block' ) ) {
- $items[] = $this->blockLink( $userId, $userText );
- }
-
- if( $items ) {
- return ' (' . implode( ' | ', $items ) . ')';
- } else {
- return '';
- }
- }
-
- /**
- * @param int $userId
- * @param string $userText
- * @return string HTML fragment with user talk link
- * @access private
- */
- function userTalkLink( $userId, $userText ) {
- global $wgContLang;
- $talkname = $wgContLang->getNsText( NS_TALK ); # use the shorter name
-
- $userTalkPage = Title::makeTitle( NS_USER_TALK, $userText );
- $userTalkLink = $this->skin->makeLinkObj( $userTalkPage, $talkname );
- return $userTalkLink;
- }
-
- /**
- * @param int $userId
- * @param string $userText
- * @return string HTML fragment with block link
- * @access private
- */
- function blockLink( $userId, $userText ) {
- $blockPage = Title::makeTitle( NS_SPECIAL, 'Blockip' );
- $blockLink = $this->skin->makeKnownLinkObj( $blockPage,
- $this->message['blocklink'], 'ip=' . urlencode( $userText ) );
- return $blockLink;
- }
}
$curIdEq.'&diff='.$rc_this_oldid.'&oldid='.$rc_last_oldid . $rcIdQuery );
}
- $rc->userlink = $this->userLink( $rc_user, $rc_user_text );
+ $rc->userlink = $this->skin->userLink( $rc_user, $rc_user_text );
$rc->lastlink = $lastLink;
$rc->curlink = $curLink;
$rc->difflink = $diffLink;
- $rc->usertalklink = $this->userToolLinks( $rc_user, $rc_user_text );
+ $rc->usertalklink = $this->skin->userToolLinks( $rc_user, $rc_user_text );
# Put accumulated information into the cache, for later display
# Page moves go on their own line
// Permission to change users' group assignments
$wgGroupPermissions['bureaucrat']['userrights'] = true;
+// Experimental permissions, not ready for production use
+//$wgGroupPermissions['sysop']['deleterevision'] = true;
+//$wgGroupPermissions['bureaucrat']['hiderevision'] = true;
+
/**
* The developer group is deprecated, but can be activated if need be
* to use the 'lockdb' and 'unlockdb' special pages. Those require
var $mOldid, $mNewid, $mTitle;
var $mOldtitle, $mNewtitle, $mPagetitle;
var $mOldtext, $mNewtext;
- var $mOldUser, $mNewUser;
- var $mOldComment, $mNewComment;
var $mOldPage, $mNewPage;
var $mRcidMarkPatrolled;
var $mOldRev, $mNewRev;
$talk = $wgContLang->getNsText( NS_TALK );
$contribs = wfMsg( 'contribslink' );
- $this->mOldComment = $sk->formatComment($this->mOldComment);
- $this->mNewComment = $sk->formatComment($this->mNewComment);
-
- $oldUserLink = $sk->makeLinkObj( Title::makeTitleSafe( NS_USER, $this->mOldUser ), $this->mOldUser );
- $newUserLink = $sk->makeLinkObj( Title::makeTitleSafe( NS_USER, $this->mNewUser ), $this->mNewUser );
- $oldUTLink = $sk->makeLinkObj( Title::makeTitleSafe( NS_USER_TALK, $this->mOldUser ), $talk );
- $newUTLink = $sk->makeLinkObj( Title::makeTitleSafe( NS_USER_TALK, $this->mNewUser ), $talk );
- $oldContribs = $sk->makeKnownLinkObj( Title::makeTitle( NS_SPECIAL, 'Contributions' ), $contribs,
- 'target=' . urlencode($this->mOldUser) );
- $newContribs = $sk->makeKnownLinkObj( Title::makeTitle( NS_SPECIAL, 'Contributions' ), $contribs,
- 'target=' . urlencode($this->mNewUser) );
if ( $this->mNewRev->isCurrent() && $wgUser->isAllowed('rollback') ) {
+ $username = $this->mNewRev->getUserText();
$rollback = ' <strong>[' . $sk->makeKnownLinkObj( $this->mTitle, wfMsg( 'rollbacklink' ),
- 'action=rollback&from=' . urlencode($this->mNewUser) .
- '&token=' . urlencode( $wgUser->editToken( array( $this->mTitle->getPrefixedText(), $this->mNewUser ) ) ) ) .
+ 'action=rollback&from=' . urlencode( $username ) .
+ '&token=' . urlencode( $wgUser->editToken( array( $this->mTitle->getPrefixedText(), $username ) ) ) ) .
']</strong>';
} else {
$rollback = '';
'diff=next&oldid='.$this->mNewid, '', '', 'id="differences-nextlink"' );
}
- $oldHeader = "<strong>{$this->mOldtitle}</strong><br />$oldUserLink " .
- "($oldUTLink | $oldContribs)<br />" . $this->mOldComment .
- '<br />' . $prevlink;
- $newHeader = "<strong>{$this->mNewtitle}</strong><br />$newUserLink " .
- "($newUTLink | $newContribs) $rollback<br />" . $this->mNewComment .
- '<br />' . $nextlink . $patrol;
+ $oldHeader = "<strong>{$this->mOldtitle}</strong><br />" .
+ $sk->revUserTools( $this->mOldRev ) . "<br />" .
+ $sk->revComment( $this->mOldRev ) . "<br />" .
+ $prevlink;
+ $newHeader = "<strong>{$this->mNewtitle}</strong><br />" .
+ $sk->revUserTools( $this->mNewRev ) . " $rollback<br />" .
+ $sk->revComment( $this->mNewRev ) . "<br />" .
+ $nextlink . $patrol;
$this->showDiff( $oldHeader, $newHeader );
$wgOut->addHTML( "<hr /><h2>{$this->mPagetitle}</h2>\n" );
#
$sk = $wgUser->getSkin();
- $uTLink = $sk->makeLinkObj( Title::makeTitleSafe( NS_USER_TALK, $this->mOldUser ), $wgLang->getNsText( NS_TALK ) );
- $userLink = $sk->makeLinkObj( Title::makeTitleSafe( NS_USER, $this->mOldUser ), $this->mOldUser );
- $contribs = $sk->makeKnownLinkObj( Title::makeTitle( NS_SPECIAL, 'Contributions' ), wfMsg( 'contribslink' ),
- 'target=' . urlencode($this->mOldUser) );
$nextlink = $sk->makeKnownLinkObj( $this->mTitle, wfMsgHtml( 'nextdiff' ), 'diff=next&oldid='.$this->mNewid, '', '', 'id="differences-nextlink"' );
- $header = "<div class=\"firstrevisionheader\" style=\"text-align: center\"><strong>{$this->mOldtitle}</strong><br />$userLink " .
- "($uTLink | $contribs)<br />" . $this->mOldComment .
- '<br />' . $nextlink. "</div>\n";
+ $header = "<div class=\"firstrevisionheader\" style=\"text-align: center\"><strong>{$this->mOldtitle}</strong><br />" .
+ $sk->revUserTools( $this->mNewRev ) . "<br />" .
+ $sk->revComment( $this->mNewRev ) . "<br />" .
+ $nextlink . "</div>\n";
$wgOut->addHTML( $header );
$wgOut->setSubtitle( wfMsg( 'difference' ) );
- $wgOut->setRobotpolicy( 'noindex,follow' );
+ $wgOut->setRobotpolicy( 'noindex,nofollow' );
# Show current revision
global $wgOut;
$diff = $this->getDiff( $otitle, $ntitle );
if ( $diff === false ) {
- $wgOut->addWikitext( wfMsg( 'missingarticle', "<nowiki>$t</nowiki>" ) );
+ $wgOut->addWikitext( wfMsg( 'missingarticle', "<nowiki>(fixme, bug)</nowiki>" ) );
return false;
} else {
$wgOut->addHTML( $diff );
$this->mNewtitle = "<a href='$newLink'>{$this->mPagetitle}</a>";
}
- $this->mNewUser = $this->mNewRev->getUserText();
- $this->mNewComment = $this->mNewRev->getComment();
-
// Load the old revision object
$this->mOldRev = false;
if( $this->mOldid ) {
$t = $wgLang->timeanddate( $this->mOldRev->getTimestamp(), true );
$oldLink = $this->mOldPage->escapeLocalUrl( 'oldid=' . $this->mOldid );
$this->mOldtitle = "<a href='$oldLink'>" . htmlspecialchars( wfMsg( 'revisionasof', $t ) ) . '</a>';
-
-
- $this->mOldUser = $this->mOldRev->getUserText();
- $this->mOldComment = $this->mOldRev->getComment();
}
return true;
return false;
}
if ( $this->mOldRev ) {
+ // FIXME: permission tests
$this->mOldtext = $this->mOldRev->getText();
if ( $this->mOldtext === false ) {
return false;
<?php
-# Copyright (C) 2003, 2005 Brion Vibber <brion@pobox.com>
+# Copyright (C) 2003, 2005, 2006 Brion Vibber <brion@pobox.com>
# http://www.mediawiki.org/
#
# This program is free software; you can redistribute it and/or modify
* @return string
*/
function schemaVersion() {
- return "0.3";
+ return "0.3"; // FIXME: upgrade to 0.4 when updated XSD is ready, for the revision deletion bits
}
/**
$ts = wfTimestamp( TS_ISO_8601, $row->rev_timestamp );
$out .= " " . wfElement( 'timestamp', null, $ts ) . "\n";
- $out .= " <contributor>\n";
- if( $row->rev_user ) {
- $out .= " " . wfElementClean( 'username', null, strval( $row->rev_user_text ) ) . "\n";
- $out .= " " . wfElement( 'id', null, strval( $row->rev_user ) ) . "\n";
+ if( $row->rev_deleted & MW_REV_DELETED_USER ) {
+ $out .= " " . wfElement( 'contributor', array( 'deleted' => 'deleted' ) ) . "\n";
} else {
- $out .= " " . wfElementClean( 'ip', null, strval( $row->rev_user_text ) ) . "\n";
+ $out .= " <contributor>\n";
+ if( $row->rev_user ) {
+ $out .= " " . wfElementClean( 'username', null, strval( $row->rev_user_text ) ) . "\n";
+ $out .= " " . wfElement( 'id', null, strval( $row->rev_user ) ) . "\n";
+ } else {
+ $out .= " " . wfElementClean( 'ip', null, strval( $row->rev_user_text ) ) . "\n";
+ }
+ $out .= " </contributor>\n";
}
- $out .= " </contributor>\n";
if( $row->rev_minor_edit ) {
$out .= " <minor/>\n";
}
- if( $row->rev_comment != '' ) {
+ if( $row->rev_deleted & MW_REV_DELETED_COMMENT ) {
+ $out .= " " . wfElement( 'comment', array( 'deleted' => 'deleted' ) ) . "\n";
+ } elseif( $row->rev_comment != '' ) {
$out .= " " . wfElementClean( 'comment', null, strval( $row->rev_comment ) ) . "\n";
}
- if( isset( $row->old_text ) ) {
+ if( $row->rev_deleted & MW_REV_DELETED_TEXT ) {
+ $out .= " " . wfElement( 'text', array( 'deleted' => 'deleted' ) ) . "\n";
+ } elseif( isset( $row->old_text ) ) {
// Raw text from the database may have invalid chars
$text = strval( Revision::getRevisionText( $row ) );
$out .= " " . wfElementClean( 'text',
require_once( 'UpdateClasses.php' );
require_once( 'LogPage.php' );
require_once( 'normal/UtfNormalUtil.php' );
+require_once( 'XmlFunctions.php' );
/**
* Compatibility functions
return $out;
}
-/**
- * Returns an escaped string suitable for inclusion in a string literal
- * for JavaScript source code.
- * Illegal control characters are assumed not to be present.
- *
- * @param string $string
- * @return string
- */
-function wfEscapeJsString( $string ) {
- // See ECMA 262 section 7.8.4 for string literal format
- $pairs = array(
- "\\" => "\\\\",
- "\"" => "\\\"",
- '\'' => '\\\'',
- "\n" => "\\n",
- "\r" => "\\r",
-
- # To avoid closing the element or CDATA section
- "<" => "\\x3c",
- ">" => "\\x3e",
- );
- return strtr( $string, $pairs );
-}
/**
* @todo document
return (float)$st[0] + (float)$st[1];
}
-/**
- * Changes the first character to an HTML entity
- */
-function wfHtmlEscapeFirst( $text ) {
- $ord = ord($text);
- $newText = substr($text, 1);
- return "&#$ord;$newText";
-}
-
/**
* Sets dest to source and returns the original value of dest
* If source is NULL, it just returns the value, it doesn't set the variable
return( $siteNotice );
}
-/**
- * Format an XML element with given attributes and, optionally, text content.
- * Element and attribute names are assumed to be ready for literal inclusion.
- * Strings are assumed to not contain XML-illegal characters; special
- * characters (<, >, &) are escaped but illegals are not touched.
- *
- * @param string $element
- * @param array $attribs Name=>value pairs. Values will be escaped.
- * @param string $contents NULL to make an open tag only; '' for a contentless closed tag (default)
- * @return string
- */
-function wfElement( $element, $attribs = null, $contents = '') {
- $out = '<' . $element;
- if( !is_null( $attribs ) ) {
- foreach( $attribs as $name => $val ) {
- $out .= ' ' . $name . '="' . htmlspecialchars( $val ) . '"';
- }
- }
- if( is_null( $contents ) ) {
- $out .= '>';
- } else {
- if( $contents == '' ) {
- $out .= ' />';
- } else {
- $out .= '>' . htmlspecialchars( $contents ) . "</$element>";
- }
- }
- return $out;
-}
-
-/**
- * Format an XML element as with wfElement(), but run text through the
- * UtfNormal::cleanUp() validator first to ensure that no invalid UTF-8
- * is passed.
- *
- * @param string $element
- * @param array $attribs Name=>value pairs. Values will be escaped.
- * @param string $contents NULL to make an open tag only; '' for a contentless closed tag (default)
- * @return string
- */
-function wfElementClean( $element, $attribs = array(), $contents = '') {
- if( $attribs ) {
- $attribs = array_map( array( 'UtfNormal', 'cleanUp' ), $attribs );
- }
- if( $contents ) {
- $contents = UtfNormal::cleanUp( $contents );
- }
- return wfElement( $element, $attribs, $contents );
-}
-
-// Shortcuts
-function wfOpenElement( $element, $attribs = null ) { return wfElement( $element, $attribs, null ); }
-function wfCloseElement( $element ) { return "</$element>"; }
-
-/**
- * Create a namespace selector
- *
- * @param mixed $selected The namespace which should be selected, default ''
- * @param string $allnamespaces Value of a special item denoting all namespaces. Null to not include (default)
- * @return Html string containing the namespace selector
- */
-function &HTMLnamespaceselector($selected = '', $allnamespaces = null) {
- global $wgContLang;
- if( $selected !== '' ) {
- if( is_null( $selected ) ) {
- // No namespace selected; let exact match work without hitting Main
- $selected = '';
- } else {
- // Let input be numeric strings without breaking the empty match.
- $selected = intval( $selected );
- }
- }
- $s = "<select id='namespace' name='namespace' class='namespaceselector'>\n\t";
- $arr = $wgContLang->getFormattedNamespaces();
- if( !is_null($allnamespaces) ) {
- $arr = array($allnamespaces => wfMsgHtml('namespacesall')) + $arr;
- }
- foreach ($arr as $index => $name) {
- if ($index < NS_MAIN) continue;
-
- $name = $index !== 0 ? $name : wfMsgHtml('blanknamespace');
-
- if ($index === $selected) {
- $s .= wfElement("option",
- array("value" => $index, "selected" => "selected"),
- $name);
- } else {
- $s .= wfElement("option", array("value" => $index), $name);
- }
- }
- $s .= "\n</select>\n";
- return $s;
-}
-
/** Global singleton instance of MimeMagic. This is initialized on demand,
* please always use the wfGetMimeMagic() function to get the instance.
*
}
}
-/**
- * Check if a string is well-formed XML.
- * Must include the surrounding tag.
- *
- * @param string $text
- * @return bool
- *
- * @todo Error position reporting return
- */
-function wfIsWellFormedXml( $text ) {
- $parser = xml_parser_create( "UTF-8" );
-
- # case folding violates XML standard, turn it off
- xml_parser_set_option( $parser, XML_OPTION_CASE_FOLDING, false );
-
- if( !xml_parse( $parser, $text, true ) ) {
- $err = xml_error_string( xml_get_error_code( $parser ) );
- $position = xml_get_current_byte_index( $parser );
- //$fragment = $this->extractFragment( $html, $position );
- //$this->mXmlError = "$err at byte $position:\n$fragment";
- xml_parser_free( $parser );
- return false;
- }
- xml_parser_free( $parser );
- return true;
-}
-
-/**
- * Check if a string is a well-formed XML fragment.
- * Wraps fragment in an <html> bit and doctype, so it can be a fragment
- * and can use HTML named entities.
- *
- * @param string $text
- * @return bool
- */
-function wfIsWellFormedXmlFragment( $text ) {
- $html =
- Sanitizer::hackDocType() .
- '<html>' .
- $text .
- '</html>';
- return wfIsWellFormedXml( $html );
-}
-
/**
* shell_exec() with time and memory limits mirrored from the PHP configuration,
* if supported.
return '<a href="'.$url.'"'.$style.'>'.$text.'</a>';
}
+ /**
+ * Make user link (or user contributions for unregistered users)
+ * @param int $userId
+ * @param string $userText
+ * @return string HTML fragment
+ * @access private
+ */
+ function userLink( $userId, $userText ) {
+ $encName = htmlspecialchars( $userText );
+ if( $userId == 0 ) {
+ $contribsPage = Title::makeTitle( NS_SPECIAL, 'Contributions' );
+ return $this->makeKnownLinkObj( $contribsPage,
+ $encName, 'target=' . urlencode( $userText ) );
+ } else {
+ $userPage = Title::makeTitle( NS_USER, $userText );
+ return $this->makeLinkObj( $userPage, $encName );
+ }
+ }
+
+ /**
+ * @param int $userId
+ * @param string $userText
+ * @return string HTML fragment with talk and/or block links
+ * @access private
+ */
+ function userToolLinks( $userId, $userText ) {
+ global $wgUser, $wgDisableAnonTalk, $wgSysopUserBans;
+ $talkable = !( $wgDisableAnonTalk && 0 == $userId );
+ $blockable = ( $wgSysopUserBans || 0 == $userId );
+
+ $items = array();
+ if( $talkable ) {
+ $items[] = $this->userTalkLink( $userId, $userText );
+ }
+ if( $blockable && $wgUser->isAllowed( 'block' ) ) {
+ $items[] = $this->blockLink( $userId, $userText );
+ }
+
+ if( $items ) {
+ return ' (' . implode( ' | ', $items ) . ')';
+ } else {
+ return '';
+ }
+ }
+
+ /**
+ * @param int $userId
+ * @param string $userText
+ * @return string HTML fragment with user talk link
+ * @access private
+ */
+ function userTalkLink( $userId, $userText ) {
+ global $wgContLang;
+ $talkname = $wgContLang->getNsText( NS_TALK ); # use the shorter name
+
+ $userTalkPage = Title::makeTitle( NS_USER_TALK, $userText );
+ $userTalkLink = $this->makeLinkObj( $userTalkPage, $talkname );
+ return $userTalkLink;
+ }
+
+ /**
+ * @param int $userId
+ * @param string $userText
+ * @return string HTML fragment with block link
+ * @access private
+ */
+ function blockLink( $userId, $userText ) {
+ $blockPage = Title::makeTitle( NS_SPECIAL, 'Blockip' );
+ $blockLink = $this->makeKnownLinkObj( $blockPage,
+ wfMsgHtml( 'blocklink' ), 'ip=' . urlencode( $userText ) );
+ return $blockLink;
+ }
+
+ /**
+ * Generate a user link if the current user is allowed to view it
+ * @param Revision $rev
+ * @return string HTML
+ */
+ function revUserLink( $rev ) {
+ if( $rev->userCan( MW_REV_DELETED_USER ) ) {
+ $link = $this->userLink( $rev->getRawUser(), $rev->getRawUserText() );
+ } else {
+ $link = wfMsgHtml( 'rev-deleted-user' );
+ }
+ if( $rev->isDeleted( MW_REV_DELETED_USER ) ) {
+ return '<span class="history-deleted">' . $link . '</span>';
+ }
+ return $link;
+ }
+
+ /**
+ * Generate a user tool link cluster if the current user is allowed to view it
+ * @param Revision $rev
+ * @return string HTML
+ */
+ function revUserTools( $rev ) {
+ if( $rev->userCan( MW_REV_DELETED_USER ) ) {
+ $link = $this->userLink( $rev->getRawUser(), $rev->getRawUserText() ) .
+ ' ' .
+ $this->userToolLinks( $rev->getRawUser(), $rev->getRawUserText() );
+ } else {
+ $link = wfMsgHtml( 'rev-deleted-user' );
+ }
+ if( $rev->isDeleted( MW_REV_DELETED_USER ) ) {
+ return '<span class="history-deleted">' . $link . '</span>';
+ }
+ return $link;
+ }
+
/**
* This function is called by all recent changes variants, by the page history,
* and by the user contributions list. It is responsible for formatting edit
*
* @param string $comment
* @param Title $title
- * @param bool $deleted
*
* @return string
*/
- function commentBlock( $comment, $title = NULL, $deleted = false ) {
+ function commentBlock( $comment, $title = NULL ) {
// '*' used to be the comment inserted by the software way back
// in antiquity in case none was provided, here for backwards
// compatability, acc. to brion -ævar
if( $comment == '' || $comment == '*' ) {
return '';
} else {
- if ( $deleted )
- return " <span class='comment'>(...)</span>";
- else {
- $formatted = $this->formatComment( $comment, $title );
- return " <span class='comment'>($formatted)</span>";
- }
+ $formatted = $this->formatComment( $comment, $title );
+ return " <span class='comment'>($formatted)</span>";
+ }
+ }
+
+ /**
+ * Wrap and format the given revision's comment block, if the current
+ * user is allowed to view it.
+ * @param Revision $rev
+ * @return string HTML
+ */
+ function revComment( $rev ) {
+ if( $rev->userCan( MW_REV_DELETED_COMMENT ) ) {
+ $block = $this->commentBlock( $rev->getRawComment(), $rev->getTitle() );
+ } else {
+ $block = " <span class='comment'>" .
+ wfMsgHtml( 'rev-deleted-comment' ) . "</span>";
+ }
+ if( $rev->isDeleted( MW_REV_DELETED_COMMENT ) ) {
+ return " <span class='history-deleted'>$block</span>";
}
+ return $block;
}
/** @todo document */
'delete/delete' => 'deletedarticle',
'delete/restore' => 'undeletedarticle',
+ 'delete/revision' => 'revdelete-logentry',
'upload/upload' => 'uploadedimage',
'upload/revert' => 'uploadedimage',
'move/move' => '1movedto2',
/** @todo document */
function historyLine( $row, $next, $counter = '', $notificationtimestamp = false, $latest = false, $firstInList = false ) {
-
- if ( 0 == $row->rev_user ) {
- $contribsPage =& Title::makeTitle( NS_SPECIAL, 'Contributions' );
- $ul = $this->mSkin->makeKnownLinkObj( $contribsPage,
- htmlspecialchars( $row->rev_user_text ),
- 'target=' . urlencode( $row->rev_user_text ) );
- } else {
- $userPage =& Title::makeTitle( NS_USER, $row->rev_user_text );
- $ul = $this->mSkin->makeLinkObj( $userPage , htmlspecialchars( $row->rev_user_text ) );
- }
+ global $wgUser;
+ $rev = new Revision( $row );
$s = '<li>';
- /* This feature is not yet used according to schema */
- if( $row->rev_deleted ) {
- $s .= '<span class="history-deleted">';
+ $curlink = $this->curLink( $rev, $latest );
+ $lastlink = $this->lastLink( $rev, $next, $counter );
+ $arbitrary = $this->diffButtons( $rev, $firstInList, $counter );
+ $link = $this->revLink( $rev );
+ $user = $this->mSkin->revUserLink( $rev );
+
+ $s .= "($curlink) ($lastlink) $arbitrary";
+
+ if( $wgUser->isAllowed( 'deleterevision' ) ) {
+ $revdel = Title::makeTitle( NS_SPECIAL, 'Revisiondelete' );
+ if( $firstInList ) {
+ // We don't currently handle well changing the top revision's settings
+ $del = wfMsgHtml( 'rev-delundel' );
+ } else {
+ $del = $this->mSkin->makeKnownLinkObj( $revdel,
+ wfMsg( 'rev-delundel' ),
+ 'target=' . urlencode( $this->mTitle->getPrefixedDbkey() ) .
+ '&oldid=' . urlencode( $rev->getId() ) );
+ }
+ $s .= "(<small>$del</small>) ";
}
- $curlink = $this->curLink( $row, $latest );
- $lastlink = $this->lastLink( $row, $next, $counter );
- $arbitrary = $this->diffButtons( $row, $firstInList, $counter );
- $link = $this->revLink( $row );
-
- $s .= "($curlink) ($lastlink) $arbitrary $link <span class='history-user'>$ul</span>";
+
+ $s .= " $link <span class='history-user'>$user</span>";
if( $row->rev_minor_edit ) {
$s .= ' ' . wfElement( 'span', array( 'class' => 'minor' ), wfMsgHtml( 'minoreditletter') );
}
- $s .= $this->mSkin->commentBlock( $row->rev_comment, $this->mTitle );
+ $s .= $this->mSkin->revComment( $rev );
if ($notificationtimestamp && ($row->rev_timestamp >= $notificationtimestamp)) {
$s .= ' <span class="updatedmarker">' . wfMsgHtml( 'updatedmarker' ) . '</span>';
}
- if( $row->rev_deleted ) {
- $s .= '</span> ' . wfMsgHtml( 'deletedrev' );
+ if( $row->rev_deleted & MW_REV_DELETED_TEXT ) {
+ $s .= ' ' . wfMsgHtml( 'deletedrev' );
}
$s .= "</li>\n";
return $s;
}
-
+
/** @todo document */
- function revLink( $row ) {
+ function revLink( $rev ) {
global $wgUser, $wgLang;
- $date = $wgLang->timeanddate( wfTimestamp(TS_MW, $row->rev_timestamp), true );
- if( $row->rev_deleted && !$wgUser->isAllowed( 'undelete' ) ) {
- return $date;
+ $date = $wgLang->timeanddate( wfTimestamp(TS_MW, $rev->getTimestamp()), true );
+ if( $rev->userCan( MW_REV_DELETED_TEXT ) ) {
+ $link = $this->mSkin->makeKnownLinkObj(
+ $this->mTitle, $date, "oldid=" . $rev->getId() );
} else {
- return $this->mSkin->makeKnownLinkObj(
- $this->mTitle, $date, "oldid={$row->rev_id}" );
+ $link = $date;
+ }
+ if( $rev->isDeleted( MW_REV_DELETED_TEXT ) ) {
+ return '<span class="history-deleted">' . $link . '</span>';
}
+ return $link;
}
/** @todo document */
- function curLink( $row, $latest ) {
+ function curLink( $rev, $latest ) {
global $wgUser;
$cur = wfMsgHtml( 'cur' );
- if( $latest
- || ( $row->rev_deleted && !$wgUser->isAllowed( 'undelete' ) ) ) {
+ if( $latest || !$rev->userCan( MW_REV_DELETED_TEXT ) ) {
return $cur;
} else {
return $this->mSkin->makeKnownLinkObj(
$this->mTitle, $cur,
'diff=' . $this->getLatestID() .
- "&oldid={$row->rev_id}" );
+ "&oldid=" . $rev->getId() );
}
}
/** @todo document */
- function lastLink( $row, $next, $counter ) {
+ function lastLink( $rev, $next, $counter ) {
global $wgUser;
$last = htmlspecialchars( wfMsg( 'last' ) );
if( is_null( $next ) ) {
- if( $row->rev_timestamp == $this->getEarliestOffset() ) {
+ if( $rev->getTimestamp() == $this->getEarliestOffset() ) {
return $last;
} else {
// Cut off by paging; there are more behind us...
return $this->mSkin->makeKnownLinkObj(
$this->mTitle,
$last,
- "diff={$row->rev_id}&oldid=prev" );
+ "diff=" . $rev->getId() . "&oldid=prev" );
}
- } elseif( $row->rev_deleted && !$wgUser->isAllowed( 'undelete' ) ) {
+ } elseif( !$rev->userCan( MW_REV_DELETED_TEXT ) ) {
return $last;
} else {
return $this->mSkin->makeKnownLinkObj(
$this->mTitle,
$last,
- "diff={$row->rev_id}&oldid={$next->rev_id}"
+ "diff=" . $rev->getId() . "&oldid={$next->rev_id}"
/*,
'',
'',
}
/** @todo document */
- function diffButtons( $row, $firstInList, $counter ) {
+ function diffButtons( $rev, $firstInList, $counter ) {
global $wgUser;
if( $this->linesonpage > 1) {
$radio = array(
'type' => 'radio',
- 'value' => $row->rev_id,
+ 'value' => $rev->getId(),
# do we really need to flood this on every item?
# 'title' => wfMsgHtml( 'selectolderversionfordiff' )
);
- if( $row->rev_deleted && !$wgUser->isAllowed( 'undelete' ) ) {
+ if( !$rev->userCan( MW_REV_DELETED_TEXT ) ) {
$radio['disabled'] = 'disabled';
}
$res = $dbr->select(
'revision',
- array('rev_id', 'rev_user', 'rev_comment', 'rev_user_text',
+ array('rev_id', 'rev_page', 'rev_text_id', 'rev_user', 'rev_comment', 'rev_user_text',
'rev_timestamp', 'rev_minor_edit', 'rev_deleted'),
array_merge(array("rev_page=$page_id"), $offsets),
$fname,
if ( $rev ) {
$lastmod = wfTimestamp( TS_RFC2822, $rev->getTimestamp() );
header( "Last-modified: $lastmod" );
- $text = $rev->isDeleted() ? '' : $rev->getText();
+ $text = $rev->getText();
} else
$text = '';
}
require_once( 'Database.php' );
require_once( 'Article.php' );
+/** @+ */
+define( 'MW_REV_DELETED_TEXT', 1 );
+define( 'MW_REV_DELETED_COMMENT', 2 );
+define( 'MW_REV_DELETED_USER', 4 );
+define( 'MW_REV_DELETED_RESTRICTED', 8 );
+/** @- */
+
/**
* @package MediaWiki
* @todo document
$this->mTimestamp = $row->rev_timestamp;
$this->mDeleted = intval( $row->rev_deleted );
- $this->mCurrent = ( $row->rev_id == $row->page_latest );
- $this->mTitle = Title::makeTitle( $row->page_namespace,
- $row->page_title );
+ if( isset( $row->page_latest ) ) {
+ $this->mCurrent = ( $row->rev_id == $row->page_latest );
+ $this->mTitle = Title::makeTitle( $row->page_namespace,
+ $row->page_title );
+ } else {
+ $this->mCurrent = false;
+ $this->mTitle = null;
+ }
if( isset( $row->old_text ) ) {
$this->mText = $this->getRevisionText( $row );
}
/**
+ * Fetch revision's user id if it's available to all users
* @return int
*/
function getUser() {
+ if( $this->isDeleted( MW_REV_DELETED_USER ) ) {
+ return 0;
+ } else {
+ return $this->mUser;
+ }
+ }
+
+ /**
+ * Fetch revision's user id without regard for the current user's permissions
+ * @return string
+ */
+ function getRawUser() {
return $this->mUser;
}
/**
+ * Fetch revision's username if it's available to all users
* @return string
*/
function getUserText() {
- return $this->mUserText;
+ if( $this->isDeleted( MW_REV_DELETED_USER ) ) {
+ return "";
+ } else {
+ return $this->mUserText;
+ }
}
/**
+ * Fetch revision's username without regard for view restrictions
+ * @return string
+ */
+ function getRawUserText() {
+ return $this->mUserText;
+ }
+
+ /**
+ * Fetch revision comment if it's available to all users
* @return string
*/
function getComment() {
+ if( $this->isDeleted( MW_REV_DELETED_COMMENT ) ) {
+ return "";
+ } else {
+ return $this->mComment;
+ }
+ }
+
+ /**
+ * Fetch revision comment without regard for the current user's permissions
+ * @return string
+ */
+ function getRawComment() {
return $this->mComment;
}
}
/**
+ * int $field one of MW_REV_DELETED_* bitfield constants
* @return bool
*/
- function isDeleted() {
- return (bool)$this->mDeleted;
+ function isDeleted( $field ) {
+ return ($this->mDeleted & $field) == $field;
}
/**
+ * Fetch revision text if it's available to all users
* @return string
*/
function getText() {
+ if( $this->isDeleted( MW_REV_DELETED_TEXT ) ) {
+ return "";
+ } else {
+ return $this->getRawText();
+ }
+ }
+
+ /**
+ * Fetch revision text without regard for view restrictions
+ * @return string
+ */
+ function getRawText() {
if( is_null( $this->mText ) ) {
// Revision text is immutable. Load on demand:
$this->mText = $this->loadText();
wfProfileOut( $fname );
return $revision;
}
+
+ /**
+ * Determine if the current user is allowed to view a particular
+ * field of this revision, if it's marked as deleted.
+ * @param int $field one of MW_REV_DELETED_TEXT,
+ * MW_REV_DELETED_COMMENT,
+ * MW_REV_DELETED_USER
+ * @return bool
+ */
+ function userCan( $field ) {
+ if( ( $this->mDeleted & $field ) == $field ) {
+ global $wgUser;
+ $permission = ( $this->mDeleted & MW_REV_DELETED_RESTRICTED ) == MW_REV_DELETED_RESTRICTED
+ ? 'hiderevision'
+ : 'deleterevision';
+ wfDebug( "Checking for $permission due to $field match on $this->mDeleted\n" );
+ return $wgUser->isAllowed( $permission );
+ } else {
+ return true;
+ }
+ }
}
+
?>
$use_index = $this->dbr->useIndexClause($index);
$sql = "SELECT
page_namespace,page_title,page_is_new,page_latest,
- rev_id,rev_timestamp,rev_comment,rev_minor_edit,rev_user_text,
+ rev_id,rev_page,rev_text_id,rev_timestamp,rev_comment,rev_minor_edit,rev_user,rev_user_text,
rev_deleted
FROM $page,$revision $use_index
WHERE page_id=rev_page AND $userCond $nscond $offsetQuery
}
}
- $page =& Title::makeTitle( $row->page_namespace, $row->page_title );
- $link = $sk->makeKnownLinkObj( $page, '' );
+ $rev = new Revision( $row );
+
+ $page = Title::makeTitle( $row->page_namespace, $row->page_title );
+ $link = $sk->makeKnownLinkObj( $page );
$difftext = $topmarktext = '';
if( $row->rev_id == $row->page_latest ) {
$topmarktext .= '<strong>' . $messages['uctop'] . '</strong>';
}
}
- if( $row->rev_deleted && !$wgUser->isAllowed( 'delete' ) ) {
- $difftext = '(' . $messages['diff'] . ')';
- } else {
+ if( $rev->userCan( MW_REV_DELETED_TEXT ) ) {
$difftext = '(' . $sk->makeKnownLinkObj( $page, $messages['diff'], 'diff=prev&oldid='.$row->rev_id ) . ')';
+ } else {
+ $difftext = '(' . $messages['diff'] . ')';
}
$histlink='('.$sk->makeKnownLinkObj( $page, $messages['hist'], 'action=history' ) . ')';
- $comment = $sk->commentBlock( $row->rev_comment, $page, (bool)$row->rev_deleted );
+ $comment = $sk->revComment( $rev );
$d = $wgLang->timeanddate( wfTimestamp(TS_MW, $row->rev_timestamp), true );
+
+ if( $rev->isDeleted( MW_REV_DELETED_TEXT ) ) {
+ $d = '<span class="history-deleted">' . $d . '</span>';
+ }
if( $row->rev_minor_edit ) {
$mflag = '<span class="minor">' . $messages['minoreditletter'] . '</span> ';
}
$ret = "{$d} {$histlink} {$difftext} {$mflag} {$link} {$comment} {$topmarktext}";
- if( $row->rev_deleted ) {
- $ret = "<span class='deleted'>$ret</span>";
+ if( $rev->isDeleted( MW_REV_DELETED_TEXT ) ) {
+ $ret .= ' ' . wfMsgHtml( 'deletedrev' );
}
$ret = "<li>$ret</li>\n";
wfProfileOut( $fname );
'Userrights' => new SpecialPage( 'Userrights', 'userrights' ),
'MIMEsearch' => new SpecialPage( 'MIMEsearch' ),
'Unwatchedpages' => new SpecialPage( 'Unwatchedpages', 'unwatchedpages' ),
- 'Listredirects' => new SpecialPage( 'Listredirects' )
+ 'Listredirects' => new SpecialPage( 'Listredirects' ),
+ 'Revisiondelete' => new SpecialPage( 'Revisiondelete', 'deleterevision' ),
);
if( !$wgDisableCounters ) {
--- /dev/null
+<?php
+
+/**
+ * Not quite ready for production use yet; need to fix up the restricted mode,
+ * and provide for preservation across delete/undelete of the page.
+ *
+ * To try this out, set up extra permissions something like:
+ * $wgGroupPermissions['sysop']['deleterevision'] = true;
+ * $wgGroupPermissions['bureaucrat']['hiderevision'] = true;
+ */
+
+function wfSpecialRevisiondelete( $par = null ) {
+ global $wgOut, $wgRequest, $wgUser, $wgContLang;
+
+ $target = $wgRequest->getVal( 'target' );
+ $oldid = $wgRequest->getInt( 'oldid' );
+
+ $sk = $wgUser->getSkin();
+ $page = Title::newFromUrl( $target );
+
+ if( is_null( $page ) ) {
+ $wgOut->errorpage( 'notargettitle', 'notargettext' );
+ return;
+ }
+
+ $form = new RevisionDeleteForm( $wgRequest );
+ if( $wgRequest->wasPosted() ) {
+ $form->submit( $wgRequest );
+ } else {
+ $form->show( $wgRequest );
+ }
+}
+
+class RevisionDeleteForm {
+ /**
+ * @param Title $page
+ * @param int $oldid
+ */
+ function __construct( $request ) {
+ global $wgUser;
+
+ $target = $request->getVal( 'target' );
+ $this->page = Title::newFromUrl( $target );
+
+ $this->revisions = $request->getIntArray( 'oldid', array() );
+
+ $this->skin = $wgUser->getSkin();
+ $this->checks = array(
+ array( 'revdelete-hide-text', 'wpHideText', MW_REV_DELETED_TEXT ),
+ array( 'revdelete-hide-comment', 'wpHideComment', MW_REV_DELETED_COMMENT ),
+ array( 'revdelete-hide-user', 'wpHideUser', MW_REV_DELETED_USER ),
+ array( 'revdelete-hide-restricted', 'wpHideRestricted', MW_REV_DELETED_RESTRICTED ) );
+ }
+
+ /**
+ * @param WebRequest $request
+ */
+ function show( $request ) {
+ global $wgOut, $wgUser;
+
+ $first = $this->revisions[0];
+
+ $wgOut->addWikiText( wfMsg( 'revdelete-selected', $this->page->getPrefixedText() ) );
+
+ $wgOut->addHtml( "<ul>" );
+ foreach( $this->revisions as $revid ) {
+ $rev = Revision::newFromTitle( $this->page, $revid );
+ $wgOut->addHtml( $this->historyLine( $rev ) );
+ $bitfields[] = $rev->mDeleted; // FIXME
+ }
+ $wgOut->addHtml( "</ul>" );
+
+ $wgOut->addWikiText( wfMsg( 'revdelete-text' ) );
+
+ $items = array(
+ wfInputLabel( wfMsg( 'revdelete-log' ), 'wpReason', 'wpReason', 60 ),
+ wfSubmitButton( wfMsg( 'revdelete-submit' ) ) );
+ $hidden = array(
+ wfHidden( 'wpEditToken', $wgUser->editToken() ),
+ wfHidden( 'target', $this->page->getPrefixedText() ) );
+ foreach( $this->revisions as $revid ) {
+ $hidden[] = wfHidden( 'oldid[]', $revid );
+ }
+
+ $special = Title::makeTitle( NS_SPECIAL, 'Revisiondelete' );
+ $wgOut->addHtml( wfElement( 'form', array(
+ 'method' => 'post',
+ 'action' => $special->getLocalUrl( 'action=submit' ) ) ) );
+
+ $wgOut->addHtml( '<fieldset><legend>' . wfMsgHtml( 'revdelete-legend' ) . '</legend>' );
+ foreach( $this->checks as $item ) {
+ list( $message, $name, $field ) = $item;
+ $wgOut->addHtml( '<div>' .
+ wfCheckLabel( wfMsg( $message), $name, $name, $rev->isDeleted( $field ) ) .
+ '</div>' );
+ }
+ $wgOut->addHtml( '</fieldset>' );
+ foreach( $items as $item ) {
+ $wgOut->addHtml( '<p>' . $item . '</p>' );
+ }
+ foreach( $hidden as $item ) {
+ $wgOut->addHtml( $item );
+ }
+
+ $wgOut->addHtml( '</form>' );
+ }
+
+ /**
+ * @param Revision $rev
+ * @returns string
+ */
+ private function historyLine( $rev ) {
+ global $wgContLang;
+ $date = $wgContLang->timeanddate( $rev->getTimestamp() );
+ return
+ "<li>" .
+ $this->skin->makeLinkObj( $this->page, $date, 'oldid=' . $rev->getId() ) .
+ " " .
+ $this->skin->revUserLink( $rev ) .
+ " " .
+ $this->skin->revComment( $rev ) .
+ "</li>";
+ }
+
+ /**
+ * @param WebRequest $request
+ */
+ function submit( $request ) {
+ $bitfield = $this->extractBitfield( $request );
+ $comment = $request->getText( 'wpReason' );
+ if( $this->save( $bitfield, $comment ) ) {
+ return $this->success( $request );
+ } else {
+ return $this->show( $request );
+ }
+ }
+
+ function success( $request ) {
+ global $wgOut;
+ $wgOut->addWikiText( 'woo' );
+ }
+
+ /**
+ * Put together a rev_deleted bitfield from the submitted checkboxes
+ * @param WebRequest $request
+ * @return int
+ */
+ function extractBitfield( $request ) {
+ $bitfield = 0;
+ foreach( $this->checks as $item ) {
+ list( $message, $name, $field ) = $item;
+ if( $request->getCheck( $name ) ) {
+ $bitfield |= $field;
+ }
+ }
+ return $bitfield;
+ }
+
+ function save( $bitfield, $reason ) {
+ $dbw = wfGetDB( DB_MASTER );
+ $deleter = new RevisionDeleter( $dbw );
+ $ok = $deleter->setVisibility( $this->revisions, $bitfield, $reason );
+ }
+}
+
+
+class RevisionDeleter {
+ function __construct( $db ) {
+ $this->db = $db;
+ }
+
+ /**
+ * @param array $items list of revision ID numbers
+ * @param int $bitfield new rev_deleted value
+ * @param string $comment Comment for log records
+ */
+ function setVisibility( $items, $bitfield, $comment ) {
+ $pages = array();
+
+ // To work!
+ foreach( $items as $revid ) {
+ $rev = Revision::newFromId( $revid );
+ $this->updateRevision( $rev, $bitfield );
+ $this->updateRecentChanges( $rev, $bitfield );
+
+ // For logging, maintain a count of revisions per page
+ $pageid = $rev->getPage();
+ if( isset( $pages[$pageid] ) ) {
+ $pages[$pageid]++;
+ } else {
+ $pages[$pageid] = 1;
+ }
+ }
+
+ // Clear caches...
+ foreach( $pages as $pageid => $count ) {
+ $title = Title::newFromId( $pageid );
+ $this->updatePage( $title );
+ $this->updateLog( $title, $count, $bitfield, $comment );
+ }
+
+ return true;
+ }
+
+ /**
+ * Update the revision's rev_deleted field
+ * @param Revision $rev
+ * @param int $bitfield new rev_deleted bitfield value
+ */
+ function updateRevision( $rev, $bitfield ) {
+ $this->db->update( 'revision',
+ array( 'rev_deleted' => $bitfield ),
+ array( 'rev_id' => $rev->getId() ),
+ 'RevisionDeleter::updateRevision' );
+ }
+
+ /**
+ * Update the revision's recentchanges record if fields have been hidden
+ * @param Revision $rev
+ * @param int $bitfield new rev_deleted bitfield value
+ */
+ function updateRecentChanges( $rev, $bitfield ) {
+ $this->db->update( 'recentchanges',
+ array(
+ 'rc_user' => ($bitfield & MW_REV_DELETED_USER) ? 0 : $rev->getUser(),
+ 'rc_user_text' => ($bitfield & MW_REV_DELETED_USER) ? wfMsg( 'rev-deleted-user' ) : $rev->getUserText(),
+ 'rc_comment' => ($bitfield & MW_REV_DELETED_COMMENT) ? wfMsg( 'rev-deleted-comment' ) : $rev->getComment() ),
+ array(
+ 'rc_this_oldid' => $rev->getId() ),
+ 'RevisionDeleter::updateRecentChanges' );
+ }
+
+ /**
+ * Touch the page's cache invalidation timestamp; this forces cached
+ * history views to refresh, so any newly hidden or shown fields will
+ * update properly.
+ * @param Title $title
+ */
+ function updatePage( $title ) {
+ $title->invalidateCache();
+ }
+
+ /**
+ * Record a log entry on the action
+ * @param Title $title
+ * @param int $count the number of revisions altered for this page
+ * @param int $bitfield the new rev_deleted value
+ * @param string $comment
+ */
+ function updateLog( $title, $count, $bitfield, $comment ) {
+ $log = new LogPage( 'delete' );
+ $reason = "changed $count revisions to $bitfield";
+ $reason .= ": $comment";
+ $log->addEntry( 'revision', $title, $reason );
+ }
+}
+
+?>
return (array)$val;
}
}
+
+ /**
+ * Fetch an array of integers, or return $default if it's not set.
+ * If source was scalar, will return an array with a single element.
+ * If no source and no default, returns NULL.
+ * If an array is returned, contents are guaranteed to be integers.
+ *
+ * @param string $name
+ * @param array $default option default (or NULL)
+ * @return array of ints
+ */
+ function getIntArray( $name, $default = NULL ) {
+ $val = $this->getArray( $name, $default );
+ if( is_array( $val ) ) {
+ $val = array_map( 'intval', $val );
+ }
+ return $val;
+ }
/**
* Fetch an integer value from the input or return $default if not set.
--- /dev/null
+<?php
+
+/**
+ * Format an XML element with given attributes and, optionally, text content.
+ * Element and attribute names are assumed to be ready for literal inclusion.
+ * Strings are assumed to not contain XML-illegal characters; special
+ * characters (<, >, &) are escaped but illegals are not touched.
+ *
+ * @param string $element
+ * @param array $attribs Name=>value pairs. Values will be escaped.
+ * @param string $contents NULL to make an open tag only; '' for a contentless closed tag (default)
+ * @return string
+ */
+function wfElement( $element, $attribs = null, $contents = '') {
+ $out = '<' . $element;
+ if( !is_null( $attribs ) ) {
+ foreach( $attribs as $name => $val ) {
+ $out .= ' ' . $name . '="' . htmlspecialchars( $val ) . '"';
+ }
+ }
+ if( is_null( $contents ) ) {
+ $out .= '>';
+ } else {
+ if( $contents == '' ) {
+ $out .= ' />';
+ } else {
+ $out .= '>' . htmlspecialchars( $contents ) . "</$element>";
+ }
+ }
+ return $out;
+}
+
+/**
+ * Format an XML element as with wfElement(), but run text through the
+ * UtfNormal::cleanUp() validator first to ensure that no invalid UTF-8
+ * is passed.
+ *
+ * @param string $element
+ * @param array $attribs Name=>value pairs. Values will be escaped.
+ * @param string $contents NULL to make an open tag only; '' for a contentless closed tag (default)
+ * @return string
+ */
+function wfElementClean( $element, $attribs = array(), $contents = '') {
+ if( $attribs ) {
+ $attribs = array_map( array( 'UtfNormal', 'cleanUp' ), $attribs );
+ }
+ if( $contents ) {
+ $contents = UtfNormal::cleanUp( $contents );
+ }
+ return wfElement( $element, $attribs, $contents );
+}
+
+// Shortcuts
+function wfOpenElement( $element, $attribs = null ) { return wfElement( $element, $attribs, null ); }
+function wfCloseElement( $element ) { return "</$element>"; }
+
+/**
+ * Create a namespace selector
+ *
+ * @param mixed $selected The namespace which should be selected, default ''
+ * @param string $allnamespaces Value of a special item denoting all namespaces. Null to not include (default)
+ * @param bool $includehidden Include hidden namespaces?
+ * @return Html string containing the namespace selector
+ */
+function &HTMLnamespaceselector($selected = '', $allnamespaces = null, $includehidden=false) {
+ global $wgContLang;
+ if( $selected !== '' ) {
+ if( is_null( $selected ) ) {
+ // No namespace selected; let exact match work without hitting Main
+ $selected = '';
+ } else {
+ // Let input be numeric strings without breaking the empty match.
+ $selected = intval( $selected );
+ }
+ }
+ $s = "<select name='namespace' class='namespaceselector'>\n\t";
+ $arr = $wgContLang->getFormattedNamespaces();
+ if( !is_null($allnamespaces) ) {
+ $arr = array($allnamespaces => wfMsgHtml('namespacesall')) + $arr;
+ }
+ foreach ($arr as $index => $name) {
+ if ($index < NS_MAIN) continue;
+
+ $name = $index !== 0 ? $name : wfMsgHtml('blanknamespace');
+
+ if ($index === $selected) {
+ $s .= wfElement("option",
+ array("value" => $index, "selected" => "selected"),
+ $name);
+ } else {
+ $s .= wfElement("option", array("value" => $index), $name);
+ }
+ }
+ $s .= "\n</select>\n";
+ return $s;
+}
+
+function wfSpan( $text, $class, $attribs=array() ) {
+ return wfElement( 'span', array( 'class' => $class ) + $attribs, $text );
+}
+
+/**
+ * Convenience function to build an HTML text input field
+ * @return string HTML
+ */
+function wfInput( $name, $size=false, $value=false, $attribs=array() ) {
+ return wfElement( 'input', array(
+ 'name' => $name,
+ 'size' => $size,
+ 'value' => $value ) + $attribs );
+}
+
+/**
+ * Internal function for use in checkboxes and radio buttons and such.
+ * @return array
+ */
+function wfAttrib( $name, $present = true ) {
+ return $present ? array( $name => $name ) : array();
+}
+
+/**
+ * Convenience function to build an HTML checkbox
+ * @return string HTML
+ */
+function wfCheck( $name, $checked=false, $attribs=array() ) {
+ return wfElement( 'input', array(
+ 'name' => $name,
+ 'type' => 'checkbox',
+ 'value' => 1 ) + wfAttrib( 'checked', $checked ) + $attribs );
+}
+
+/**
+ * Convenience function to build an HTML radio button
+ * @return string HTML
+ */
+function wfRadio( $name, $value, $checked=false, $attribs=array() ) {
+ return wfElement( 'input', array(
+ 'name' => $name,
+ 'type' => 'radio',
+ 'value' => $value ) + wfAttrib( 'checked', $checked ) + $attribs );
+}
+
+/**
+ * Convenience function to build an HTML form label
+ * @return string HTML
+ */
+function wfLabel( $label, $id ) {
+ return wfElement( 'label', array( 'for' => $id ), $label );
+}
+
+/**
+ * Convenience function to build an HTML text input field with a label
+ * @return string HTML
+ */
+function wfInputLabel( $label, $name, $id, $size=false, $value=false, $attribs=array() ) {
+ return wfLabel( $label, $id ) .
+ ' ' .
+ wfInput( $name, $size, $value, array( 'id' => $id ) + $attribs );
+}
+
+/**
+ * Convenience function to build an HTML checkbox with a label
+ * @return string HTML
+ */
+function wfCheckLabel( $label, $name, $id, $checked=false, $attribs=array() ) {
+ return wfCheck( $name, $checked, array( 'id' => $id ) + $attribs ) .
+ ' ' .
+ wfLabel( $label, $id );
+}
+
+/**
+ * Convenience function to build an HTML radio button with a label
+ * @return string HTML
+ */
+function wfRadioLabel( $label, $name, $value, $id, $checked=false, $attribs=array() ) {
+ return wfRadio( $name, $checked, $value, array( 'id' => $id ) + $attribs ) .
+ ' ' .
+ wfLabel( $label, $id );
+}
+
+/**
+ * Convenience function to build an HTML submit button
+ * @param string $value Label text for the button
+ * @param array $attribs optional custom attributes
+ * @return string HTML
+ */
+function wfSubmitButton( $value, $attribs=array() ) {
+ return wfElement( 'input', array( 'type' => 'submit', 'value' => $value ) + $attribs );
+}
+
+/**
+ * Convenience function to build an HTML hidden form field
+ * @param string $value Label text for the button
+ * @param array $attribs optional custom attributes
+ * @return string HTML
+ */
+function wfHidden( $name, $value, $attribs=array() ) {
+ return wfElement( 'input', array(
+ 'name' => $name,
+ 'type' => 'hidden',
+ 'value' => $value ) + $attribs );
+}
+
+/**
+ * Returns an escaped string suitable for inclusion in a string literal
+ * for JavaScript source code.
+ * Illegal control characters are assumed not to be present.
+ *
+ * @param string $string
+ * @return string
+ */
+function wfEscapeJsString( $string ) {
+ // See ECMA 262 section 7.8.4 for string literal format
+ $pairs = array(
+ "\\" => "\\\\",
+ "\"" => "\\\"",
+ '\'' => '\\\'',
+ "\n" => "\\n",
+ "\r" => "\\r",
+
+ # To avoid closing the element or CDATA section
+ "<" => "\\x3c",
+ ">" => "\\x3e",
+ );
+ return strtr( $string, $pairs );
+}
+
+/**
+ * Check if a string is well-formed XML.
+ * Must include the surrounding tag.
+ *
+ * @param string $text
+ * @return bool
+ *
+ * @todo Error position reporting return
+ */
+function wfIsWellFormedXml( $text ) {
+ $parser = xml_parser_create( "UTF-8" );
+
+ # case folding violates XML standard, turn it off
+ xml_parser_set_option( $parser, XML_OPTION_CASE_FOLDING, false );
+
+ if( !xml_parse( $parser, $text, true ) ) {
+ $err = xml_error_string( xml_get_error_code( $parser ) );
+ $position = xml_get_current_byte_index( $parser );
+ //$fragment = $this->extractFragment( $html, $position );
+ //$this->mXmlError = "$err at byte $position:\n$fragment";
+ xml_parser_free( $parser );
+ return false;
+ }
+ xml_parser_free( $parser );
+ return true;
+}
+
+/**
+ * Check if a string is a well-formed XML fragment.
+ * Wraps fragment in an <html> bit and doctype, so it can be a fragment
+ * and can use HTML named entities.
+ *
+ * @param string $text
+ * @return bool
+ */
+function wfIsWellFormedXmlFragment( $text ) {
+ $html =
+ Sanitizer::hackDocType() .
+ '<html>' .
+ $text .
+ '</html>';
+ return wfIsWellFormedXml( $html );
+}
+
+
+?>
\ No newline at end of file
'deletedrev' => '[deleted]',
'histfirst' => 'Earliest',
'histlast' => 'Latest',
+'rev-deleted-comment' => '(comment removed)',
+'rev-deleted-user' => '(username removed)',
+'rev-deleted-text-permission' => '<div class="mw-warning plainlinks">
+This page revision has been removed from the public archives.
+There may be details in the [{{fullurl:Special:Log/delete|page={{PAGENAMEE}}}} deletion log].
+</div>',
+'rev-deleted-text-view' => '<div class="mw-warning plainlinks">
+This page revision has been removed from the public archives.
+As an administrator on this site you can view it;
+there may be details in the [{{fullurl:Special:Log/delete|page={{PAGENAMEE}}}} deletion log].
+</div>',
+#'rev-delundel' => 'del/undel',
+'rev-delundel' => 'show/hide',
+
+# Revision deletion
+#
+'revisiondelete' => 'Delete/undelete revisions',
+'revdelete-selected' => 'Selected revision of [[:$1]]:',
+'revdelete-text' => "Deleted revisions will still appear in the page history,
+but their text contents will be inaccessible to the public.
+
+Other admins on this wiki will still be able to access the hidden content and can
+undelete it again through this same interface, unless an additional restriction
+is placed by the site operators.",
+'revdelete-legend' => 'Set revision restrictions:',
+'revdelete-hide-text' => 'Hide revision text',
+'revdelete-hide-comment' => 'Hide edit comment',
+'revdelete-hide-user' => 'Hide editor\'s username/IP',
+'revdelete-hide-restricted' => 'Apply these restrictions to sysops as well as others',
+'revdelete-log' => 'Log comment:',
+'revdelete-submit' => 'Apply to selected revision',
+'revdelete-logentry' => 'changed revision visibility for [[$1]]',
# Diffs
#
*/
#toc,
-.toc {
+.toc,
+.mw-warning {
border: 1px solid #aaa;
background-color: #f9f9f9;
padding: 5px;
font-size: 94%;
}
+.mw-warning {
+ margin-left: 50px;
+ margin-right: 50px;
+ text-align: center;
+}
/* images */
div.floatright, table.floatright {