merged master

Change-Id: Iad12ee382d6aeb1fab6fefb611d290b74865ea4b
This commit is contained in:
daniel 2012-07-23 22:07:18 +02:00
commit d87135d706
544 changed files with 16373 additions and 7901 deletions

23
.jshintignore Normal file
View file

@ -0,0 +1,23 @@
# upstream libs
resources/jquery/jquery.appear.js
resources/jquery/jquery.async.js
resources/jquery/jquery.cycle.all.js
resources/jquery/jquery.cookie.js
resources/jquery/jquery.farbtastic.js
resources/jquery/jquery.form.js
resources/jquery/jquery.hoverIntent.js
resources/jquery/jquery.js
resources/jquery/jquery.json.js
resources/jquery/jquery.mockjax.js
resources/jquery/jquery.qunit.js
resources/jquery/jquery.validate.js
resources/jquery/jquery.xmldom.js
resources/jquery.effects
resources/jquery.tipsy
resources/jquery.ui
resources/mediawiki.libs/mediawiki.libs.jpegmeta.js
tests/jasmine/lib/jasmine-1.0.1/jasmine-html.js
tests/jasmine/lib/jasmine-1.0.1/jasmine.js
# legacy stuff
skins/common

22
.jshintrc Normal file
View file

@ -0,0 +1,22 @@
{
"predef": [
"mediaWiki",
"jQuery",
"QUnit"
],
"bitwise": true,
"curly": true,
"eqeqeq": true,
"immed": true,
"latedef": true,
"newcap": true,
"noempty": true,
"undef": true,
"trailing": true,
"laxbreak": true,
"smarttabs": true,
"browser": true
}

3
README
View file

@ -46,8 +46,7 @@ The MediaWiki software was written by:
* Victor Vasiliev
* Rotem Liss
* Platonides
* Antoine Musso
* Several others (view CREDITS for a more complete list)
* Many others (view CREDITS for a more complete list)
The contributors hold the copyright to this work, and it is licensed under the
terms of the GNU General Public License, version 2 or later (see

View file

@ -20,11 +20,18 @@ upgrade PHP if you have not done so prior to upgrading MediaWiki.
* `$wgUsePathInfo = true;` is no longer needed to make $wgArticlePath work on servers
using like nginx, lighttpd, and apache over fastcgi. MediaWiki now always extracts
path info from REQUEST_URI if it's available.
* The user right 'upload_by_url' is no longer given to sysops by default.
This only affects installations which have $wgAllowCopyUploads set to true.
* Removed f-prot support from $wgAntivirusSetup.
* $wgDBerrorLogInUTC to log error in $wgDBerrorLog using an UTC date instead
of the wiki timezone set by $wgLocalTimezone.
=== New features in 1.20 ===
* Added TitleIsAlwaysKnown hook which gets called when determining if a page exists.
* Added NamespaceIsMovable hook which gets called when determining if pages in a
certain namespace can be moved.
* Added SpecialPageBeforeExecute hook which gets called before SpecialPage::execute.
* Added SpecialPageAfterExecute hook which gets called after SpecialPage::execute.
* (bug 32341) Add upload by URL domain limitation.
* &useskin=default will now always display the default skin. Useful for users with a
preference for the non-default skin to look at something using the default skin.
@ -55,6 +62,8 @@ upgrade PHP if you have not done so prior to upgrading MediaWiki.
* Edit notices can now be translated.
* (bug 35680) jQuery upgraded to 1.7.2.
* jQuery UI upgraded to 1.8.21.
* (bug 35705) QUnit upgraded from v1.2.0 to v1.8.0.
* (bug 37604) jquery.cookie upgraded to 2011 version.
* (bug 22887) Add warning and tracking category for preprocessor errors
* (bug 31704) Allow selection of associated namespace on the watchlist
* (bug 5445) Now remove autoblocks when a user is unblocked.
@ -67,7 +76,6 @@ upgrade PHP if you have not done so prior to upgrading MediaWiki.
* New getCreator and getOldestRevision methods added to WikiPage class
* (bug 4220) the XML dump format schema now have unique identity constraints
for page and revision identifiers. Patch by Elvis Stansvik.
* (bug 35705) QUnit upgraded from v1.2.0 to v1.8.0.
* cleanupSpam.php now can delete spam pages if --delete was specified instead of blanking
them.
* Added new hook ChangePasswordForm to allow adding of additional fields in Special:ChangePassword
@ -76,6 +84,23 @@ upgrade PHP if you have not done so prior to upgrading MediaWiki.
Will be null on previewing a page being created.
* Added new hook AfterFinalPageOutput to allow modifications to buffered page output before sent
to the client.
* (bug 37627) UserNotLoggedIn() exception to show a generic error page whenever
a user is not logged in.
* Watched status in changes lists are no longer indicated by <strong></strong>
tags with class "mw-watched". Instead, each line now has a class
"mw-changeslist-line-watched" or "mw-changeslist-line-not-watched", and the
title itself is surrounded by <span></span> tags with class "mw-title".
* Added ContribsPager::reallyDoQuery hook allowing extensions to data to MyContribs
* Added new hook ParserAfterParse to allow extensions to affect parsed output
after the parse is complete but before block level processing, link holder
replacement, and so on.
* (bug 34678) Added InternalParseBeforeSanitize hook which gets called during Parser's
internalParse method just before the parser removes unwanted/dangerous HTML tags.
* (bug 36783) Implement jQuery Promise interface in mediawiki.api module.
* Make dates in sortable tables sort according to the page content language
instead of the site content language
* (bug 37926) Deleterevision will no longer allow users to delete log entries,
the new deletelogentry permission is required for this.
=== Bug fixes in 1.20 ===
* (bug 30245) Use the correct way to construct a log page title.
@ -118,16 +143,34 @@ upgrade PHP if you have not done so prior to upgrading MediaWiki.
* (bug 35572) Blocks appear to succeed even if query fails due to wrong DB structure
* (bug 31757) Add a word-separator between help-messages in HTMLForm
* (bug 30410) Removed deprecated $wgFilterCallback and the 'filtered' API error.
* (bug 32604) Some messages needs escaping of wikitext inside username
* (bug 32604) Some messages needs escaping of wikitext inside username.
* (bug 36537) Rename wfArrayToCGI to wfArrayToCgi for consistency with wfCgiToArray.
* (bug 25946) The message on the top of Special:RecentChanges is now displayed
* (bug 25946) The message on the top of Special:RecentChanges is now displayed.
in user language instead of content language.
* (bug 35264) Wrong type used for <ns> in export.xsd
* (bug 24985) Use $wgTmpDirectory as the default temp directory so that people
who don't have access to /tmp can specify an alternative.
* (bug 27283) SqlBagOStuff breaks PostgreSQL transactions.
* (bug 35727) mw.Api ajax() should put token parameter last.
* (bug 260) Handle <pre> overflow automatically with a scroll bar.
* (bug 37708) mw.Uri.clone() should make a deep copy.
* (bug 38024) ResourceLoader should not create empty stylesheets for modules
that don't have stylesheets.
* (bug 36812) Special:ActiveUsers "Hide bots" should hide users from any group
having the "bot" user right, instead of just the default "bot" user group.
* (bug 35082) mw.util.addPortletLink incorrectly adds link to mutiple <ul> tags.
* (bug 36495) Sanitizer::fixDeprecatedAttributes should convert "align"
attribute to margin or float instead of text-align (for non-table-cells).
* (bug 36991) jquery.tablesorter should extract date sort format from date
string instead of global config. Dates like "April 1 2012" and "1 April 2012"
now sort correctly regardless of the content language's DefaultDateFormat.
* (bug 31895) mw.loader mode now correct when triggered from a $.fn.ready
handler that is bound before mediawiki.js's handler (e.g. browser-userscripts
like greasemonkey).
* (bug 38152) jquery.tablesorter: Use .data() instead of .attr(), so that live
values are used instead of just the fixed values from when the tablesorter
was initialized.
* (bug 38093) Gender of changed user groups missing in Special:Log/rights
=== API changes in 1.20 ===
* (bug 34316) Add ability to retrieve maximum upload size from MediaWiki API.
@ -139,13 +182,24 @@ upgrade PHP if you have not done so prior to upgrading MediaWiki.
* (bug 32497) API now allows changing of protection level using pageid.
* (bug 32498) API now allows comparing pages using pageids.
* (bug 30975) API import of pages with invalid characters in this wiki leads to Fatal Error.
* (bug 30488) API now allows listing of backlinks/embeddedin/imageusage per pageid
* (bug 34927) Output media_type for list=filearchive
* (bug 28814) add properties to output of action=parse
* (bug 33224) add variants of content language to meta=siteinfo
* (bug 36761) "Mark pages as visited" now submits previously established filter options
* (bug 32643) action=purge with forcelinkupdate no longer crashes when ratelimit is reached
* The paraminfo module now also contains result properties for most modules
* (bug 30488) API now allows listing of backlinks/embeddedin/imageusage per pageid.
* (bug 34927) Output media_type for list=filearchive.
* (bug 28814) add properties to output of action=parse.
* (bug 33224) add variants of content language to meta=siteinfo.
* (bug 36761) "Mark pages as visited" now submits previously established filter options.
* (bug 32643) action=purge with forcelinkupdate no longer crashes when ratelimit is reached.
* The paraminfo module now also contains result properties for most modules.
* (bug 32348) Allow descending order for list=alllinks.
* (bug 31777) Upload unknown error ``fileexists-forbidden''.
* (bug 32382) Allow descending order for list=iwbacklinks.
* (bug 32381) Allow descending order for list=backlinks, list=embeddedin and list=imageusage.
* (bug 32383) Allow descending order for list=langbacklinks.
* API meta=siteinfo can now return the list of known variable IDs.
* (bug 35980) list=deletedrevs now honors drdir correctly in "all" mode (mode #3).
* (bug 29290) API avoids mangling fields in continuation parameters
* (bug 36987) API avoids mangling fields in continuation parameters
* (bug 30836) siteinfo prop=specialpagealiases will no longer return nonexistent special pages
* (bug 38190) Add "required" flag to some token params for hint in api docs.
=== Languages updated in 1.20 ===
@ -153,6 +207,7 @@ MediaWiki supports over 350 languages. Many localisations are updated
regularly. Below only new and removed languages are listed, as well as
changes to languages because of Bugzilla reports.
* Emilian (egl) added.
* Tornedalen Finnish (fit) added.
* Mizo (lus) added.
* Santali (sat) added.

View file

@ -76,7 +76,7 @@ behaviour of MediaWiki.
Extensions usually need to be upgraded at the same time as the MediaWiki core.
In MediaWiki 1.14 some extensions are migrated into the core. Please see the
In MediaWiki 1.14 some extensions were migrated into the core. Please see the
HISTORY section "Migrated extensions" and disable these extensions in your
LocalSettings.php

39
api.php
View file

@ -62,43 +62,6 @@ if ( !$wgEnableAPI ) {
die(1);
}
// Selectively allow cross-site AJAX
/**
* Helper function to convert wildcard string into a regex
* '*' => '.*?'
* '?' => '.'
*
* @param $search string
* @return string
*/
function convertWildcard( $search ) {
$search = preg_quote( $search, '/' );
$search = str_replace(
array( '\*', '\?' ),
array( '.*?', '.' ),
$search
);
return "/$search/";
}
if ( $wgCrossSiteAJAXdomains && isset( $_SERVER['HTTP_ORIGIN'] ) ) {
$exceptions = array_map( 'convertWildcard', $wgCrossSiteAJAXdomainExceptions );
$regexes = array_map( 'convertWildcard', $wgCrossSiteAJAXdomains );
foreach ( $regexes as $regex ) {
if ( preg_match( $regex, $_SERVER['HTTP_ORIGIN'] ) ) {
foreach ( $exceptions as $exc ) { // Check against exceptions
if ( preg_match( $exc, $_SERVER['HTTP_ORIGIN'] ) ) {
break 2;
}
}
header( "Access-Control-Allow-Origin: {$_SERVER['HTTP_ORIGIN']}" );
header( 'Access-Control-Allow-Credentials: true' );
break;
}
}
}
// Set a dummy $wgTitle, because $wgTitle == null breaks various things
// In a perfect world this wouldn't be necessary
$wgTitle = Title::makeTitle( NS_MAIN, 'API' );
@ -107,7 +70,7 @@ $wgTitle = Title::makeTitle( NS_MAIN, 'API' );
* is some form of an ApiMain, possibly even one that produces an error message,
* but we don't care here, as that is handled by the ctor.
*/
$processor = new ApiMain( $wgRequest, $wgEnableWriteAPI );
$processor = new ApiMain( RequestContext::getMain(), $wgEnableWriteAPI );
// Process data & print results
$processor->execute();

View file

@ -23,6 +23,7 @@
Fix type for <ns> from "positiveInteger" to "nonNegativeInteger" to allow 0
Moves <logitem> to its right location.
Add parentid to revision.
Fix type for <id> within <contributor> to "nonNegativeInteger"
The canonical URL to the schema document is:
http://www.mediawiki.org/xml/export-0.7.xsd
@ -227,7 +228,7 @@
<complexType name="ContributorType">
<sequence>
<element name="username" type="string" minOccurs="0" />
<element name="id" type="positiveInteger" minOccurs="0" />
<element name="id" type="nonNegativeInteger" minOccurs="0" />
<element name="ip" type="string" minOccurs="0" />
</sequence>

View file

@ -245,6 +245,10 @@ $block: The block from which the autoblock is coming.
'AbortDiffCache': Can be used to cancel the caching of a diff
&$diffEngine: DifferenceEngine object
'AbortEmailNotification': Can be used to cancel email notifications for an edit.
$editor: The User who made the change.
$title: The Title of the page that was edited.
'AbortLogin': Return false to cancel account login.
$user: the User object being authenticated against
$password: the password being submitted, not yet checked for validity
@ -437,6 +441,8 @@ $user: the user (object) deleting the article
$reason: the reason (string) the article is being deleted
$error: if the deletion was prohibited, the (raw HTML) error message to display
(added in 1.13)
$status: Status object, modify this to throw an error. Overridden by $error
(added in 1.20)
'ArticleDeleteComplete': after an article is deleted
$article: the WikiPage that was deleted
@ -762,10 +768,18 @@ $modeName: the requested content model name
&$pager: Pager object for contributions
&$queryInfo: The query for the contribs Pager
'ContribsPager::reallyDoQuery': Called before really executing the query for My Contributions
&$data: an array of results of all contribs queries
$pager: The ContribsPager object hooked into
$offset: Index offset, inclusive
$limit: Exact query limit
$descending: Query direction, false for ascending, true for descending
'ContributionsLineEnding': Called before a contributions HTML line is finished
$page: SpecialPage object for contributions
$ret: the HTML line
&$ret: the HTML line
$row: the DB row for this line
&$classes: the classes to add to the surrounding <li>
'ContributionsToolLinks': Change tool links above Special:Contributions
$id: User identifier
@ -1201,8 +1215,16 @@ $prefix: interwiki prefix we are looking for.
&$iwData: output array describing the interwiki with keys iw_url, iw_local,
iw_trans and optionally iw_api and iw_wikiid.
'InternalParseBeforeSanitize': during Parser's internalParse method just before the
parser removes unwanted/dangerous HTML tags and after nowiki/noinclude/includeonly/
onlyinclude and other processings. Ideal for syntax-extensions after template/parser
function execution which respect nowiki and HTML-comments.
&$parser: Parser object
&$text: string containing partially parsed text
&$stripState: Parser's internal StripState object
'InternalParseBeforeLinks': during Parser's internalParse method before links
but after noinclude/includeonly/onlyinclude and other processing.
but after nowiki/noinclude/includeonly/onlyinclude and other processings.
&$parser: Parser object
&$text: string containing partially parsed text
&$stripState: Parser's internal StripState object
@ -1542,6 +1564,12 @@ A parser extension which depends on user options should install
this hook and append its values to the key.
$hash: reference to a hash key string which can be modified
'ParserAfterParse': Called from Parser::parse() just after the call to
Parser::internalParse() returns
$parser: parser object
$text: text being parsed
$stripState: stripState used (object)
'ParserAfterStrip': Same as ParserBeforeStrip
'ParserAfterTidy': Called after Parser::tidy() in Parser::parse()
@ -1680,6 +1708,11 @@ $out: OutputPage object
'RecentChange_save': called at the end of RecentChange::save()
$recentChange: RecentChange object
'RedirectSpecialArticleRedirectParams': lets you alter the set of
parameter names such as "oldid" that are preserved when using
redirecting special pages such as Special:MyPage and Special:MyTalk.
&$redirectParams: An array of parameters preserved by redirecting special pages.
'RequestContextCreateSkin': Called when RequestContext::getSkin creates a skin instance.
Can be used by an extension override what skin is used in certain contexts.
IContextSource $context: The RequestContext the skin is being created for.
@ -1911,6 +1944,14 @@ Each key maps to an associative array with a 'msg' (message key) and a 'default'
hook to remove a core special page
$list: list (array) of core special pages
'SpecialPageAfterExecute': called after SpecialPage::execute
$special: the SpecialPage object
$subPage: the subpage string or null if no subpage was specified
'SpecialPageBeforeExecute': called before SpecialPage::execute
$special: the SpecialPage object
$subPage: the subpage string or null if no subpage was specified
'SpecialPasswordResetOnSubmit': when executing a form submission on Special:PasswordReset
$users: array of User objects
$data: array of data submitted by the user

View file

@ -1448,10 +1448,12 @@ class Article extends Page {
$this->doDelete( $reason, $suppress );
if ( $request->getCheck( 'wpWatch' ) && $user->isLoggedIn() ) {
WatchAction::doWatch( $title, $user );
} elseif ( $title->userIsWatching() ) {
WatchAction::doUnwatch( $title, $user );
if ( $user->isLoggedIn() && $request->getCheck( 'wpWatch' ) != $user->isWatched( $title ) ) {
if ( $request->getCheck( 'wpWatch' ) ) {
WatchAction::doWatch( $title, $user );
} else {
WatchAction::doUnwatch( $title, $user );
}
}
return;
@ -1521,7 +1523,7 @@ class Article extends Page {
} else {
$suppress = '';
}
$checkWatch = $user->getBoolOption( 'watchdeletion' ) || $this->getTitle()->userIsWatching();
$checkWatch = $user->getBoolOption( 'watchdeletion' ) || $user->isWatched( $this->getTitle() );
$form = Xml::openElement( 'form', array( 'method' => 'post',
'action' => $this->getTitle()->getLocalURL( 'action=delete' ), 'id' => 'deleteconfirm' ) ) .
@ -1605,7 +1607,8 @@ class Article extends Page {
public function doDelete( $reason, $suppress = false ) {
$error = '';
$outputPage = $this->getContext()->getOutput();
if ( $this->mPage->doDeleteArticle( $reason, $suppress, 0, true, $error ) ) {
$status = $this->mPage->doDeleteArticleReal( $reason, $suppress, 0, true, $error );
if ( $status->isGood() ) {
$deleted = $this->getTitle()->getPrefixedText();
$outputPage->setPageTitle( wfMessage( 'actioncomplete' ) );
@ -1618,8 +1621,9 @@ class Article extends Page {
} else {
$outputPage->setPageTitle( wfMessage( 'cannotdelete-title', $this->getTitle()->getPrefixedText() ) );
if ( $error == '' ) {
$errors = $status->getErrorsArray();
$outputPage->wrapWikiMsg( "<div class=\"error mw-error-cannotdelete\">\n$1\n</div>",
array( 'cannotdelete', wfEscapeWikiText( $this->getTitle()->getPrefixedText() ) )
$errors[0]
);
$outputPage->addHTML( Xml::element( 'h2', null, LogPage::logName( 'delete' ) ) );

View file

@ -433,6 +433,7 @@ $wgAutoloadLocalClasses = array(
'LinkCache' => 'includes/cache/LinkCache.php',
'MessageCache' => 'includes/cache/MessageCache.php',
'ObjectFileCache' => 'includes/cache/ObjectFileCache.php',
'ProcessCacheLRU' => 'includes/cache/ProcessCacheLRU.php',
'ResourceFileCache' => 'includes/cache/ResourceFileCache.php',
'SquidUpdate' => 'includes/cache/SquidUpdate.php',
'TitleDependency' => 'includes/cache/CacheDependency.php',
@ -449,6 +450,9 @@ $wgAutoloadLocalClasses = array(
'IContextSource' => 'includes/context/IContextSource.php',
'RequestContext' => 'includes/context/RequestContext.php',
# includes/dao
'IDBAccessObject' => 'includes/dao/IDBAccessObject.php',
# includes/db
'Blob' => 'includes/db/DatabaseUtility.php',
'ChronologyProtector' => 'includes/db/LBFactory.php',

View file

@ -73,7 +73,8 @@ interface ICacheHelper {
function saveCache();
/**
* Sets the time to live for the cache, in seconds or a unix timestamp indicating the point of expiry..
* Sets the time to live for the cache, in seconds or a unix timestamp
* indicating the point of expiry...
*
* @since 1.20
*
@ -319,7 +320,8 @@ class CacheHelper implements ICacheHelper {
}
/**
* Sets the time to live for the cache, in seconds or a unix timestamp indicating the point of expiry..
* Sets the time to live for the cache, in seconds or a unix timestamp
* indicating the point of expiry...
*
* @since 1.20
*

View file

@ -35,6 +35,8 @@ class ChangeTags {
*
*/
static function formatSummaryRow( $tags, $page ) {
global $wgLang;
if( !$tags )
return array( '', array() );
@ -51,7 +53,7 @@ class ChangeTags {
);
$classes[] = Sanitizer::escapeClass( "mw-tag-$tag" );
}
$markers = '(' . implode( ', ', $displayTags ) . ')';
$markers = wfMessage( 'parentheses' )->rawParams( $wgLang->commaList( $displayTags ) )->text();
$markers = Xml::tags( 'span', array( 'class' => 'mw-tag-markers' ), $markers );
return array( $markers, $classes );

View file

@ -108,7 +108,7 @@ class ChangesList extends ContextSource {
}
/**
* Sets the list to use a <li class="watchlist-(namespace)-(page)"> tag
* Sets the list to use a "<li class='watchlist-(namespace)-(page)'>" tag
* @param $value Boolean
*/
public function setWatchlistDivs( $value = true ) {
@ -123,7 +123,7 @@ class ChangesList extends ContextSource {
if( !isset( $this->message ) ) {
foreach ( explode( ' ', 'cur diff hist last blocklink history ' .
'semicolon-separator pipe-separator' ) as $msg ) {
$this->message[$msg] = wfMsgExt( $msg, array( 'escapenoentities' ) );
$this->message[$msg] = $this->msg( $msg )->escaped();
}
}
}
@ -145,7 +145,7 @@ class ChangesList extends ContextSource {
}
/**
* Provide the <abbr> element appropriate to a given abbreviated flag,
* Provide the "<abbr>" element appropriate to a given abbreviated flag,
* namely the flag indicating a new page, a minor edit, a bot edit, or an
* unpatrolled edit. By default in English it will contain "N", "m", "b",
* "!" respectively, plus it will have an appropriate title and class.
@ -192,6 +192,7 @@ class ChangesList extends ContextSource {
$this->rcCacheIndex = 0;
$this->lastdate = '';
$this->rclistOpen = false;
$this->getOutput()->addModuleStyles( 'mediawiki.special.changeslist' );
return '';
}
@ -199,22 +200,31 @@ class ChangesList extends ContextSource {
* Show formatted char difference
* @param $old Integer: bytes
* @param $new Integer: bytes
* @param $context IContextSource context to use
* @return String
*/
public static function showCharacterDifference( $old, $new ) {
global $wgRCChangedSizeThreshold, $wgLang, $wgMiserMode;
$szdiff = $new - $old;
public static function showCharacterDifference( $old, $new, IContextSource $context = null ) {
global $wgRCChangedSizeThreshold, $wgMiserMode;
$code = $wgLang->getCode();
static $fastCharDiff = array();
if ( !isset($fastCharDiff[$code]) ) {
$fastCharDiff[$code] = $wgMiserMode || wfMsgNoTrans( 'rc-change-size' ) === '$1';
if ( !$context ) {
$context = RequestContext::getMain();
}
$formattedSize = $wgLang->formatNum($szdiff);
$new = (int)$new;
$old = (int)$old;
$szdiff = $new - $old;
$lang = $context->getLanguage();
$code = $lang->getCode();
static $fastCharDiff = array();
if ( !isset($fastCharDiff[$code]) ) {
$fastCharDiff[$code] = $wgMiserMode || $context->msg( 'rc-change-size' )->plain() === '$1';
}
$formattedSize = $lang->formatNum( $szdiff );
if ( !$fastCharDiff[$code] ) {
$formattedSize = wfMsgExt( 'rc-change-size', array( 'parsemag' ), $formattedSize );
$formattedSize = $context->msg( 'rc-change-size', $formattedSize )->text();
}
if( abs( $szdiff ) > abs( $wgRCChangedSizeThreshold ) ) {
@ -234,11 +244,34 @@ class ChangesList extends ContextSource {
$formattedSizeClass = 'mw-plusminus-neg';
}
$formattedTotalSize = wfMsgExt( 'rc-change-size-new', 'parsemag', $wgLang->formatNum( $new ) );
$formattedTotalSize = $context->msg( 'rc-change-size-new' )->numParams( $new )->text();
return Html::element( $tag,
array( 'dir' => 'ltr', 'class' => $formattedSizeClass, 'title' => $formattedTotalSize ),
wfMessage( 'parentheses', $formattedSize )->plain() ) . $wgLang->getDirMark();
$context->msg( 'parentheses', $formattedSize )->plain() ) . $lang->getDirMark();
}
/**
* Format the character difference of one or several changes.
*
* @param $old RecentChange
* @param $new RecentChange last change to use, if not provided, $old will be used
* @return string HTML fragment
*/
public function formatCharacterDifference( RecentChange $old, RecentChange $new = null ) {
$oldlen = $old->mAttribs['rc_old_len'];
if ( $new ) {
$newlen = $new->mAttribs['rc_new_len'];
} else {
$newlen = $old->mAttribs['rc_new_len'];
}
if( $oldlen === null || $newlen === null ) {
return '';
}
return self::showCharacterDifference( $oldlen, $newlen, $this->getContext() );
}
/**
@ -255,7 +288,7 @@ class ChangesList extends ContextSource {
public function insertDateHeader( &$s, $rc_timestamp ) {
# Make date header if necessary
$date = $this->getLanguage()->date( $rc_timestamp, true, true );
$date = $this->getLanguage()->userDate( $rc_timestamp, $this->getUser() );
if( $date != $this->lastdate ) {
if( $this->lastdate != '' ) {
$s .= "</ul>\n";
@ -339,10 +372,8 @@ class ChangesList extends ContextSource {
if( $this->isDeleted($rc,Revision::DELETED_TEXT) ) {
$articlelink = '<span class="history-deleted">' . $articlelink . '</span>';
}
# Bolden pages watched by this user
if( $watched ) {
$articlelink = "<strong class=\"mw-watched\">{$articlelink}</strong>";
}
# To allow for boldening pages watched by this user
$articlelink = "<span class=\"mw-title\">{$articlelink}</span>";
# RTL/LTR marker
$articlelink .= $this->getLanguage()->getDirMark();
@ -358,7 +389,7 @@ class ChangesList extends ContextSource {
*/
public function insertTimestamp( &$s, $rc ) {
$s .= $this->message['semicolon-separator'] .
$this->getLanguage()->time( $rc->mAttribs['rc_timestamp'], true, true ) . ' . . ';
$this->getLanguage()->userTime( $rc->mAttribs['rc_timestamp'], $this->getUser() ) . ' . . ';
}
/**
@ -369,7 +400,7 @@ class ChangesList extends ContextSource {
*/
public function insertUserRelatedLinks( &$s, &$rc ) {
if( $this->isDeleted( $rc, Revision::DELETED_USER ) ) {
$s .= ' <span class="history-deleted">' . wfMsgHtml( 'rev-deleted-user' ) . '</span>';
$s .= ' <span class="history-deleted">' . $this->msg( 'rev-deleted-user' )->escaped() . '</span>';
} else {
$s .= $this->getLanguage()->getDirMark() . Linker::userLink( $rc->mAttribs['rc_user'],
$rc->mAttribs['rc_user_text'] );
@ -385,12 +416,13 @@ class ChangesList extends ContextSource {
*/
public function insertLogEntry( $rc ) {
$formatter = LogFormatter::newFromRow( $rc->mAttribs );
$formatter->setContext( $this->getContext() );
$formatter->setShowUserToolLinks( true );
$mark = $this->getLanguage()->getDirMark();
return $formatter->getActionText() . " $mark" . $formatter->getComment();
}
/**
/**
* Insert a formatted comment
* @param $rc RecentChange
* @return string
@ -398,7 +430,7 @@ class ChangesList extends ContextSource {
public function insertComment( $rc ) {
if( $rc->mAttribs['rc_type'] != RC_MOVE && $rc->mAttribs['rc_type'] != RC_MOVE_OVER_REDIRECT ) {
if( $this->isDeleted( $rc, Revision::DELETED_COMMENT ) ) {
return ' <span class="history-deleted">' . wfMsgHtml( 'rev-deleted-comment' ) . '</span>';
return ' <span class="history-deleted">' . $this->msg( 'rev-deleted-comment' )->escaped() . '</span>';
} else {
return Linker::commentBlock( $rc->mAttribs['rc_comment'], $rc->getTitle() );
}
@ -422,8 +454,7 @@ class ChangesList extends ContextSource {
static $cache = array();
if( $count > 0 ) {
if( !isset( $cache[$count] ) ) {
$cache[$count] = wfMsgExt( 'number_of_watching_users_RCview',
array('parsemag', 'escape' ), $this->getLanguage()->formatNum( $count ) );
$cache[$count] = $this->msg( 'number_of_watching_users_RCview' )->numParams( $count )->escaped();
}
return $cache[$count];
} else {
@ -533,7 +564,9 @@ class OldChangesList extends ChangesList {
/**
* Format a line using the old system (aka without any javascript).
*
* @param $rc RecentChange
* @param $rc RecentChange, passed by reference
* @param $watched Bool (default false)
* @param $linenumber Int (default null)
* @return string
*/
public function recentChangesLine( &$rc, $watched = false, $linenumber = null ) {
@ -558,6 +591,10 @@ class OldChangesList extends ChangesList {
}
}
// Indicate watched status on the line to allow for more
// comprehensive styling.
$classes[] = $watched ? 'mw-changeslist-line-watched' : 'mw-changeslist-line-not-watched';
// Moved pages (very very old, not supported anymore)
if( $rc->mAttribs['rc_type'] == RC_MOVE || $rc->mAttribs['rc_type'] == RC_MOVE_OVER_REDIRECT ) {
// Log entries
@ -588,9 +625,9 @@ class OldChangesList extends ChangesList {
# Edit/log timestamp
$this->insertTimestamp( $s, $rc );
# Bytes added or removed
if( $wgRCShowChangedSize ) {
$cd = $rc->getCharacterDifference();
if( $cd != '' ) {
if ( $wgRCShowChangedSize ) {
$cd = $this->formatCharacterDifference( $rc );
if ( $cd !== '' ) {
$s .= "$cd . . ";
}
}
@ -614,8 +651,7 @@ class OldChangesList extends ChangesList {
# How many users watch this page
if( $rc->numberofWatchingusers > 0 ) {
$s .= ' ' . wfMsgExt( 'number_of_watching_users_RCview',
array( 'parsemag', 'escape' ), $this->getLanguage()->formatNum( $rc->numberofWatchingusers ) );
$s .= ' ' . $this->numberofWatchingusers( $rc->numberofWatchingusers );
}
if( $this->watchlist ) {
@ -667,7 +703,7 @@ class EnhancedChangesList extends ChangesList {
$curIdEq = array( 'curid' => $rc->mAttribs['rc_cur_id'] );
# If it's a new day, add the headline and flush the cache
$date = $this->getLanguage()->date( $rc->mAttribs['rc_timestamp'], true );
$date = $this->getLanguage()->userDate( $rc->mAttribs['rc_timestamp'], $this->getUser() );
$ret = '';
if( $date != $this->lastdate ) {
# Process current cache
@ -715,7 +751,7 @@ class EnhancedChangesList extends ChangesList {
$showdifflinks = false;
}
$time = $this->getLanguage()->time( $rc->mAttribs['rc_timestamp'], true, true );
$time = $this->getLanguage()->userTime( $rc->mAttribs['rc_timestamp'], $this->getUser() );
$rc->watched = $watched;
$rc->link = $clink;
$rc->timestamp = $time;
@ -764,7 +800,7 @@ class EnhancedChangesList extends ChangesList {
# Make user links
if( $this->isDeleted( $rc, Revision::DELETED_USER ) ) {
$rc->userlink = ' <span class="history-deleted">' . wfMsgHtml( 'rev-deleted-user' ) . '</span>';
$rc->userlink = ' <span class="history-deleted">' . $this->msg( 'rev-deleted-user' )->escaped() . '</span>';
} else {
$rc->userlink = Linker::userLink( $rc->mAttribs['rc_user'], $rc->mAttribs['rc_user_text'] );
$rc->usertalklink = Linker::userToolLinks( $rc->mAttribs['rc_user'], $rc->mAttribs['rc_user_text'] );
@ -808,14 +844,16 @@ class EnhancedChangesList extends ChangesList {
wfProfileIn( __METHOD__ );
# Add the namespace and title of the block as part of the class
$classes = array( 'mw-collapsible', 'mw-collapsed', 'mw-enhanced-rc' );
if ( $block[0]->mAttribs['rc_log_type'] ) {
# Log entry
$classes = 'mw-collapsible mw-collapsed mw-enhanced-rc ' . Sanitizer::escapeClass( 'mw-changeslist-log-'
$classes[] = Sanitizer::escapeClass( 'mw-changeslist-log-'
. $block[0]->mAttribs['rc_log_type'] . '-' . $block[0]->mAttribs['rc_title'] );
} else {
$classes = 'mw-collapsible mw-collapsed mw-enhanced-rc ' . Sanitizer::escapeClass( 'mw-changeslist-ns'
$classes[] = Sanitizer::escapeClass( 'mw-changeslist-ns'
. $block[0]->mAttribs['rc_namespace'] . '-' . $block[0]->mAttribs['rc_title'] );
}
$classes[] = $block[0]->watched ? 'mw-changeslist-line-watched' : 'mw-changeslist-line-not-watched';
$r = Html::openElement( 'table', array( 'class' => $classes ) ) .
Html::openElement( 'tr' );
@ -875,9 +913,9 @@ class EnhancedChangesList extends ChangesList {
}
$users = ' <span class="changedby">'
. $this->getContext()->msg( 'brackets' )->rawParams(
. $this->msg( 'brackets' )->rawParams(
implode( $this->message['semicolon-separator'], $users )
)->plain() . '</span>';
)->escaped() . '</span>';
$tl = '<span class="mw-collapsible-toggle mw-enhancedchanges-arrow"></span>';
$r .= "<td>$tl</td>";
@ -895,7 +933,7 @@ class EnhancedChangesList extends ChangesList {
# Article link
if( $namehidden ) {
$r .= ' <span class="history-deleted">' . wfMsgHtml( 'rev-deleted-event' ) . '</span>';
$r .= ' <span class="history-deleted">' . $this->msg( 'rev-deleted-event' )->escaped() . '</span>';
} elseif( $allLogs ) {
$r .= $this->maybeWatchedLink( $block[0]->link, $block[0]->watched );
} else {
@ -909,7 +947,7 @@ class EnhancedChangesList extends ChangesList {
$n = count($block);
static $nchanges = array();
if ( !isset( $nchanges[$n] ) ) {
$nchanges[$n] = wfMsgExt( 'nchanges', array( 'parsemag', 'escape' ), $this->getLanguage()->formatNum( $n ) );
$nchanges[$n] = $this->msg( 'nchanges' )->numParams( $n )->escaped();
}
# Total change link
$r .= ' ';
@ -970,8 +1008,7 @@ class EnhancedChangesList extends ChangesList {
$first--;
}
# Get net change
$chardiff = $rcObj->getCharacterDifference( $block[$first]->mAttribs['rc_old_len'],
$block[$last]->mAttribs['rc_new_len'] );
$chardiff = $this->formatCharacterDifference( $block[$first], $block[$last] );
if( $chardiff == '' ) {
$r .= ' ';
@ -1032,8 +1069,11 @@ class EnhancedChangesList extends ChangesList {
$r .= ' . . ';
# Character diff
if( $wgRCShowChangedSize && $rcObj->getCharacterDifference() ) {
$r .= $rcObj->getCharacterDifference() . ' . . ' ;
if ( $wgRCShowChangedSize ) {
$cd = $this->formatCharacterDifference( $rcObj );
if ( $cd !== '' ) {
$r .= "$cd . . ";
}
}
if ( $rcObj->mAttribs['rc_type'] == RC_LOG ) {
@ -1066,7 +1106,7 @@ class EnhancedChangesList extends ChangesList {
* @param $dir String: one of '', 'd', 'l', 'r'
* @param $alt String: text
* @param $title String: text
* @return String: HTML <img> tag
* @return String: HTML "<img>" tag
*/
protected function arrow( $dir, $alt='', $title='' ) {
global $wgStylePath;
@ -1079,26 +1119,25 @@ class EnhancedChangesList extends ChangesList {
/**
* Generate HTML for a right- or left-facing arrow,
* depending on language direction.
* @return String: HTML <img> tag
* @return String: HTML "<img>" tag
*/
protected function sideArrow() {
global $wgLang;
$dir = $wgLang->isRTL() ? 'l' : 'r';
return $this->arrow( $dir, '+', wfMsg( 'rc-enhanced-expand' ) );
$dir = $this->getLanguage()->isRTL() ? 'l' : 'r';
return $this->arrow( $dir, '+', $this->msg( 'rc-enhanced-expand' )->text() );
}
/**
* Generate HTML for a down-facing arrow
* depending on language direction.
* @return String: HTML <img> tag
* @return String: HTML "<img>" tag
*/
protected function downArrow() {
return $this->arrow( 'd', '-', wfMsg( 'rc-enhanced-hide' ) );
return $this->arrow( 'd', '-', $this->msg( 'rc-enhanced-hide' )->text() );
}
/**
* Generate HTML for a spacer image
* @return String: HTML <img> tag
* @return String: HTML "<img>" tag
*/
protected function spacerArrow() {
return $this->arrow( '', codepointToUtf8( 0xa0 ) ); // non-breaking space
@ -1118,14 +1157,16 @@ class EnhancedChangesList extends ChangesList {
$type = $rcObj->mAttribs['rc_type'];
$logType = $rcObj->mAttribs['rc_log_type'];
$classes = array( 'mw-enhanced-rc' );
if( $logType ) {
# Log entry
$classes = 'mw-enhanced-rc ' . Sanitizer::escapeClass( 'mw-changeslist-log-'
$classes[] = Sanitizer::escapeClass( 'mw-changeslist-log-'
. $logType . '-' . $rcObj->mAttribs['rc_title'] );
} else {
$classes = 'mw-enhanced-rc ' . Sanitizer::escapeClass( 'mw-changeslist-ns' .
$classes[] = Sanitizer::escapeClass( 'mw-changeslist-ns' .
$rcObj->mAttribs['rc_namespace'] . '-' . $rcObj->mAttribs['rc_title'] );
}
$classes[] = $rcObj->watched ? 'mw-changeslist-line-watched' : 'mw-changeslist-line-not-watched';
$r = Html::openElement( 'table', array( 'class' => $classes ) ) .
Html::openElement( 'tr' );
@ -1162,20 +1203,22 @@ class EnhancedChangesList extends ChangesList {
}
$r .= ' . . ';
# Character diff
if( $wgRCShowChangedSize && ($cd = $rcObj->getCharacterDifference()) ) {
$r .= "$cd . . ";
if ( $wgRCShowChangedSize ) {
$cd = $this->formatCharacterDifference( $rcObj );
if ( $cd !== '' ) {
$r .= "$cd . . ";
}
}
if ( $type == RC_LOG ) {
$r .= $this->insertLogEntry( $rcObj );
} else {
} else {
$r .= ' '.$rcObj->userlink . $rcObj->usertalklink;
$r .= $this->insertComment( $rcObj );
$this->insertRollback( $r, $rcObj );
}
# Tags
$classes = explode( ' ', $classes );
$this->insertTags( $r, $rcObj, $classes );
# Show how many people are watching this if enabled
$r .= $this->numberofWatchingusers($rcObj->numberofWatchingusers);

View file

@ -80,8 +80,8 @@ class Cookie {
* A better method might be to use a blacklist like
* http://publicsuffix.org/
*
* @fixme fails to detect 3-letter top-level domains
* @fixme fails to detect 2-letter top-level domains for single-domain use (probably not a big problem in practice, but there are test cases)
* @todo fixme fails to detect 3-letter top-level domains
* @todo fixme fails to detect 2-letter top-level domains for single-domain use (probably not a big problem in practice, but there are test cases)
*
* @param $domain String: the domain to validate
* @param $originDomain String: (optional) the domain the cookie originates from

View file

@ -24,6 +24,10 @@
/**
* Abstract base class for update jobs that do something with some secondary
* data extracted from article.
*
* @note: subclasses should NOT start or commit transactions in their doUpdate() method,
* a transaction will automatically be wrapped around the update. If need be,
* subclasses can override the beginTransaction() and commitTransaction() methods.
*/
abstract class DataUpdate implements DeferrableUpdate {

File diff suppressed because it is too large Load diff

View file

@ -88,10 +88,19 @@ class DeferredUpdates {
}
foreach ( $updates as $update ) {
$update->doUpdate();
try {
$update->doUpdate();
if ( $doCommit && $dbw->trxLevel() ) {
$dbw->commit( __METHOD__ );
if ( $doCommit && $dbw->trxLevel() ) {
$dbw->commit( __METHOD__ );
}
} catch ( MWException $e ) {
// We don't want exceptions thrown during deferred updates to
// be reported to the user since the output is already sent.
// Instead we just log them.
if ( !$e instanceof ErrorPageError ) {
wfDebugLog( 'exception', $e->getLogMessage() );
}
}
}

View file

@ -25,7 +25,7 @@
*/
/**
* @defgroup Constants
* @defgroup Constants MediaWiki constants
*/
/**

View file

@ -603,8 +603,8 @@ class EditPage {
wfProfileOut( get_class( $this ) . "::importContentFormData" );
}
# Truncate for whole multibyte characters. +5 bytes for ellipsis
$this->summary = $wgLang->truncate( $request->getText( 'wpSummary' ), 250 );
# Truncate for whole multibyte characters
$this->summary = $wgLang->truncate( $request->getText( 'wpSummary' ), 255 );
# If the summary consists of a heading, e.g. '==Foobar==', extract the title from the
# header syntax, e.g. 'Foobar'. This is mainly an issue when we are using wpSummary for
@ -616,7 +616,7 @@ class EditPage {
# currently doing double duty as both edit summary and section title. Right now this
# is just to allow API edits to work around this limitation, but this should be
# incorporated into the actual edit form when EditPage is rewritten (Bugs 18654, 26312).
$this->sectiontitle = $wgLang->truncate( $request->getText( 'wpSectionTitle' ), 250 );
$this->sectiontitle = $wgLang->truncate( $request->getText( 'wpSectionTitle' ), 255 );
$this->sectiontitle = preg_replace( '/^\s*=+\s*(.*?)\s*=+\s*$/', '$1', $this->sectiontitle );
$this->edittime = $request->getVal( 'wpEdittime' );
@ -778,7 +778,7 @@ class EditPage {
} elseif ( $wgUser->getOption( 'watchcreations' ) && !$this->mTitle->exists() ) {
# Watch creations
$this->watchthis = true;
} elseif ( $this->mTitle->userIsWatching() ) {
} elseif ( $wgUser->isWatched( $this->mTitle ) ) {
# Already watched
$this->watchthis = true;
}
@ -1306,9 +1306,9 @@ class EditPage {
wfProfileOut( __METHOD__ . '-checks' );
// Use SELECT FOR UPDATE here to avoid transaction collision in
// WikiPage::updateRevisionOn() and ending in the self::AS_END case.
$this->mArticle->loadPageData( 'forupdate' );
# Load the page data from the master. If anything changes in the meantime,
# we detect it by using page_latest like a token in a 1 try compare-and-swap.
$this->mArticle->loadPageData( 'fromdbmaster' );
$new = !$this->mArticle->exists();
try {
@ -1594,7 +1594,7 @@ class EditPage {
*/
protected function commitWatch() {
global $wgUser;
if ( $this->watchthis xor $this->mTitle->userIsWatching() ) {
if ( $wgUser->isLoggedIn() && $this->watchthis != $wgUser->isWatched( $this->mTitle ) ) {
$dbw = wfGetDB( DB_MASTER );
$dbw->begin( __METHOD__ );
if ( $this->watchthis ) {
@ -1639,7 +1639,7 @@ class EditPage {
* @private
* @todo document
*
* @parma $editText string
* @param $editText string
*
* @return bool
* @deprecated since 1.WD, use mergeChangesIntoContent() instead
@ -1734,7 +1734,7 @@ class EditPage {
/**
* Check given input text against $wgSpamRegex, and return the text of the first match.
*
* @parma $text string
* @param $text string
*
* @return string|bool matching string or false
*/
@ -1990,6 +1990,10 @@ class EditPage {
$wgOut->addHTML( Html::hidden( 'wpIgnoreBlankSummary', true ) );
}
if ( $this->undidRev ) {
$wgOut->addHTML( Html::hidden( 'wpUndidRevision', $this->undidRev ) );
}
if ( $this->hasPresetSummary ) {
// If a summary has been preset using &summary= we dont want to prompt for
// a different summary. Only prompt for a summary if the summary is blanked.
@ -2267,7 +2271,7 @@ class EditPage {
* @return array An array in the format array( $label, $input )
*/
function getSummaryInput( $summary = "", $labelText = null, $inputAttrs = null, $spanLabelAttrs = null ) {
// Note: the maxlength is overriden in JS to 250 and to make it use UTF-8 bytes, not characters.
// Note: the maxlength is overriden in JS to 255 and to make it use UTF-8 bytes, not characters.
$inputAttrs = ( is_array( $inputAttrs ) ? $inputAttrs : array() ) + array(
'id' => 'wpSummary',
'maxlength' => '200',
@ -2591,7 +2595,16 @@ HTML
'</div>' );
}
/**
* Get the copyright warning
*
* Renamed to getCopyrightWarning(), old name kept around for backwards compatibility
*/
protected function getCopywarn() {
return self::getCopyrightWarning( $this->mTitle );
}
public static function getCopyrightWarning( $title ) {
global $wgRightsText;
if ( $wgRightsText ) {
$copywarnMsg = array( 'copyrightwarning',
@ -2602,7 +2615,7 @@ HTML
'[[' . wfMsgForContent( 'copyrightpage' ) . ']]' );
}
// Allow for site and per-namespace customization of contribution/copyright notice.
wfRunHooks( 'EditPageCopyrightWarning', array( $this->mTitle, &$copywarnMsg ) );
wfRunHooks( 'EditPageCopyrightWarning', array( $title, &$copywarnMsg ) );
return "<div id=\"editpage-copywarn\">\n" .
call_user_func_array( "wfMsgNoTrans", $copywarnMsg ) . "\n</div>";
@ -3181,8 +3194,8 @@ HTML
* failure, etc).
*
* @todo This doesn't include category or interlanguage links.
* Would need to enhance it a bit, <s>maybe wrap them in XML
* or something...</s> that might also require more skin
* Would need to enhance it a bit, "<s>maybe wrap them in XML
* or something...</s>" that might also require more skin
* initialization, so check whether that's a problem.
*/
function livePreview() {
@ -3325,12 +3338,14 @@ HTML
* @private
*/
function checkUnicodeCompliantBrowser() {
global $wgBrowserBlackList;
if ( empty( $_SERVER["HTTP_USER_AGENT"] ) ) {
global $wgBrowserBlackList, $wgRequest;
$currentbrowser = $wgRequest->getHeader( 'User-Agent' );
if ( $currentbrowser === false ) {
// No User-Agent header sent? Trust it by default...
return true;
}
$currentbrowser = $_SERVER["HTTP_USER_AGENT"];
foreach ( $wgBrowserBlackList as $browser ) {
if ( preg_match( $browser, $currentbrowser ) ) {
return false;

View file

@ -33,7 +33,8 @@ class MWException extends Exception {
var $logId;
/**
* Should the exception use $wgOut to output the error ?
* Should the exception use $wgOut to output the error?
*
* @return bool
*/
function useOutputPage() {
@ -44,7 +45,8 @@ class MWException extends Exception {
}
/**
* Can the extension use wfMsg() to get i18n messages ?
* Can the extension use wfMsg() to get i18n messages?
*
* @return bool
*/
function useMessageCache() {
@ -62,9 +64,9 @@ class MWException extends Exception {
/**
* Run hook to allow extensions to modify the text of the exception
*
* @param $name String: class name of the exception
* @param $args Array: arguments to pass to the callback functions
* @return Mixed: string to output or null if any hook has been called
* @param $name string: class name of the exception
* @param $args array: arguments to pass to the callback functions
* @return string|null string to output or null if any hook has been called
*/
function runHooks( $name, $args = array() ) {
global $wgExceptionHooks;
@ -97,11 +99,11 @@ class MWException extends Exception {
/**
* Get a message from i18n
*
* @param $key String: message name
* @param $fallback String: default message if the message cache can't be
* @param $key string: message name
* @param $fallback string: default message if the message cache can't be
* called by the exception
* The function also has other parameters that are arguments for the message
* @return String message with arguments replaced
* @return string message with arguments replaced
*/
function msg( $key, $fallback /*[, params...] */ ) {
$args = array_slice( func_get_args(), 2 );
@ -118,7 +120,7 @@ class MWException extends Exception {
* backtrace to the error, otherwise show a message to ask to set it to true
* to show that information.
*
* @return String html to output
* @return string html to output
*/
function getHTML() {
global $wgShowExceptionDetails;
@ -128,10 +130,10 @@ class MWException extends Exception {
'</p><p>Backtrace:</p><p>' . nl2br( htmlspecialchars( $this->getTraceAsString() ) ) .
"</p>\n";
} else {
return
return
"<div class=\"errorbox\">" .
'[' . $this->getLogId() . '] ' .
gmdate( 'Y-m-d H:i:s' ) .
gmdate( 'Y-m-d H:i:s' ) .
": Fatal exception of type " . get_class( $this ) . "</div>\n" .
"<!-- Set \$wgShowExceptionDetails = true; " .
"at the bottom of LocalSettings.php to show detailed " .
@ -140,8 +142,10 @@ class MWException extends Exception {
}
/**
* Get the text to display when reporting the error on the command line.
* If $wgShowExceptionDetails is true, return a text message with a
* backtrace to the error.
*
* @return string
*/
function getText() {
@ -157,13 +161,21 @@ class MWException extends Exception {
}
/**
* Return titles of this error page
* @return String
* Return the title of the page when reporting this error in a HTTP response.
*
* @return string
*/
function getPageTitle() {
return $this->msg( 'internalerror', "Internal error" );
}
/**
* Get a random ID for this error.
* This allows to link the exception to its correspoding log entry when
* $wgShowExceptionDetails is set to false.
*
* @return string
*/
function getLogId() {
if ( $this->logId === null ) {
$this->logId = wfRandomString( 8 );
@ -175,7 +187,7 @@ class MWException extends Exception {
* Return the requested URL and point to file and line number from which the
* exception occured
*
* @return String
* @return string
*/
function getLogMessage() {
global $wgRequest;
@ -197,7 +209,9 @@ class MWException extends Exception {
return "[$id] $url Exception from line $line of $file: $message";
}
/** Output the exception report using HTML */
/**
* Output the exception report using HTML.
*/
function reportHTML() {
global $wgOut;
if ( $this->useOutputPage() ) {
@ -213,13 +227,20 @@ class MWException extends Exception {
$wgOut->output();
} else {
header( "Content-Type: text/html; charset=utf-8" );
echo "<!doctype html>\n" .
'<html><head>' .
'<title>' . htmlspecialchars( $this->getPageTitle() ) . '</title>' .
"</head><body>\n";
$hookResult = $this->runHooks( get_class( $this ) . "Raw" );
if ( $hookResult ) {
die( $hookResult );
echo $hookResult;
} else {
echo $this->getHTML();
}
echo $this->getHTML();
die(1);
echo "</body></html>\n";
die( 1 );
}
}
@ -254,7 +275,9 @@ class MWException extends Exception {
}
/**
* @static
* Check whether we are in command line mode or not to report the exception
* in the correct format.
*
* @return bool
*/
static function isCommandLine() {
@ -265,6 +288,7 @@ class MWException extends Exception {
/**
* Exception class which takes an HTML error message, and does not
* produce a backtrace. Replacement for OutputPage::fatalError().
*
* @ingroup Exception
*/
class FatalError extends MWException {
@ -285,14 +309,21 @@ class FatalError extends MWException {
}
/**
* An error page which can definitely be safely rendered using the OutputPage
* An error page which can definitely be safely rendered using the OutputPage.
*
* @ingroup Exception
*/
class ErrorPageError extends MWException {
public $title, $msg, $params;
/**
* @todo document
*
* Note: these arguments are keys into wfMsg(), not text!
*
* @param $title A title
* @param $msg String|Message . In string form, should be a message key
* @param $params Array Array to wfMsg()
*/
function __construct( $title, $msg, $params = null ) {
$this->title = $title;
@ -318,6 +349,8 @@ class ErrorPageError extends MWException {
* Show an error page on a badtitle.
* Similar to ErrorPage, but emit a 400 HTTP error code to let mobile
* browser it is not really a valid content.
*
* @ingroup Exception
*/
class BadTitleError extends ErrorPageError {
@ -347,6 +380,7 @@ class BadTitleError extends ErrorPageError {
/**
* Show an error when a user tries to do something they do not have the necessary
* permissions for.
*
* @ingroup Exception
*/
class PermissionsError extends ErrorPageError {
@ -383,7 +417,8 @@ class PermissionsError extends ErrorPageError {
/**
* Show an error when the wiki is locked/read-only and the user tries to do
* something that requires write access
* something that requires write access.
*
* @ingroup Exception
*/
class ReadOnlyError extends ErrorPageError {
@ -397,7 +432,8 @@ class ReadOnlyError extends ErrorPageError {
}
/**
* Show an error when the user hits a rate limit
* Show an error when the user hits a rate limit.
*
* @ingroup Exception
*/
class ThrottledError extends ErrorPageError {
@ -416,7 +452,8 @@ class ThrottledError extends ErrorPageError {
}
/**
* Show an error when the user tries to do something whilst blocked
* Show an error when the user tries to do something whilst blocked.
*
* @ingroup Exception
*/
class UserBlockedError extends ErrorPageError {
@ -457,6 +494,52 @@ class UserBlockedError extends ErrorPageError {
}
}
/**
* Shows a generic "user is not logged in" error page.
*
* This is essentially an ErrorPageError exception which by default use the
* 'exception-nologin' as a title and 'exception-nologin-text' for the message.
* @see bug 37627
*
* @par Example:
* @code
* if( $user->isAnon ) {
* throw new UserNotLoggedIn();
* }
* @endcode
*
* Please note the parameters are mixed up compared to ErrorPageError, this
* is done to be able to simply specify a reason whitout overriding the default
* title.
*
* @par Example:
* @code
* if( $user->isAnon ) {
* throw new UserNotLoggedIn( 'action-require-loggedin' );
* }
* @endcode
*
* @ingroup Exception
*/
class UserNotLoggedIn extends ErrorPageError {
/**
* @param $reasonMsg A message key containing the reason for the error.
* Optional, default: 'exception-nologin-text'
* @param $titleMsg A message key to set the page title.
* Optional, default: 'exception-nologin'
* @param $params Parameters to wfMsg().
* Optiona, default: null
*/
public function __construct(
$reasonMsg = 'exception-nologin-text',
$titleMsg = 'exception-nologin',
$params = null
) {
parent::__construct( $titleMsg, $reasonMsg, $params );
}
}
/**
* Show an error that looks like an HTTP server error.
* Replacement for wfHttpError().
@ -572,7 +655,8 @@ class MWExceptionHandler {
/**
* Print a message, if possible to STDERR.
* Use this in command line mode only (see isCommandLine)
* @param $message String Failure text
*
* @param $message string Failure text
*/
public static function printError( $message ) {
# NOTE: STDERR may not be available, especially if php-cgi is used from the command line (bug #15602).
@ -586,8 +670,9 @@ class MWExceptionHandler {
/**
* Print a message after escaping it and converting newlines to <br>
* Use this for non-command line failures
* @param $message String Failure text
* Use this for non-command line failures.
*
* @param $message string Failure text
*/
private static function escapeEchoAndDie( $message ) {
echo nl2br( htmlspecialchars( $message ) ) . "\n";

View file

@ -58,6 +58,14 @@ class WikiExporter {
*/
var $sink;
/**
* Returns the export schema version.
* @return string
*/
public static function schemaVersion() {
return "0.7"; #FIXME: bump this when pushing ContentHandler additions.
}
/**
* If using WikiExporter::STREAM to stream a large amount of data,
* provide a database connection which is not managed by
@ -465,14 +473,16 @@ class WikiExporter {
class XmlDumpWriter {
/**
* Returns the export schema version.
* @deprecated in 1.20; use WikiExporter::schemaVersion() instead
* @return string
*/
function schemaVersion() {
return "0.8"; #FIXME: Make sure to bump this to > 0.7 when merging Wikidata branch!
wfDeprecated( __METHOD__, '1.20' );
return WikiExporter::schemaVersion();
}
/**
* Opens the XML output stream's root <mediawiki> element.
* Opens the XML output stream's root "<mediawiki>" element.
* This does not include an xml directive, so is safe to include
* as a subelement in a larger XML stream. Namespace and XML Schema
* references are included.
@ -483,7 +493,7 @@ class XmlDumpWriter {
*/
function openStream() {
global $wgLanguageCode;
$ver = $this->schemaVersion();
$ver = WikiExporter::schemaVersion();
return Xml::element( 'mediawiki', array(
'xmlns' => "http://www.mediawiki.org/xml/export-$ver/",
'xmlns:xsi' => "http://www.w3.org/2001/XMLSchema-instance",
@ -572,7 +582,7 @@ class XmlDumpWriter {
}
/**
* Opens a <page> section on the output stream, with data
* Opens a "<page>" section on the output stream, with data
* from the given database row.
*
* @param $row object
@ -604,7 +614,7 @@ class XmlDumpWriter {
}
/**
* Closes a <page> section on the output stream.
* Closes a "<page>" section on the output stream.
*
* @access private
* @return string
@ -614,7 +624,7 @@ class XmlDumpWriter {
}
/**
* Dumps a <revision> section on the output stream, with
* Dumps a "<revision>" section on the output stream, with
* data filled in from the given database row.
*
* @param $row object
@ -700,7 +710,7 @@ class XmlDumpWriter {
}
/**
* Dumps a <logitem> section on the output stream, with
* Dumps a "<logitem>" section on the output stream, with
* data filled in from the given database row.
*
* @param $row object
@ -748,6 +758,7 @@ class XmlDumpWriter {
/**
* @param $timestamp string
* @param $indent string Default to six spaces
* @return string
*/
function writeTimestamp( $timestamp, $indent = " " ) {
@ -758,6 +769,7 @@ class XmlDumpWriter {
/**
* @param $id
* @param $text string
* @param $indent string Default to six spaces
* @return string
*/
function writeContributor( $id, $text, $indent = " " ) {
@ -779,7 +791,7 @@ class XmlDumpWriter {
* @return string
*/
function writeUploads( $row, $dumpContents = false ) {
if ( $row->page_namespace == NS_IMAGE ) {
if ( $row->page_namespace == NS_FILE ) {
$img = wfLocalFile( $row->page_title );
if ( $img && $img->exists() ) {
$out = '';
@ -837,7 +849,7 @@ class XmlDumpWriter {
* Return prefixed text form of title, but using the content language's
* canonical namespace. This skips any special-casing such as gendered
* user namespaces -- which while useful, are not yet listed in the
* XML <siteinfo> data so are unsafe in export.
* XML "<siteinfo>" data so are unsafe in export.
*
* @param Title $title
* @return string

View file

@ -183,34 +183,35 @@ class FeedItem {
* @todo document (needs one-sentence top-level class description).
* @ingroup Feed
*/
class ChannelFeed extends FeedItem {
/**#@+
* Abstract function, override!
* @abstract
*/
abstract class ChannelFeed extends FeedItem {
/**
* Generate Header of the feed
* @par Example:
* @code
* print "<feed>";
* @endcode
* @param $item
*/
function outHeader() {
# print "<feed>";
}
abstract public function outHeader();
/**
* Generate an item
* @par Example:
* @code
* print "<item>...</item>";
* @endcode
* @param $item
*/
function outItem( $item ) {
# print "<item>...</item>";
}
abstract public function outItem( $item );
/**
* Generate Footer of the feed
* @par Example:
* @code
* print "</feed>";
* @endcode
*/
function outFooter() {
# print "</feed>";
}
/**#@-*/
abstract public function outFooter();
/**
* Setup and send HTTP headers. Don't send any content;

View file

@ -119,10 +119,12 @@ class FileDeleteForm {
// file, otherwise go back to the description page
$wgOut->addReturnTo( $this->oldimage ? $this->title : Title::newMainPage() );
if ( $wgRequest->getCheck( 'wpWatch' ) && $wgUser->isLoggedIn() ) {
WatchAction::doWatch( $this->title, $wgUser );
} elseif ( $this->title->userIsWatching() ) {
WatchAction::doUnwatch( $this->title, $wgUser );
if ( $wgUser->isLoggedIn() && $wgRequest->getCheck( 'wpWatch' ) != $wgUser->isWatched( $this->title ) ) {
if ( $wgRequest->getCheck( 'wpWatch' ) ) {
WatchAction::doWatch( $this->title, $wgUser );
} else {
WatchAction::doUnwatch( $this->title, $wgUser );
}
}
}
return;
@ -154,12 +156,19 @@ class FileDeleteForm {
$status = $file->deleteOld( $oldimage, $reason, $suppress );
if( $status->ok ) {
// Need to do a log item
$log = new LogPage( 'delete' );
$logComment = wfMsgForContent( 'deletedrevision', $oldimage );
if( trim( $reason ) != '' ) {
$logComment .= wfMsgForContent( 'colon-separator' ) . $reason;
}
$log->addEntry( 'delete', $title, $logComment );
$logtype = $suppress ? 'suppress' : 'delete';
$logEntry = new ManualLogEntry( $logtype, 'delete' );
$logEntry->setPerformer( $user );
$logEntry->setTarget( $title );
$logEntry->setComment( $logComment );
$logid = $logEntry->insert();
$logEntry->publish( $logid );
}
} else {
$status = Status::newFatal( 'cannotdelete',
@ -170,7 +179,10 @@ class FileDeleteForm {
try {
// delete the associated article first
$error = '';
if ( $page->doDeleteArticleReal( $reason, $suppress, 0, false, $error, $user ) >= WikiPage::DELETE_SUCCESS ) {
$deleteStatus = $page->doDeleteArticleReal( $reason, $suppress, 0, false, $error, $user );
// doDeleteArticleReal() returns a non-fatal error status if the page
// or revision is missing, so check for isOK() rather than isGood()
if ( $deleteStatus->isOK() ) {
$status = $file->delete( $reason, $suppress );
if( $status->isOK() ) {
$dbw->commit( __METHOD__ );
@ -210,7 +222,7 @@ class FileDeleteForm {
$suppress = '';
}
$checkWatch = $wgUser->getBoolOption( 'watchdeletion' ) || $this->title->userIsWatching();
$checkWatch = $wgUser->getBoolOption( 'watchdeletion' ) || $wgUser->isWatched( $this->title );
$form = Xml::openElement( 'form', array( 'method' => 'post', 'action' => $this->getAction(),
'id' => 'mw-img-deleteconfirm' ) ) .
Xml::openElement( 'fieldset' ) .

View file

@ -1053,11 +1053,22 @@ function wfDebugLog( $logGroup, $text, $public = true ) {
* @param $text String: database error message.
*/
function wfLogDBError( $text ) {
global $wgDBerrorLog;
global $wgDBerrorLog, $wgDBerrorLogInUTC;
if ( $wgDBerrorLog ) {
$host = wfHostname();
$wiki = wfWikiID();
$text = date( 'D M j G:i:s T Y' ) . "\t$host\t$wiki\t$text";
if( $wgDBerrorLogInUTC ) {
$wikiTimezone = date_default_timezone_get();
date_default_timezone_set( 'UTC' );
}
$date = date( 'D M j G:i:s T Y' );
if( $wgDBerrorLogInUTC ) {
// Restore timezone
date_default_timezone_set( $wikiTimezone );
}
$text = "$date\t$host\t$wiki\t$text";
wfErrorLog( $text, $wgDBerrorLog );
}
}
@ -1758,6 +1769,15 @@ function wfDebugDieBacktrace( $msg = '' ) {
function wfHostname() {
static $host;
if ( is_null( $host ) ) {
# Hostname overriding
global $wgOverrideHostname;
if( $wgOverrideHostname !== false ) {
# Set static and skip any detection
$host = $wgOverrideHostname;
return $host;
}
if ( function_exists( 'posix_uname' ) ) {
// This function not present on Windows
$uname = posix_uname();
@ -2054,7 +2074,7 @@ function wfCheckLimits( $deflimit = 50, $optionname = 'rclimit' ) {
* Escapes the given text so that it may be output using addWikiText()
* without any linking, formatting, etc. making its way through. This
* is achieved by substituting certain characters with HTML entities.
* As required by the callers, <nowiki> is not used.
* As required by the callers, "<nowiki>" is not used.
*
* @param $text String: text to be escaped
* @return String
@ -2865,9 +2885,11 @@ function wfEscapeShellArg( ) {
* (non-zero is usually failure)
* @param $environ Array optional environment variables which should be
* added to the executed command environment.
* @param $limits Array optional array with limits(filesize, memory, time)
* this overwrites the global wgShellMax* limits.
* @return string collected stdout as a string (trailing newlines stripped)
*/
function wfShellExec( $cmd, &$retval = null, $environ = array() ) {
function wfShellExec( $cmd, &$retval = null, $environ = array(), $limits = array() ) {
global $IP, $wgMaxShellMemory, $wgMaxShellFileSize, $wgMaxShellTime;
static $disabled;
@ -2915,9 +2937,9 @@ function wfShellExec( $cmd, &$retval = null, $environ = array() ) {
$cmd = $envcmd . $cmd;
if ( php_uname( 's' ) == 'Linux' ) {
$time = intval( $wgMaxShellTime );
$mem = intval( $wgMaxShellMemory );
$filesize = intval( $wgMaxShellFileSize );
$time = intval ( isset($limits['time']) ? $limits['time'] : $wgMaxShellTime );
$mem = intval ( isset($limits['memory']) ? $limits['memory'] : $wgMaxShellMemory );
$filesize = intval ( isset($limits['filesize']) ? $limits['filesize'] : $wgMaxShellFileSize );
if ( $time > 0 && $mem > 0 ) {
$script = "$IP/bin/ulimit4.sh";
@ -3183,11 +3205,11 @@ function wfUseMW( $req_ver ) {
/**
* Return the final portion of a pathname.
* Reimplemented because PHP5's basename() is buggy with multibyte text.
* Reimplemented because PHP5's "basename()" is buggy with multibyte text.
* http://bugs.php.net/bug.php?id=33898
*
* PHP's basename() only considers '\' a pathchar on Windows and Netware.
* We'll consider it so always, as we don't want \s in our Unix paths either.
* We'll consider it so always, as we don't want '\s' in our Unix paths either.
*
* @param $path String
* @param $suffix String: to remove if present

View file

@ -546,7 +546,7 @@ class HTMLForm extends ContextSource {
}
/**
* Wrap the form innards in an actual <form> element
* Wrap the form innards in an actual "<form>" element
* @param $html String HTML contents to wrap.
* @return String wrapped HTML.
*/
@ -688,7 +688,7 @@ class HTMLForm extends ContextSource {
/**
* Format a stack of error messages into a single HTML string
* @param $errors Array of message keys/values
* @return String HTML, a <ul> list of errors
* @return String HTML, a "<ul>" list of errors
*/
public static function formatErrors( $errors ) {
$errorstr = '';
@ -761,16 +761,16 @@ class HTMLForm extends ContextSource {
$this->mId = $id;
}
/**
* Prompt the whole form to be wrapped in a <fieldset>, with
* this text as its <legend> element.
* @param $legend String HTML to go inside the <legend> element.
* Prompt the whole form to be wrapped in a "<fieldset>", with
* this text as its "<legend>" element.
* @param $legend String HTML to go inside the "<legend>" element.
* Will be escaped
*/
public function setWrapperLegend( $legend ) { $this->mWrapperLegend = $legend; }
/**
* Prompt the whole form to be wrapped in a <fieldset>, with
* this message as its <legend> element.
* Prompt the whole form to be wrapped in a "<fieldset>", with
* this message as its "<legend>" element.
* @since 1.19
* @param $msg String message key
*/
@ -780,7 +780,7 @@ class HTMLForm extends ContextSource {
/**
* Set the prefix for various default messages
* TODO: currently only used for the <fieldset> legend on forms
* @todo currently only used for the "<fieldset>" legend on forms
* with multiple sections; should be used elsewhre?
* @param $p String
*/
@ -819,10 +819,10 @@ class HTMLForm extends ContextSource {
}
/**
* TODO: Document
* @todo Document
* @param $fields array[]|HTMLFormField[] array of fields (either arrays or objects)
* @param $sectionName string ID attribute of the <table> tag for this section, ignored if empty
* @param $fieldsetIDPrefix string ID prefix for the <fieldset> tag of each subsection, ignored if empty
* @param $sectionName string ID attribute of the "<table>" tag for this section, ignored if empty
* @param $fieldsetIDPrefix string ID prefix for the "<fieldset>" tag of each subsection, ignored if empty
* @return String
*/
public function displaySection( $fields, $sectionName = '', $fieldsetIDPrefix = '' ) {
@ -938,8 +938,8 @@ class HTMLForm extends ContextSource {
}
/**
* Get a string to go in the <legend> of a section fieldset. Override this if you
* want something more complicated
* Get a string to go in the "<legend>" of a section fieldset.
* Override this if you want something more complicated.
* @param $key String
* @return String
*/
@ -1350,7 +1350,7 @@ abstract class HTMLFormField {
/**
* flatten an array of options to a single array, for instance,
* a set of <options> inside <optgroups>.
* a set of "<options>" inside "<optgroups>".
* @param $options array Associative Array with values either Strings
* or Arrays
* @return Array flattened input
@ -1889,7 +1889,7 @@ class HTMLMultiSelectField extends HTMLFormField {
* ** <option value>
* * New Optgroup header
* Plus a text field underneath for an additional reason. The 'value' of the field is
* ""<select>: <extra reason>"", or "<extra reason>" if nothing has been selected in the
* "<select>: <extra reason>", or "<extra reason>" if nothing has been selected in the
* select dropdown.
* @todo FIXME: If made 'required', only the text field should be compulsory.
*/
@ -1991,7 +1991,7 @@ class HTMLSelectAndOtherField extends HTMLSelectField {
/**
* @param $request WebRequest
* @return Array( <overall message>, <select value>, <text field value> )
* @return Array("<overall message>","<select value>","<text field value>")
*/
function loadDataFromRequest( $request ) {
if ( $request->getCheck( $this->mName ) ) {

View file

@ -548,9 +548,10 @@ class Html {
}
/**
* Output a <script> tag with the given contents. TODO: do some useful
* escaping as well, like if $contents contains literal '</script>' or (for
* XML) literal "]]>".
* Output a "<script>" tag with the given contents.
*
* @todo do some useful escaping as well, like if $contents contains
* literal "</script>" or (for XML) literal "]]>".
*
* @param $contents string JavaScript
* @return string Raw HTML
@ -572,8 +573,8 @@ class Html {
}
/**
* Output a <script> tag linking to the given URL, e.g.,
* <script src=foo.js></script>.
* Output a "<script>" tag linking to the given URL, e.g.,
* "<script src=foo.js></script>".
*
* @param $url string
* @return string Raw HTML
@ -591,9 +592,9 @@ class Html {
}
/**
* Output a <style> tag with the given contents for the given media type
* Output a "<style>" tag with the given contents for the given media type
* (if any). TODO: do some useful escaping as well, like if $contents
* contains literal '</style>' (admittedly unlikely).
* contains literal "</style>" (admittedly unlikely).
*
* @param $contents string CSS
* @param $media mixed A media type string, like 'screen'
@ -613,7 +614,7 @@ class Html {
}
/**
* Output a <link rel=stylesheet> linking to the given URL for the given
* Output a "<link rel=stylesheet>" linking to the given URL for the given
* media type (if any).
*
* @param $url string
@ -630,7 +631,7 @@ class Html {
}
/**
* Convenience function to produce an <input> element. This supports the
* Convenience function to produce an "<input>" element. This supports the
* new HTML5 input types and attributes, and will silently strip them if
* $wgHtml5 is false.
*
@ -663,11 +664,12 @@ class Html {
}
/**
* Convenience function to produce an <input> element. This supports leaving
* out the cols= and rows= which Xml requires and are required by HTML4/XHTML
* but not required by HTML5 and will silently set cols="" and rows="" if
* $wgHtml5 is false and cols and rows are omitted (HTML4 validates present
* but empty cols="" and rows="" as valid).
* Convenience function to produce an "<input>" element.
*
* This supports leaving out the cols= and rows= which Xml requires and are
* required by HTML4/XHTML but not required by HTML5 and will silently set
* cols="" and rows="" if $wgHtml5 is false and cols and rows are omitted
* (HTML4 validates present but empty cols="" and rows="" as valid).
*
* @param $name string name attribute
* @param $value string value attribute
@ -706,7 +708,7 @@ class Html {
*
* @param $params array:
* - selected: [optional] Id of namespace which should be pre-selected
* - all: [optional] Value of item for "all namespaces". If null or unset, no <option> is generated to select all namespaces
* - all: [optional] Value of item for "all namespaces". If null or unset, no "<option>" is generated to select all namespaces
* - label: text for label to add before the field
* - exclude: [optional] Array of namespace ids to exclude
* - disable: [optional] Array of namespace ids for which the option should be disabled in the selector

View file

@ -156,7 +156,7 @@ class Http {
*
* file:// should not be allowed here for security purpose (r67684)
*
* @fixme this is wildly inaccurate and fails to actually check most stuff
* @todo FIXME this is wildly inaccurate and fails to actually check most stuff
*
* @param $uri Mixed: URI to check for validity
* @return Boolean

View file

@ -155,7 +155,7 @@ class ImagePage extends Article {
# should be in page content language
$pageLang = $this->getTitle()->getPageLanguage();
$out->addHTML( Xml::openElement( 'div', array( 'id' => 'mw-imagepage-content',
'lang' => $pageLang->getCode(), 'dir' => $pageLang->getDir(),
'lang' => $pageLang->getHtmlCode(), 'dir' => $pageLang->getDir(),
'class' => 'mw-content-'.$pageLang->getDir() ) ) );
parent::view();
@ -414,12 +414,11 @@ class ImagePage extends Article {
if ( $page > 1 ) {
$label = $out->parse( wfMsg( 'imgmultipageprev' ), false );
$link = Linker::link(
$link = Linker::linkKnown(
$this->getTitle(),
$label,
array(),
array( 'page' => $page - 1 ),
array( 'known', 'noclasses' )
array( 'page' => $page - 1 )
);
$thumb1 = Linker::makeThumbLinkObj( $this->getTitle(), $this->displayImg, $link, $label, 'none',
array( 'page' => $page - 1 ) );
@ -429,12 +428,11 @@ class ImagePage extends Article {
if ( $page < $count ) {
$label = wfMsg( 'imgmultipagenext' );
$link = Linker::link(
$link = Linker::linkKnown(
$this->getTitle(),
$label,
array(),
array( 'page' => $page + 1 ),
array( 'known', 'noclasses' )
array( 'page' => $page + 1 )
);
$thumb2 = Linker::makeThumbLinkObj( $this->getTitle(), $this->displayImg, $link, $label, 'none',
array( 'page' => $page + 1 ) );
@ -646,7 +644,7 @@ EOT
# External editing link
if ( $wgUseExternalEditor ) {
$elink = Linker::link(
$elink = Linker::linkKnown(
$this->getTitle(),
wfMsgHtml( 'edit-externally' ),
array(),
@ -654,8 +652,7 @@ EOT
'action' => 'edit',
'externaledit' => 'true',
'mode' => 'file'
),
array( 'known', 'noclasses' )
)
);
$out->addHTML(
'<li id="mw-imagepage-edit-external">' . $elink . ' <small>' .
@ -830,13 +827,7 @@ EOT
foreach ( $dupes as $file ) {
$fromSrc = '';
if ( $file->isLocal() ) {
$link = Linker::link(
$file->getTitle(),
null,
array(),
array(),
array( 'known', 'noclasses' )
);
$link = Linker::linkKnown( $file->getTitle() );
} else {
$link = Linker::makeExternalLink( $file->getDescriptionUrl(),
$file->getTitle()->getPrefixedText() );
@ -1006,10 +997,10 @@ class ImageHistoryList extends ContextSource {
if ( !$iscur ) {
$q['oldimage'] = $img;
}
$row .= Linker::link(
$row .= Linker::linkKnown(
$this->title,
wfMsgHtml( $iscur ? 'filehist-deleteall' : 'filehist-deleteone' ),
array(), $q, array( 'known' )
array(), $q
);
}
# Link to hide content. Don't show useless link to people who cannot hide revisions.
@ -1046,7 +1037,7 @@ class ImageHistoryList extends ContextSource {
if ( $file->isDeleted( File::DELETED_FILE ) ) {
$row .= wfMsgHtml( 'filehist-revert' );
} else {
$row .= Linker::link(
$row .= Linker::linkKnown(
$this->title,
wfMsgHtml( 'filehist-revert' ),
array(),
@ -1054,8 +1045,7 @@ class ImageHistoryList extends ContextSource {
'action' => 'revert',
'oldimage' => $img,
'wpEditToken' => $user->getEditToken( $img )
),
array( 'known', 'noclasses' )
)
);
}
}
@ -1074,7 +1064,7 @@ class ImageHistoryList extends ContextSource {
$this->preventClickjacking();
$revdel = SpecialPage::getTitleFor( 'Revisiondelete' );
# Make a link to review the image
$url = Linker::link(
$url = Linker::linkKnown(
$revdel,
$lang->timeanddate( $timestamp, true ),
array(),
@ -1082,8 +1072,7 @@ class ImageHistoryList extends ContextSource {
'target' => $this->title->getPrefixedText(),
'file' => $img,
'token' => $user->getEditToken( $img )
),
array( 'known', 'noclasses' )
)
);
} else {
$url = $lang->timeanddate( $timestamp, true );

View file

@ -1,6 +1,6 @@
<?php
/**
* MediaWiki page data importer
* MediaWiki page data importer.
*
* Copyright © 2003,2005 Brion Vibber <brion@pobox.com>
* http://www.mediawiki.org/
@ -275,7 +275,7 @@ class WikiImporter {
}
/**
* Notify the callback function when a new <page> is reached.
* Notify the callback function when a new "<page>" is reached.
* @param $title Title
*/
function pageCallback( $title ) {
@ -285,7 +285,7 @@ class WikiImporter {
}
/**
* Notify the callback function when a </page> is closed.
* Notify the callback function when a "</page>" is closed.
* @param $title Title
* @param $origTitle Title
* @param $revCount Integer

View file

@ -407,6 +407,11 @@ class Linker {
* despite $query not being used.
*
* @param $nt Title
* @param $html String [optional]
* @param $query String [optional]
* @param $trail String [optional]
* @param $prefix String [optional]
*
*
* @return string
*/
@ -978,7 +983,7 @@ class Linker {
* @param $userName String: user name in database.
* @param $altUserName String: text to display instead of the user name (optional)
* @return String: HTML fragment
* @since 1.19 Method exists for a long time. $displayText was added in 1.19.
* @since 1.19 Method exists for a long time. $altUserName was added in 1.19.
*/
public static function userLink( $userId, $userName, $altUserName = false ) {
if ( $userId == 0 ) {
@ -1646,11 +1651,17 @@ class Linker {
* other users.
*
* @param $rev Revision object
* @param $context IContextSource context to use or null for the main context.
* @return string
*/
public static function generateRollback( $rev ) {
public static function generateRollback( $rev, IContextSource $context = null ) {
if ( $context === null ) {
$context = RequestContext::getMain();
}
return '<span class="mw-rollback-link">'
. wfMessage( 'brackets' )->rawParams( self::buildRollbackLink( $rev ) )->plain()
. $context->msg( 'brackets' )->rawParams(
self::buildRollbackLink( $rev, $context ) )->plain()
. '</span>';
}
@ -1658,24 +1669,28 @@ class Linker {
* Build a raw rollback link, useful for collections of "tool" links
*
* @param $rev Revision object
* @param $context IContextSource context to use or null for the main context.
* @return String: HTML fragment
*/
public static function buildRollbackLink( $rev ) {
global $wgRequest, $wgUser;
public static function buildRollbackLink( $rev, IContextSource $context = null ) {
if ( $context === null ) {
$context = RequestContext::getMain();
}
$title = $rev->getTitle();
$query = array(
'action' => 'rollback',
'from' => $rev->getUserText(),
'token' => $wgUser->getEditToken( array( $title->getPrefixedText(), $rev->getUserText() ) ),
'token' => $context->getUser()->getEditToken( array( $title->getPrefixedText(), $rev->getUserText() ) ),
);
if ( $wgRequest->getBool( 'bot' ) ) {
if ( $context->getRequest()->getBool( 'bot' ) ) {
$query['bot'] = '1';
$query['hidediff'] = '1'; // bug 15999
}
return self::link(
$title,
wfMsgHtml( 'rollbacklink' ),
array( 'title' => wfMsg( 'tooltip-rollback' ) ),
$context->msg( 'rollbacklink' )->escaped(),
array( 'title' => $context->msg( 'tooltip-rollback' )->text() ),
$query,
array( 'known', 'noclasses' )
);
@ -1713,7 +1728,7 @@ class Linker {
}
$outText .= "</div><ul>\n";
usort( $templates, array( 'Title', 'compare' ) );
usort( $templates, 'Title::compare' );
foreach ( $templates as $titleObj ) {
$r = $titleObj->getRestrictions( 'edit' );
if ( in_array( 'sysop', $r ) ) {
@ -1913,10 +1928,10 @@ class Linker {
* Creates a (show/hide) link for deleting revisions/log entries
*
* @param $query Array: query parameters to be passed to link()
* @param $restricted Boolean: set to true to use a <strong> instead of a <span>
* @param $restricted Boolean: set to true to use a "<strong>" instead of a "<span>"
* @param $delete Boolean: set to true to use (show/hide) rather than (show)
*
* @return String: HTML <a> link to Special:Revisiondelete, wrapped in a
* @return String: HTML "<a>" link to Special:Revisiondelete, wrapped in a
* span to allow for customization of appearance with CSS
*/
public static function revDeleteLink( $query = array(), $restricted = false, $delete = true ) {

View file

@ -821,9 +821,7 @@ class LinksDeletionUpdate extends SqlDataUpdate {
/**
* Constructor
*
* @param $title Title of the page we're updating
* @param $parserOutput ParserOutput: output from a full parse of this page
* @param $recursive Boolean: queue jobs for recursive updates?
* @param $page WikiPage Page we are updating
*/
function __construct( Title $title ) {
parent::__construct( );

View file

@ -24,7 +24,7 @@
*/
/**
* This class encapsulates "magic words" such as #redirect, __NOTOC__, etc.
* This class encapsulates "magic words" such as "#redirect", __NOTOC__, etc.
*
* @par Usage:
* @code
@ -42,7 +42,7 @@
*
* To add magic words in an extension, use $magicWords in a file listed in
* $wgExtensionMessagesFiles[].
*
*
* @par Example:
* @code
* $magicWords = array();

View file

@ -341,6 +341,33 @@ class MWNamespace {
return $wgContentNamespaces;
}
}
/**
* List all namespace indices which are considered subject, aka not a talk
* or special namespace. See also MWNamespace::isSubject
*
* @return array of namespace indices
*/
public static function getSubjectNamespaces() {
return array_filter(
MWNamespace::getValidNamespaces(),
'MWNamespace::isSubject'
);
}
/**
* List all namespace indices which are considered talks, aka not a subject
* or special namespace. See also MWNamespace::isTalk
*
* @return array of namespace indices
*/
public static function getTalkNamespaces() {
return array_filter(
MWNamespace::getValidNamespaces(),
'MWNamespace::isTalk'
);
}
/**
* Is the namespace first-letter capitalized?
*

View file

@ -36,10 +36,10 @@
* @todo document
*/
class OutputPage extends ContextSource {
/// Should be private. Used with addMeta() which adds <meta>
/// Should be private. Used with addMeta() which adds "<meta>"
var $mMetatags = array();
/// <meta keywords="stuff"> most of the time the first 10 links to an article
/// "<meta keywords='stuff'>" most of the time the first 10 links to an article
var $mKeywords = array();
var $mLinktags = array();
@ -50,7 +50,7 @@ class OutputPage extends ContextSource {
/// Should be private - has getter and setter. Contains the HTML title
var $mPagetitle = '';
/// Contains all of the <body> content. Should be private we got set/get accessors and the append() method.
/// Contains all of the "<body>" content. Should be private we got set/get accessors and the append() method.
var $mBodytext = '';
/**
@ -60,7 +60,7 @@ class OutputPage extends ContextSource {
*/
public $mDebugtext = ''; // TODO: we might want to replace it by wfDebug() wfDebugLog()
/// Should be private. Stores contents of <title> tag
/// Should be private. Stores contents of "<title>" tag
var $mHTMLtitle = '';
/// Should be private. Is the displayed content related to the source of the corresponding wiki article.
@ -116,8 +116,8 @@ class OutputPage extends ContextSource {
/**
* Should be private. Used for JavaScript (pre resource loader)
* We should split js / css.
* mScripts content is inserted as is in <head> by Skin. This might contains
* either a link to a stylesheet or inline css.
* mScripts content is inserted as is in "<head>" by Skin. This might
* contains either a link to a stylesheet or inline css.
*/
var $mScripts = '';
@ -135,7 +135,7 @@ class OutputPage extends ContextSource {
*/
var $mPageLinkTitle = '';
/// Array of elements in <head>. Parser might add its own headers!
/// Array of elements in "<head>". Parser might add its own headers!
var $mHeadItems = array();
// @todo FIXME: Next variables probably comes from the resource loader
@ -197,7 +197,7 @@ class OutputPage extends ContextSource {
/**
* Comes from the parser. This was probably made to load CSS/JS only
* if we had <gallery>. Used directly in CategoryPage.php
* if we had "<gallery>". Used directly in CategoryPage.php
* Looks like resource loader can replace this.
*/
var $mNoGallery = false;
@ -237,7 +237,6 @@ class OutputPage extends ContextSource {
private $mFollowPolicy = 'follow';
private $mVaryHeader = array(
'Accept-Encoding' => array( 'list-contains=gzip' ),
'Cookie' => null
);
/**
@ -293,7 +292,7 @@ class OutputPage extends ContextSource {
}
/**
* Add a new <meta> tag
* Add a new "<meta>" tag
* To add an http-equiv meta tag, precede the name with "http:"
*
* @param $name String tag name
@ -406,7 +405,7 @@ class OutputPage extends ContextSource {
/**
* Add a self-contained script tag with the given contents
*
* @param $script String: JavaScript text, no <script> tags
* @param $script String: JavaScript text, no "<script>" tags
*/
public function addInlineScript( $script ) {
$this->mScripts .= Html::inlineScript( "\n$script\n" ) . "\n";
@ -780,7 +779,7 @@ class OutputPage extends ContextSource {
}
/**
* "HTML title" means the contents of <title>.
* "HTML title" means the contents of "<title>".
* It is stored as plain, unescaped text and will be run through htmlspecialchars in the skin file.
*
* @param $name string
@ -794,7 +793,7 @@ class OutputPage extends ContextSource {
}
/**
* Return the "HTML title", i.e. the content of the <title> tag.
* Return the "HTML title", i.e. the content of the "<title>" tag.
*
* @return String
*/
@ -805,7 +804,7 @@ class OutputPage extends ContextSource {
/**
* Set $mRedirectedFrom, the Title of the page which redirected us to the current page.
*
* param @t Title
* @param $t Title
*/
public function setRedirectedFrom( $t ) {
$this->mRedirectedFrom = $t;
@ -1363,11 +1362,11 @@ class OutputPage extends ContextSource {
* Set the timestamp of the revision which will be displayed. This is used
* to avoid a extra DB call in Skin::lastModified().
*
* @param $revid Mixed: string, or null
* @param $timestamp Mixed: string, or null
* @return Mixed: previous value
*/
public function setRevisionTimestamp( $timestmap ) {
return wfSetVar( $this->mRevisionTimestamp, $timestmap );
public function setRevisionTimestamp( $timestamp) {
return wfSetVar( $this->mRevisionTimestamp, $timestamp );
}
/**
@ -1722,6 +1721,16 @@ class OutputPage extends ContextSource {
$this->mVaryHeader[$header] = array_unique( (array)$this->mVaryHeader[$header] );
}
/**
* Return a Vary: header on which to vary caches. Based on the keys of $mVaryHeader,
* such as Accept-Encoding or Cookie
*
* @return String
*/
public function getVaryHeader() {
return 'Vary: ' . join( ', ', array_keys( $this->mVaryHeader ) );
}
/**
* Get a complete X-Vary-Options header
*
@ -1834,11 +1843,12 @@ class OutputPage extends ContextSource {
$response->header( "ETag: $this->mETag" );
}
$this->addVaryHeader( 'Cookie' );
$this->addAcceptLanguage();
# don't serve compressed data to clients who can't handle it
# maintain different caches for logged-in users and non-logged in ones
$response->header( 'Vary: ' . join( ', ', array_keys( $this->mVaryHeader ) ) );
$response->header( $this->getVaryHeader() );
if ( $wgUseXVO ) {
# Add an X-Vary-Options header for Squid with Wikimedia patches
@ -2017,11 +2027,11 @@ class OutputPage extends ContextSource {
/**
* Prepare this object to display an error page; disable caching and
* indexing, clear the current text and redirect, set the page's title
* and optionally an custom HTML title (content of the <title> tag).
* and optionally an custom HTML title (content of the "<title>" tag).
*
* @param $pageTitle String|Message will be passed directly to setPageTitle()
* @param $htmlTitle String|Message will be passed directly to setHTMLTitle();
* optional, if not passed the <title> attribute will be
* optional, if not passed the "<title>" attribute will be
* based on $pageTitle
*/
public function prepareErrorPage( $pageTitle, $htmlTitle = false ) {
@ -2385,7 +2395,7 @@ $templates
/**
* @param $sk Skin The given Skin
* @param $includeStyle Boolean: unused
* @return String: The doctype, opening <html>, and head element.
* @return String: The doctype, opening "<html>", and head element.
*/
public function headElement( Skin $sk, $includeStyle = true ) {
global $wgContLang;
@ -2510,19 +2520,21 @@ $templates
* @param $only String ResourceLoaderModule TYPE_ class constant
* @param $useESI boolean
* @param $extraQuery Array with extra query parameters to add to each request. array( param => value )
* @param $loadCall boolean If true, output an (asynchronous) mw.loader.load() call rather than a <script src="..."> tag
* @return string html <script> and <style> tags
* @param $loadCall boolean If true, output an (asynchronous) mw.loader.load() call rather than a "<script src='...'>" tag
* @return string html "<script>" and "<style>" tags
*/
protected function makeResourceLoaderLink( $modules, $only, $useESI = false, array $extraQuery = array(), $loadCall = false ) {
global $wgResourceLoaderUseESI;
$modules = (array) $modules;
if ( !count( $modules ) ) {
return '';
}
if ( count( $modules ) > 1 ) {
// Remove duplicate module requests
$modules = array_unique( (array) $modules );
$modules = array_unique( $modules );
// Sort module names so requests are more uniform
sort( $modules );
@ -2539,7 +2551,7 @@ $templates
// Create keyed-by-group list of module objects from modules list
$groups = array();
$resourceLoader = $this->getResourceLoader();
foreach ( (array) $modules as $name ) {
foreach ( $modules as $name ) {
$module = $resourceLoader->getModule( $name );
# Check that we're allowed to include this module on this page
if ( !$module
@ -2560,7 +2572,7 @@ $templates
}
$links = '';
foreach ( $groups as $group => $modules ) {
foreach ( $groups as $group => $grpModules ) {
// Special handling for user-specific groups
$user = null;
if ( ( $group === 'user' || $group === 'private' ) && $this->getUser()->isLoggedIn() ) {
@ -2584,10 +2596,10 @@ $templates
$context = new ResourceLoaderContext( $resourceLoader, new FauxRequest( $query ) );
// Extract modules that know they're empty
$emptyModules = array ();
foreach ( $modules as $key => $module ) {
foreach ( $grpModules as $key => $module ) {
if ( $module->isKnownEmpty( $context ) ) {
$emptyModules[$key] = 'ready';
unset( $modules[$key] );
unset( $grpModules[$key] );
}
}
// Inline empty modules: since they're empty, just mark them as 'ready'
@ -2605,7 +2617,7 @@ $templates
}
// If there are no modules left, skip this group
if ( $modules === array() ) {
if ( count( $grpModules ) === 0 ) {
continue;
}
@ -2616,12 +2628,12 @@ $templates
if ( $group === 'private' ) {
if ( $only == ResourceLoaderModule::TYPE_STYLES ) {
$links .= Html::inlineStyle(
$resourceLoader->makeModuleResponse( $context, $modules )
$resourceLoader->makeModuleResponse( $context, $grpModules )
);
} else {
$links .= Html::inlineScript(
ResourceLoader::makeLoaderConditionalScript(
$resourceLoader->makeModuleResponse( $context, $modules )
$resourceLoader->makeModuleResponse( $context, $grpModules )
)
);
}
@ -2637,7 +2649,7 @@ $templates
if ( $group === 'user' ) {
// Get the maximum timestamp
$timestamp = 1;
foreach ( $modules as $module ) {
foreach ( $grpModules as $module ) {
$timestamp = max( $timestamp, $module->getModifiedTime( $context ) );
}
// Add a version parameter so cache will break when things change
@ -2645,7 +2657,7 @@ $templates
}
$url = ResourceLoader::makeLoaderURL(
array_keys( $modules ),
array_keys( $grpModules ),
$this->getLanguage()->getCode(),
$this->getSkin()->getSkinName(),
$user,
@ -2688,7 +2700,7 @@ $templates
}
/**
* JS stuff to put in the <head>. This is the startup module, config
* JS stuff to put in the "<head>". This is the startup module, config
* vars and modules marked with position 'top'
*
* @return String: HTML fragment
@ -2736,12 +2748,12 @@ $templates
}
/**
* JS stuff to put at the 'bottom', which can either be the bottom of the <body>
* or the bottom of the <head> depending on $wgResourceLoaderExperimentalAsyncLoading:
* JS stuff to put at the 'bottom', which can either be the bottom of the "<body>"
* or the bottom of the "<head>" depending on $wgResourceLoaderExperimentalAsyncLoading:
* modules marked with position 'bottom', legacy scripts ($this->mScripts),
* user preferences, site JS and user JS
*
* @param $inHead boolean If true, this HTML goes into the <head>, if false it goes into the <body>
* @param $inHead boolean If true, this HTML goes into the "<head>", if false it goes into the "<body>"
* @return string
*/
function getScriptsForBottomQueue( $inHead ) {
@ -2857,7 +2869,7 @@ $templates
}
/**
* JS stuff to put at the bottom of the <body>
* JS stuff to put at the bottom of the "<body>"
* @return string
*/
function getBottomScripts() {
@ -2872,7 +2884,7 @@ $templates
/**
* Add one or more variables to be set in mw.config in JavaScript.
*
* @param $key {String|Array} Key or array of key/value pars.
* @param $keys {String|Array} Key or array of key/value pairs.
* @param $value {Mixed} [optional] Value of the configuration variable.
*/
public function addJsConfigVars( $keys, $value = null ) {
@ -2955,6 +2967,9 @@ $templates
'wgPageContentLanguage' => $lang->getCode(),
'wgSeparatorTransformTable' => $compactSeparatorTransTable,
'wgDigitTransformTable' => $compactDigitTransTable,
'wgDefaultDateFormat' => $lang->getDefaultDateFormat(),
'wgMonthNames' => $lang->getMonthNamesArray(),
'wgMonthNamesShort' => $lang->getMonthAbbreviationsArray(),
'wgRelevantPageName' => $relevantTitle->getPrefixedDBKey(),
);
if ( $wgContLang->hasVariants() ) {
@ -3008,7 +3023,7 @@ $templates
}
/**
* @param $addContentType bool: Whether <meta> specifying content type should be returned
* @param $addContentType bool: Whether "<meta>" specifying content type should be returned
*
* @return array in format "link name or number => 'link html'".
*/
@ -3242,7 +3257,7 @@ $templates
/**
* @param $unused
* @param $addContentType bool: Whether <meta> specifying content type should be returned
* @param $addContentType bool: Whether "<meta>" specifying content type should be returned
*
* @return string HTML tag links to be put in the header.
*/
@ -3251,7 +3266,7 @@ $templates
}
/**
* Generate a <link rel/> for a feed.
* Generate a "<link rel/>" for a feed.
*
* @param $type String: feed type
* @param $url String: URL to the feed
@ -3306,7 +3321,7 @@ $templates
}
/**
* Build a set of <link>s for the stylesheets specified in the $this->styles array.
* Build a set of "<link>" elements for the stylesheets specified in the $this->styles array.
* These will be applied to various media & IE conditionals.
*
* @return string

View file

@ -175,6 +175,15 @@ abstract class IndexPager extends ContextSource implements Pager {
}
}
/**
* Get the Database object in use
*
* @return DatabaseBase
*/
public function getDatabase() {
return $this->mDb;
}
/**
* Do the query, using information from the object context. This function
* has been kept minimal to make it overridable if necessary, to allow for
@ -194,6 +203,7 @@ abstract class IndexPager extends ContextSource implements Pager {
$queryLimit,
$descending
);
$this->extractResultInfo( $this->mOffset, $queryLimit, $this->mResult );
$this->mQueryDone = true;
@ -303,7 +313,20 @@ abstract class IndexPager extends ContextSource implements Pager {
* @param $descending Boolean: query direction, false for ascending, true for descending
* @return ResultWrapper
*/
function reallyDoQuery( $offset, $limit, $descending ) {
public function reallyDoQuery( $offset, $limit, $descending ) {
list( $tables, $fields, $conds, $fname, $options, $join_conds ) = $this->buildQueryInfo( $offset, $limit, $descending );
return $this->mDb->select( $tables, $fields, $conds, $fname, $options, $join_conds );
}
/**
* Build variables to use by the database wrapper.
*
* @param $offset String: index offset, inclusive
* @param $limit Integer: exact query limit
* @param $descending Boolean: query direction, false for ascending, true for descending
* @return array
*/
protected function buildQueryInfo( $offset, $limit, $descending ) {
$fname = __METHOD__ . ' (' . $this->getSqlComment() . ')';
$info = $this->getQueryInfo();
$tables = $info['tables'];
@ -327,8 +350,7 @@ abstract class IndexPager extends ContextSource implements Pager {
$conds[] = $this->mIndexField . $operator . $this->mDb->addQuotes( $offset );
}
$options['LIMIT'] = intval( $limit );
$res = $this->mDb->select( $tables, $fields, $conds, $fname, $options, $join_conds );
return new ResultWrapper( $this->mDb, $res );
return array( $tables, $fields, $conds, $fname, $options, $join_conds );
}
/**
@ -980,7 +1002,7 @@ abstract class TablePager extends IndexPager {
* @protected
*
* @param $row Object: the database result row
* @return Array of <attr> => <value>
* @return Array of attribute => value
*/
function getRowAttrs( $row ) {
$class = $this->getRowClass( $row );
@ -1095,7 +1117,7 @@ abstract class TablePager extends IndexPager {
}
/**
* Get a <select> element which has options for each of the allowed limits
* Get a "<select>" element which has options for each of the allowed limits
*
* @return String: HTML fragment
*/
@ -1125,7 +1147,7 @@ abstract class TablePager extends IndexPager {
}
/**
* Get <input type="hidden"> elements for use in a method="get" form.
* Get \<input type="hidden"\> elements for use in a method="get" form.
* Resubmits all defined elements of the query string, except for a
* blacklist, passed in the $blacklist parameter.
*

View file

@ -939,7 +939,7 @@ class Preferences {
if ( $wgEnableAPI ) {
# Some random gibberish as a proposed default
// @fixme This should use CryptRand but we may not want to read urandom on every view
// @todo Fixme: this should use CryptRand but we may not want to read urandom on every view
$hash = sha1( mt_rand() . microtime( true ) );
$defaultPreferences['watchlisttoken'] = array(
@ -1587,7 +1587,7 @@ class PreferencesForm extends HTMLForm {
}
/**
* Get the <legend> for a given section key. Normally this is the
* Get the "<legend>" for a given section key. Normally this is the
* prefs-$key message but we'll allow extensions to override it.
* @param $key string
* @return string

View file

@ -318,10 +318,12 @@ class ProtectionForm {
return false;
}
if ( $wgRequest->getCheck( 'mwProtectWatch' ) && $wgUser->isLoggedIn() ) {
WatchAction::doWatch( $this->mTitle, $wgUser );
} elseif ( $this->mTitle->userIsWatching() ) {
WatchAction::doUnwatch( $this->mTitle, $wgUser );
if ( $wgUser->isLoggedIn() && $wgRequest->getCheck( 'mwProtectWatch' ) != $wgUser->isWatched( $this->mTitle ) ) {
if ( $wgRequest->getCheck( 'mwProtectWatch' ) ) {
WatchAction::doWatch( $this->mTitle, $wgUser );
} else {
WatchAction::doUnwatch( $this->mTitle, $wgUser );
}
}
return true;
}

View file

@ -19,7 +19,7 @@
*
* @file
*/
/**
* Utility class for creating new RC entries
*
@ -108,13 +108,7 @@ class RecentChange {
* @return RecentChange
*/
public static function newFromId( $rcid ) {
$dbr = wfGetDB( DB_SLAVE );
$row = $dbr->selectRow( 'recentchanges', '*', array( 'rc_id' => $rcid ), __METHOD__ );
if( $row !== false ) {
return self::newFromRow( $row );
} else {
return null;
}
return self::newFromConds( array( 'rc_id' => $rcid ), __METHOD__ );
}
/**
@ -126,18 +120,12 @@ class RecentChange {
*/
public static function newFromConds( $conds, $fname = __METHOD__ ) {
$dbr = wfGetDB( DB_SLAVE );
$res = $dbr->select(
'recentchanges',
'*',
$conds,
$fname
);
if( $res instanceof ResultWrapper && $res->numRows() > 0 ) {
$row = $res->fetchObject();
$res->free();
$row = $dbr->selectRow( 'recentchanges', '*', $conds, $fname );
if ( $row !== false ) {
return self::newFromRow( $row );
} else {
return null;
}
return null;
}
# Accessors
@ -170,7 +158,7 @@ class RecentChange {
}
/**
* @return bool|\Title
* @return bool|Title
*/
public function getMovedToTitle() {
if( $this->mMovedToTitle === false ) {
@ -243,13 +231,15 @@ class RecentChange {
}
$title = Title::makeTitle( $this->mAttribs['rc_namespace'], $this->mAttribs['rc_title'] );
# @todo FIXME: This would be better as an extension hook
$enotif = new EmailNotification();
$status = $enotif->notifyOnPageChange( $editor, $title,
$this->mAttribs['rc_timestamp'],
$this->mAttribs['rc_comment'],
$this->mAttribs['rc_minor'],
$this->mAttribs['rc_last_oldid'] );
if ( wfRunHooks( 'AbortEmailNotification', array($editor, $title) ) ) {
# @todo FIXME: This would be better as an extension hook
$enotif = new EmailNotification();
$status = $enotif->notifyOnPageChange( $editor, $title,
$this->mAttribs['rc_timestamp'],
$this->mAttribs['rc_comment'],
$this->mAttribs['rc_minor'],
$this->mAttribs['rc_last_oldid'] );
}
}
}
@ -781,7 +771,7 @@ class RecentChange {
}
} else {
$ip = $wgRequest->getIP();
if( !$ip )
if( !$ip )
$ip = '';
}
return $ip;

View file

@ -23,7 +23,7 @@
/**
* @todo document
*/
class Revision {
class Revision implements IDBAccessObject {
protected $mId;
protected $mPage;
protected $mUserText;
@ -51,7 +51,7 @@ class Revision {
const DELETED_RESTRICTED = 8;
// Convenience field
const SUPPRESSED_USER = 12;
// Audience options for Revision::getText()
// Audience options for accessors
const FOR_PUBLIC = 1;
const FOR_THIS_USER = 2;
const RAW = 3;
@ -60,11 +60,17 @@ class Revision {
* Load a page revision from a given revision ID number.
* Returns null if no such revision can be found.
*
* $flags include:
* IDBAccessObject::LATEST_READ : Select the data from the master
* IDBAccessObject::LOCKING_READ : Select & lock the data from the master
* IDBAccessObject::AVOID_MASTER : Avoid master queries; data may be stale
*
* @param $id Integer
* @param $flags Integer (optional)
* @return Revision or null
*/
public static function newFromId( $id ) {
return Revision::newFromConds( array( 'rev_id' => intval( $id ) ) );
public static function newFromId( $id, $flags = 0 ) {
return self::newFromConds( array( 'rev_id' => intval( $id ) ), $flags );
}
/**
@ -72,11 +78,17 @@ class Revision {
* that's attached to a given title. If not attached
* to that title, will return null.
*
* $flags include:
* IDBAccessObject::LATEST_READ : Select the data from the master
* IDBAccessObject::LOCKING_READ : Select & lock the data from the master
* IDBAccessObject::AVOID_MASTER : Avoid master queries; data may be stale
*
* @param $title Title
* @param $id Integer (optional)
* @param $flags Integer Bitfield (optional)
* @return Revision or null
*/
public static function newFromTitle( $title, $id = 0 ) {
public static function newFromTitle( $title, $id = 0, $flags = 0 ) {
$conds = array(
'page_namespace' => $title->getNamespace(),
'page_title' => $title->getDBkey()
@ -84,7 +96,7 @@ class Revision {
if ( $id ) {
// Use the specified ID
$conds['rev_id'] = $id;
} elseif ( wfGetLB()->getServerCount() > 1 ) {
} elseif ( !( $flags & self::AVOID_MASTER ) && wfGetLB()->getServerCount() > 1 ) {
// Get the latest revision ID from the master
$dbw = wfGetDB( DB_MASTER );
$latest = $dbw->selectField( 'page', 'page_latest', $conds, __METHOD__ );
@ -96,7 +108,7 @@ class Revision {
// Use a join to get the latest revision
$conds[] = 'rev_id=page_latest';
}
return Revision::newFromConds( $conds );
return self::newFromConds( $conds, $flags );
}
/**
@ -104,15 +116,21 @@ class Revision {
* that's attached to a given page ID.
* Returns null if no such revision can be found.
*
* $flags include:
* IDBAccessObject::LATEST_READ : Select the data from the master
* IDBAccessObject::LOCKING_READ : Select & lock the data from the master
* IDBAccessObject::AVOID_MASTER : Avoid master queries; data may be stale
*
* @param $revId Integer
* @param $pageId Integer (optional)
* @param $flags Integer Bitfield (optional)
* @return Revision or null
*/
public static function newFromPageId( $pageId, $revId = 0 ) {
public static function newFromPageId( $pageId, $revId = 0, $flags = 0 ) {
$conds = array( 'page_id' => $pageId );
if ( $revId ) {
$conds['rev_id'] = $revId;
} elseif ( wfGetLB()->getServerCount() > 1 ) {
} elseif ( !( $flags & self::AVOID_MASTER ) && wfGetLB()->getServerCount() > 1 ) {
// Get the latest revision ID from the master
$dbw = wfGetDB( DB_MASTER );
$latest = $dbw->selectField( 'page', 'page_latest', $conds, __METHOD__ );
@ -123,7 +141,7 @@ class Revision {
} else {
$conds[] = 'rev_id = page_latest';
}
return Revision::newFromConds( $conds );
return self::newFromConds( $conds, $flags );
}
/**
@ -189,7 +207,7 @@ class Revision {
* @return Revision or null
*/
public static function loadFromId( $db, $id ) {
return Revision::loadFromConds( $db, array( 'rev_id' => intval( $id ) ) );
return self::loadFromConds( $db, array( 'rev_id' => intval( $id ) ) );
}
/**
@ -209,7 +227,7 @@ class Revision {
} else {
$conds[] = 'rev_id=page_latest';
}
return Revision::loadFromConds( $db, $conds );
return self::loadFromConds( $db, $conds );
}
/**
@ -228,7 +246,7 @@ class Revision {
} else {
$matchId = 'page_latest';
}
return Revision::loadFromConds( $db,
return self::loadFromConds( $db,
array( "rev_id=$matchId",
'page_namespace' => $title->getNamespace(),
'page_title' => $title->getDBkey() )
@ -246,7 +264,7 @@ class Revision {
* @return Revision or null
*/
public static function loadFromTimestamp( $db, $title, $timestamp ) {
return Revision::loadFromConds( $db,
return self::loadFromConds( $db,
array( 'rev_timestamp' => $db->timestamp( $timestamp ),
'page_namespace' => $title->getNamespace(),
'page_title' => $title->getDBkey() )
@ -257,14 +275,17 @@ class Revision {
* Given a set of conditions, fetch a revision.
*
* @param $conditions Array
* @param $flags integer (optional)
* @return Revision or null
*/
public static function newFromConds( $conditions ) {
$db = wfGetDB( DB_SLAVE );
$rev = Revision::loadFromConds( $db, $conditions );
if( is_null( $rev ) && wfGetLB()->getServerCount() > 1 ) {
$dbw = wfGetDB( DB_MASTER );
$rev = Revision::loadFromConds( $dbw, $conditions );
private static function newFromConds( $conditions, $flags = 0 ) {
$db = wfGetDB( ( $flags & self::LATEST_READ ) ? DB_MASTER : DB_SLAVE );
$rev = self::loadFromConds( $db, $conditions, $flags );
if ( is_null( $rev ) && wfGetLB()->getServerCount() > 1 ) {
if ( !( $flags & self::LATEST_READ ) && !( $flags & self::AVOID_MASTER ) ) {
$dbw = wfGetDB( DB_MASTER );
$rev = self::loadFromConds( $dbw, $conditions, $flags );
}
}
return $rev;
}
@ -275,10 +296,11 @@ class Revision {
*
* @param $db DatabaseBase
* @param $conditions Array
* @param $flags integer (optional)
* @return Revision or null
*/
private static function loadFromConds( $db, $conditions ) {
$res = Revision::fetchFromConds( $db, $conditions );
private static function loadFromConds( $db, $conditions, $flags = 0 ) {
$res = self::fetchFromConds( $db, $conditions, $flags );
if( $res ) {
$row = $res->fetchObject();
if( $row ) {
@ -299,7 +321,7 @@ class Revision {
* @return ResultWrapper
*/
public static function fetchRevision( $title ) {
return Revision::fetchFromConds(
return self::fetchFromConds(
wfGetDB( DB_SLAVE ),
array( 'rev_id=page_latest',
'page_namespace' => $title->getNamespace(),
@ -314,20 +336,25 @@ class Revision {
*
* @param $db DatabaseBase
* @param $conditions Array
* @param $flags integer (optional)
* @return ResultWrapper
*/
private static function fetchFromConds( $db, $conditions ) {
private static function fetchFromConds( $db, $conditions, $flags = 0 ) {
$fields = array_merge(
self::selectFields(),
self::selectPageFields(),
self::selectUserFields()
);
$options = array( 'LIMIT' => 1 );
if ( $flags & self::FOR_UPDATE ) {
$options[] = 'FOR UPDATE';
}
return $db->select(
array( 'revision', 'page', 'user' ),
$fields,
$conditions,
__METHOD__,
array( 'LIMIT' => 1 ),
$options,
array( 'page' => self::pageJoinCond(), 'user' => self::userJoinCond() )
);
}
@ -1037,7 +1064,7 @@ class Revision {
if( $this->getTitle() ) {
$prev = $this->getTitle()->getPreviousRevisionID( $this->getId() );
if( $prev ) {
return Revision::newFromTitle( $this->getTitle(), $prev );
return self::newFromTitle( $this->getTitle(), $prev );
}
}
return null;
@ -1052,7 +1079,7 @@ class Revision {
if( $this->getTitle() ) {
$next = $this->getTitle()->getNextRevisionID( $this->getId() );
if ( $next ) {
return Revision::newFromTitle( $this->getTitle(), $next );
return self::newFromTitle( $this->getTitle(), $next );
}
}
return null;
@ -1182,7 +1209,7 @@ class Revision {
$text = gzdeflate( $text );
$flags[] = 'gzip';
} else {
wfDebug( "Revision::compressRevisionText() -- no zlib support, not compressing\n" );
wfDebug( __METHOD__ . " -- no zlib support, not compressing\n" );
}
}
return implode( ',', $flags );
@ -1203,7 +1230,7 @@ class Revision {
$this->checkContentModel();
$data = $this->mText;
$flags = Revision::compressRevisionText( $data );
$flags = self::compressRevisionText( $data );
# Write to external storage if required
if( $wgDefaultExternalStore ) {
@ -1237,7 +1264,6 @@ class Revision {
$rev_id = isset( $this->mId )
? $this->mId
: $dbw->nextSequenceValue( 'revision_rev_id_seq' );
$row = array(
'rev_id' => $rev_id,
'rev_page' => $this->mPage,
@ -1552,7 +1578,7 @@ class Revision {
static function countByTitle( $db, $title ) {
$id = $title->getArticleID();
if( $id ) {
return Revision::countByPageId( $db, $id );
return self::countByPageId( $db, $id );
}
return 0;
}

View file

@ -245,7 +245,7 @@ abstract class RevisionItemBase {
abstract public function canViewContent();
/**
* Get the HTML of the list item. Should be include <li></li> tags.
* Get the HTML of the list item. Should be include "<li></li>" tags.
* This is used to show the list in HTML form, by the special page.
*/
abstract public function getHTML();

View file

@ -692,6 +692,16 @@ class Sanitizer {
}
}
if ( $attribute === 'align' && !in_array( $element, $cells ) ) {
if ( $value === 'center' ) {
$style .= ' margin-left: auto;';
$property = 'margin-right';
$value = 'auto';
} else {
$property = 'float';
}
}
$style .= " $property: $value;";
unset( $attribs[$attribute] );
@ -1243,7 +1253,7 @@ class Sanitizer {
* a. named char refs can only be &lt; &gt; &amp; &quot;, others are
* numericized (this way we're well-formed even without a DTD)
* b. any numeric char refs must be legal chars, not invalid or forbidden
* c. use &#x, not &#X
* c. use lower cased "&#x", not "&#X"
* d. fix or reject non-valid attributes
*
* @param $text String
@ -1411,7 +1421,7 @@ class Sanitizer {
/**
* If the named entity is defined in the HTML 4.0/XHTML 1.0 DTD,
* return the UTF-8 encoding of that character. Otherwise, returns
* pseudo-entity source (eg &foo;)
* pseudo-entity source (eg "&foo;")
*
* @param $name String
* @return String

View file

@ -22,13 +22,17 @@
/**
* Class to expand PHP execution time for a function call.
* Use this when performing changes that should not be interrupted.
*
* On construction, set_time_limit() is called and set to $seconds.
* If the client aborts the connection, PHP will continue to run.
* When the object goes out of scope, the timer is restarted, with
* the original time limit minus the time the object existed.
*/
class ScopedPHPTimeout {
protected $startTime; // float; seconds
protected $oldTimeout; // integer; seconds
protected $oldIgnoreAbort; // boolean
protected static $stackDepth = 0; // integer
protected static $totalCalls = 0; // integer
@ -50,6 +54,7 @@ class ScopedPHPTimeout {
} elseif ( self::$stackDepth > 0 ) { // recursion guard
trigger_error( "Resursive invocation of " . __CLASS__ . " attempted." );
} else {
$this->oldIgnoreAbort = ignore_user_abort( true );
$this->oldTimeout = ini_set( 'max_execution_time', $seconds );
$this->startTime = microtime( true );
++self::$stackDepth;
@ -73,6 +78,7 @@ class ScopedPHPTimeout {
// take some measures to prevent this. Track total time and calls.
self::$totalElapsed += $elapsed;
--self::$stackDepth;
ignore_user_abort( $this->oldIgnoreAbort );
}
}
}

View file

@ -138,6 +138,7 @@ class SiteConfiguration {
/**
* Optional callback to load full configuration data.
* @var string|array
*/
public $fullLoadCallback = null;
@ -155,6 +156,8 @@ class SiteConfiguration {
* argument and the wiki in the second one.
* if suffix and lang are passed they will be used for the return value of
* self::siteFromDB() and self::$suffixes will be ignored
*
* @var string|array
*/
public $siteParamsCallback = null;
@ -189,7 +192,7 @@ class SiteConfiguration {
if( array_key_exists( $wiki, $thisSetting ) ) {
$retval = $thisSetting[$wiki];
break;
} elseif( array_key_exists( "+$wiki", $thisSetting ) && is_array( $thisSetting["+$wiki"] ) ) {
} elseif ( array_key_exists( "+$wiki", $thisSetting ) && is_array( $thisSetting["+$wiki"] ) ) {
$retval = $thisSetting["+$wiki"];
}
@ -203,8 +206,9 @@ class SiteConfiguration {
}
break 2;
} elseif( array_key_exists( "+$tag", $thisSetting ) && is_array($thisSetting["+$tag"]) ) {
if( !isset( $retval ) )
if( !isset( $retval ) ) {
$retval = array();
}
$retval = self::arrayMerge( $retval, $thisSetting["+$tag"] );
}
}
@ -218,9 +222,10 @@ class SiteConfiguration {
$retval = $thisSetting[$suffix];
}
break;
} elseif( array_key_exists( "+$suffix", $thisSetting ) && is_array($thisSetting["+$suffix"]) ) {
if (!isset($retval))
} elseif ( array_key_exists( "+$suffix", $thisSetting ) && is_array($thisSetting["+$suffix"]) ) {
if ( !isset( $retval ) ) {
$retval = array();
}
$retval = self::arrayMerge( $retval, $thisSetting["+$suffix"] );
}
}
@ -287,8 +292,9 @@ class SiteConfiguration {
}
$value = $this->getSetting( $varname, $wiki, $params );
if ( $append && is_array( $value ) && is_array( $GLOBALS[$var] ) )
if ( $append && is_array( $value ) && is_array( $GLOBALS[$var] ) ) {
$value = self::arrayMerge( $value, $GLOBALS[$var] );
}
if ( !is_null( $value ) ) {
$localSettings[$var] = $value;
}
@ -408,8 +414,9 @@ class SiteConfiguration {
}
foreach( $default as $name => $def ){
if( !isset( $ret[$name] ) || ( is_array( $default[$name] ) && !is_array( $ret[$name] ) ) )
if( !isset( $ret[$name] ) || ( is_array( $default[$name] ) && !is_array( $ret[$name] ) ) ) {
$ret[$name] = $default[$name];
}
}
return $ret;
@ -430,18 +437,21 @@ class SiteConfiguration {
protected function mergeParams( $wiki, $suffix, /*array*/ $params, /*array*/ $wikiTags ){
$ret = $this->getWikiParams( $wiki );
if( is_null( $ret['suffix'] ) )
if( is_null( $ret['suffix'] ) ) {
$ret['suffix'] = $suffix;
}
$ret['tags'] = array_unique( array_merge( $ret['tags'], $wikiTags ) );
$ret['params'] += $params;
// Automatically fill that ones if needed
if( !isset( $ret['params']['lang'] ) && !is_null( $ret['lang'] ) )
if( !isset( $ret['params']['lang'] ) && !is_null( $ret['lang'] ) ){
$ret['params']['lang'] = $ret['lang'];
if( !isset( $ret['params']['site'] ) && !is_null( $ret['suffix'] ) )
}
if( !isset( $ret['params']['site'] ) && !is_null( $ret['suffix'] ) ) {
$ret['params']['site'] = $ret['suffix'];
}
return $ret;
}
@ -455,8 +465,9 @@ class SiteConfiguration {
public function siteFromDB( $db ) {
// Allow override
$def = $this->getWikiParams( $db );
if( !is_null( $def['suffix'] ) && !is_null( $def['lang'] ) )
if( !is_null( $def['suffix'] ) && !is_null( $def['lang'] ) ) {
return array( $def['suffix'], $def['lang'] );
}
$site = null;
$lang = null;
@ -513,7 +524,7 @@ class SiteConfiguration {
}
public function loadFullData() {
if ($this->fullLoadCallback && !$this->fullLoadDone) {
if ( $this->fullLoadCallback && !$this->fullLoadDone ) {
call_user_func( $this->fullLoadCallback, $this );
$this->fullLoadDone = true;
}

View file

@ -326,7 +326,7 @@ abstract class Skin extends ContextSource {
}
/**
* Make a <script> tag containing global variables
* Make a "<script>" tag containing global variables
*
* @deprecated in 1.19
* @param $unused
@ -363,7 +363,7 @@ abstract class Skin extends ContextSource {
* inside ->getOutput() is deprecated. The $out arg is kept
* for compatibility purposes with skins.
* @param $out OutputPage
* @delete
* @todo delete
*/
abstract function setupSkinUserCss( OutputPage $out );
@ -397,7 +397,7 @@ abstract class Skin extends ContextSource {
/**
* This will be called by OutputPage::headElement when it is creating the
* <body> tag, skins can override it if they have a need to add in any
* "<body>" tag, skins can override it if they have a need to add in any
* body attributes or classes of their own.
* @param $out OutputPage
* @param $bodyAttrs Array
@ -636,9 +636,9 @@ abstract class Skin extends ContextSource {
}
/**
* This gets called shortly before the </body> tag.
* This gets called shortly before the "</body>" tag.
*
* @return String HTML-wrapped JS code to be put before </body>
* @return String HTML-wrapped JS code to be put before "</body>"
*/
function bottomScripts() {
// TODO and the suckage continues. This function is really just a wrapper around

View file

@ -95,7 +95,7 @@ class LegacyTemplate extends BaseTemplate {
}
/**
* This will be called immediately after the <body> tag. Split into
* This will be called immediately after the "<body>" tag. Split into
* two functions to make it easier to subclass.
* @return string
*/
@ -159,8 +159,8 @@ class LegacyTemplate extends BaseTemplate {
}
/**
* This gets called shortly before the </body> tag.
* @return String HTML to be put before </body>
* This gets called shortly before the "</body>" tag.
* @return String HTML to be put before "</body>"
*/
function afterContent() {
return $this->doAfterContent();
@ -638,7 +638,7 @@ class LegacyTemplate extends BaseTemplate {
$title = $this->getSkin()->getTitle();
if ( $wgOut->isArticleRelated() ) {
if ( $title->userIsWatching() ) {
if ( $wgUser->isWatched( $title ) ) {
$text = wfMsg( 'unwatchthispage' );
$query = array(
'action' => 'unwatch',

View file

@ -91,7 +91,7 @@ class SkinTemplate extends Skin {
var $template = 'QuickTemplate';
/**
* Whether this skin use OutputPage::headElement() to generate the <head>
* Whether this skin use OutputPage::headElement() to generate the "<head>"
* tag
*/
var $useHeadElement = false;
@ -258,7 +258,7 @@ class SkinTemplate extends Skin {
/* XXX currently unused, might get useful later
$tpl->set( 'editable', ( !$title->isSpecialPage() ) );
$tpl->set( 'exists', $title->getArticleID() != 0 );
$tpl->set( 'watch', $title->userIsWatching() ? 'unwatch' : 'watch' );
$tpl->set( 'watch', $user->isWatched( $title ) ? 'unwatch' : 'watch' );
$tpl->set( 'protect', count( $title->isProtected() ) ? 'unprotect' : 'protect' );
$tpl->set( 'helppage', $this->msg( 'helppage' )->text() );
*/
@ -974,7 +974,7 @@ class SkinTemplate extends Skin {
* a change to that procedure these messages will have to remain as
* the global versions.
*/
$mode = $title->userIsWatching() ? 'unwatch' : 'watch';
$mode = $user->isWatched( $title ) ? 'unwatch' : 'watch';
$token = WatchAction::getWatchToken( $title, $user, $mode );
$content_navigation['actions'][$mode] = array(
'class' => $onPage && ( $action == 'watch' || $action == 'unwatch' ) ? 'selected' : false,
@ -1605,26 +1605,39 @@ abstract class BaseTemplate extends QuickTemplate {
* Makes a link, usually used by makeListItem to generate a link for an item
* in a list used in navigation lists, portlets, portals, sidebars, etc...
*
* $key is a string, usually a key from the list you are generating this link from
* $item is an array containing some of a specific set of keys.
* The text of the link will be generated either from the contents of the "text"
* key in the $item array, if a "msg" key is present a message by that name will
* be used, and if neither of those are set the $key will be used as a message name.
* @param $key string usually a key from the list you are generating this
* link from.
* @param $item array contains some of a specific set of keys.
*
* The text of the link will be generated either from the contents of the
* "text" key in the $item array, if a "msg" key is present a message by
* that name will be used, and if neither of those are set the $key will be
* used as a message name.
*
* If a "href" key is not present makeLink will just output htmlescaped text.
* The href, id, class, rel, and type keys are used as attributes for the link if present.
* If an "id" or "single-id" (if you don't want the actual id to be output on the link)
* is present it will be used to generate a tooltip and accesskey for the link.
* The "href", "id", "class", "rel", and "type" keys are used as attributes
* for the link if present.
*
* If an "id" or "single-id" (if you don't want the actual id to be output
* on the link) is present it will be used to generate a tooltip and
* accesskey for the link.
*
* If you don't want an accesskey, set $item['tooltiponly'] = true;
* $options can be used to affect the output of a link:
* You can use a text-wrapper key to specify a list of elements to wrap the
* text of a link in. This should be an array of arrays containing a 'tag' and
* optionally an 'attributes' key. If you only have one element you don't need
* to wrap it in another array. eg: To use <a><span>...</span></a> in all links
* use array( 'text-wrapper' => array( 'tag' => 'span' ) ) for your options.
* A link-class key can be used to specify additional classes to apply to all links.
* A link-fallback can be used to specify a tag to use instead of <a> if there is
* no link. eg: If you specify 'link-fallback' => 'span' than any non-link will
* output a <span> instead of just text.
*
* @param $options array can be used to affect the output of a link.
* Possible options are:
* - 'text-wrapper' key to specify a list of elements to wrap the text of
* a link in. This should be an array of arrays containing a 'tag' and
* optionally an 'attributes' key. If you only have one element you don't
* need to wrap it in another array. eg: To use <a><span>...</span></a>
* in all links use array( 'text-wrapper' => array( 'tag' => 'span' ) )
* for your options.
* - 'link-class' key can be used to specify additional classes to apply
* to all links.
* - 'link-fallback' can be used to specify a tag to use instead of "<a>"
* if there is no link. eg: If you specify 'link-fallback' => 'span' than
* any non-link will output a "<span>" instead of just text.
*
* @return string
*/
function makeLink( $key, $item, $options = array() ) {
@ -1686,17 +1699,22 @@ abstract class BaseTemplate extends QuickTemplate {
}
/**
* Generates a list item for a navigation, portlet, portal, sidebar... etc list
* $key is a string, usually a key from the list you are generating this link from
* $item is an array of list item data containing some of a specific set of keys.
* Generates a list item for a navigation, portlet, portal, sidebar... list
*
* @param $key string, usually a key from the list you are generating this link from.
* @param $item array, of list item data containing some of a specific set of keys.
* The "id" and "class" keys will be used as attributes for the list item,
* if "active" contains a value of true a "active" class will also be appended to class.
* If you want something other than a <li> you can pass a tag name such as
*
* @param $options array
*
* If you want something other than a "<li>" you can pass a tag name such as
* "tag" => "span" in the $options array to change the tag used.
* link/content data for the list item may come in one of two forms
* A "links" key may be used, in which case it should contain an array with
* a list of links to include inside the list item, see makeLink for the format
* of individual links array items.
* a list of links to include inside the list item, see makeLink for the
* format of individual links array items.
*
* Otherwise the relevant keys from the list item $item array will be passed
* to makeLink instead. Note however that "id" and "class" are used by the
* list item directly so they will not be passed to makeLink
@ -1704,6 +1722,7 @@ abstract class BaseTemplate extends QuickTemplate {
* If you need an id or class on a single link you should include a "links"
* array with just one link item inside of it.
* $options is also passed on to makeLink calls
*
* @return string
*/
function makeListItem( $key, $item, $options = array() ) {

View file

@ -589,15 +589,70 @@ class SpecialPage {
$out->setPageTitle( $this->getDescription() );
}
/**
* Entry point.
*
* @since 1.20
*
* @param $subPage string|null
*/
public final function run( $subPage ) {
/**
* Gets called before @see SpecialPage::execute.
*
* @since 1.20
*
* @param $special SpecialPage
* @param $subPage string|null
*/
wfRunHooks( 'SpecialPageBeforeExecute', array( &$this, $subPage ) );
$this->beforeExecute( $subPage );
$this->execute( $subPage );
$this->afterExecute( $subPage );
/**
* Gets called after @see SpecialPage::execute.
*
* @since 1.20
*
* @param $special SpecialPage
* @param $subPage string|null
*/
wfRunHooks( 'SpecialPageAfterExecute', array( &$this, $subPage ) );
}
/**
* Gets called before @see SpecialPage::execute.
*
* @since 1.20
*
* @param $subPage string|null
*/
protected function beforeExecute( $subPage ) {
// No-op
}
/**
* Gets called after @see SpecialPage::execute.
*
* @since 1.20
*
* @param $subPage string|null
*/
protected function afterExecute( $subPage ) {
// No-op
}
/**
* Default execute method
* Checks user permissions, calls the function given in mFunction
*
* This must be overridden by subclasses; it will be made abstract in a future version
*
* @param $par String subpage string, if one was specified
* @param $subPage string|null
*/
function execute( $par ) {
public function execute( $subPage ) {
$this->setHeaders();
$this->checkPermissions();
@ -607,7 +662,7 @@ class SpecialPage {
require_once( $this->mFile );
}
$this->outputHeader();
call_user_func( $func, $par, $this );
call_user_func( $func, $subPage, $this );
}
/**
@ -1081,17 +1136,103 @@ class SpecialCreateAccount extends SpecialRedirectToSpecial {
* users.
*/
/**
* Superclass for any RedirectSpecialPage which redirects the user
* to a particular article (as opposed to user contributions, logs, etc.).
*
* For security reasons these special pages are restricted to pass on
* the following subset of GET parameters to the target page while
* removing all others:
*
* - useskin, uselang, printable: to alter the appearance of the resulting page
*
* - redirect: allows viewing one's user page or talk page even if it is a
* redirect.
*
* - rdfrom: allows redirecting to one's user page or talk page from an
* external wiki with the "Redirect from..." notice.
*
* - limit, offset: Useful for linking to history of one's own user page or
* user talk page. For example, this would be a link to "the last edit to your
* user talk page in the year 2010":
* http://en.wikipedia.org/w/index.php?title=Special:MyPage&offset=20110000000000&limit=1&action=history
*
* - feed: would allow linking to the current user's RSS feed for their user
* talk page:
* http://en.wikipedia.org/w/index.php?title=Special:MyTalk&action=history&feed=rss
*
* - preloadtitle: Can be used to provide a default section title for a
* preloaded new comment on one's own talk page.
*
* - summary : Can be used to provide a default edit summary for a preloaded
* edit to one's own user page or talk page.
*
* - preview: Allows showing/hiding preview on first edit regardless of user
* preference, useful for preloaded edits where you know preview wouldn't be
* useful.
*
* - internaledit, externaledit, mode: Allows forcing the use of the
* internal/external editor, e.g. to force the internal editor for
* short/simple preloaded edits.
*
* - redlink: Affects the message the user sees if their talk page/user talk
* page does not currently exist. Avoids confusion for newbies with no user
* pages over why they got a "permission error" following this link:
* http://en.wikipedia.org/w/index.php?title=Special:MyPage&redlink=1
*
* - debug: determines whether the debug parameter is passed to load.php,
* which disables reformatting and allows scripts to be debugged. Useful
* when debugging scripts that manipulate one's own user page or talk page.
*
* @par Hook extension:
* Extensions can add to the redirect parameters list by using the hook
* RedirectSpecialArticleRedirectParams
*
* This hook allows extensions which add GET parameters like FlaggedRevs to
* retain those parameters when redirecting using special pages.
*
* @par Hook extension example:
* @code
* $wgHooks['RedirectSpecialArticleRedirectParams'][] =
* 'MyExtensionHooks::onRedirectSpecialArticleRedirectParams';
* public static function onRedirectSpecialArticleRedirectParams( &$redirectParams ) {
* $redirectParams[] = 'stable';
* return true;
* }
* @endcode
* @ingroup SpecialPage
*/
abstract class RedirectSpecialArticle extends RedirectSpecialPage {
function __construct( $name ) {
parent::__construct( $name );
$redirectParams = array(
'action',
'redirect', 'rdfrom',
# Options for preloaded edits
'preload', 'editintro', 'preloadtitle', 'summary',
# Options for overriding user settings
'preview', 'internaledit', 'externaledit', 'mode',
# Options for history/diffs
'section', 'oldid', 'diff', 'dir',
'limit', 'offset', 'feed',
# Misc options
'redlink', 'debug',
# Options for action=raw; missing ctype can break JS or CSS in some browsers
'ctype', 'maxage', 'smaxage',
);
wfRunHooks( "RedirectSpecialArticleRedirectParams", array(&$redirectParams) );
$this->mAllowedRedirectParams = $redirectParams;
}
}
/**
* Shortcut to construct a special page pointing to current user user's page.
* @ingroup SpecialPage
*/
class SpecialMypage extends RedirectSpecialPage {
class SpecialMypage extends RedirectSpecialArticle {
function __construct() {
parent::__construct( 'Mypage' );
$this->mAllowedRedirectParams = array( 'action', 'preload', 'preloadtitle', 'editintro',
'section', 'oldid', 'diff', 'dir',
// Options for action=raw; missing ctype can break JS or CSS in some browsers
'ctype', 'maxage', 'smaxage' );
}
function getRedirect( $subpage ) {
@ -1107,11 +1248,9 @@ class SpecialMypage extends RedirectSpecialPage {
* Shortcut to construct a special page pointing to current user talk page.
* @ingroup SpecialPage
*/
class SpecialMytalk extends RedirectSpecialPage {
class SpecialMytalk extends RedirectSpecialArticle {
function __construct() {
parent::__construct( 'Mytalk' );
$this->mAllowedRedirectParams = array( 'action', 'preload', 'preloadtitle', 'editintro',
'section', 'oldid', 'diff', 'dir' );
}
function getRedirect( $subpage ) {

View file

@ -490,7 +490,7 @@ class SpecialPageFactory {
// Execute special page
$profName = 'Special:' . $page->getName();
wfProfileIn( $profName );
$page->execute( $par );
$page->run( $par );
wfProfileOut( $profName );
wfProfileOut( __METHOD__ );
return true;

View file

@ -24,6 +24,11 @@
/**
* Abstract base class for update jobs that put some secondary data extracted
* from article content into the database.
*
* @note: subclasses should NOT start or commit transactions in their doUpdate() method,
* a transaction will automatically be wrapped around the update. Starting another
* one would break the outer transaction bracket. If need be, subclasses can override
* the beginTransaction() and commitTransaction() methods.
*/
abstract class SqlDataUpdate extends DataUpdate {

View file

@ -44,7 +44,15 @@ class SquidPurgeClient {
* The socket resource, or null for unconnected, or false for disabled due to error
*/
var $socket;
var $readBuffer;
var $bodyRemaining;
/**
* @param $server string
* @param $options array
*/
public function __construct( $server, $options = array() ) {
$parts = explode( ':', $server, 2 );
$this->host = $parts[0];
@ -340,6 +348,9 @@ class SquidPurgeClient {
$this->bodyRemaining = null;
}
/**
* @param $msg string
*/
protected function log( $msg ) {
wfDebugLog( 'squid', __CLASS__." ($this->host): $msg\n" );
}
@ -353,6 +364,9 @@ class SquidPurgeClientPool {
var $clients = array();
var $timeout = 5;
/**
* @param $options array
*/
function __construct( $options = array() ) {
if ( isset( $options['timeout'] ) ) {
$this->timeout = $options['timeout'];
@ -372,6 +386,9 @@ class SquidPurgeClientPool {
$startTime = microtime( true );
while ( !$done ) {
$readSockets = $writeSockets = array();
/**
* @var $client SquidPurgeClient
*/
foreach ( $this->clients as $clientIndex => $client ) {
$sockets = $client->getReadSocketsForSelect();
foreach ( $sockets as $i => $socket ) {

View file

@ -213,7 +213,7 @@ class StringUtils {
* Returns an Iterator
* @param $separator
* @param $subject
* @return ArrayIterator|\ExplodeIterator
* @return ArrayIterator|ExplodeIterator
*/
static function explode( $separator, $subject ) {
if ( substr_count( $subject, $separator ) > 1000 ) {

View file

@ -1526,6 +1526,7 @@ class Title {
/**
* Is $wgUser watching this page?
*
* @deprecated in 1.20; use User::isWatched() instead.
* @return Bool
*/
public function userIsWatching() {
@ -2883,7 +2884,7 @@ class Title {
* @return Int or 0 if the page doesn't exist
*/
public function getLatestRevID( $flags = 0 ) {
if ( $this->mLatestID !== false ) {
if ( !( $flags & Title::GAID_FOR_UPDATE ) && $this->mLatestID !== false ) {
return intval( $this->mLatestID );
}
# Calling getArticleID() loads the field from cache as needed
@ -2909,7 +2910,7 @@ class Title {
*
* - This is called from WikiPage::doEdit() and WikiPage::insertOn() to allow
* loading of the new page_id. It's also called from
* WikiPage::doDeleteArticle()
* WikiPage::doDeleteArticleReal()
*
* @param $newid Int the new Article ID
*/
@ -3322,16 +3323,14 @@ class Title {
* @return Array of String the URLs
*/
public function getSquidURLs() {
global $wgContLang;
$urls = array(
$this->getInternalURL(),
$this->getInternalURL( 'action=history' )
);
// purge variant urls as well
if ( $wgContLang->hasVariants() ) {
$variants = $wgContLang->getVariants();
$pageLang = $this->getPageLanguage();
if ( $pageLang->hasVariants() ) {
$variants = $pageLang->getVariants();
foreach ( $variants as $vCode ) {
$urls[] = $this->getInternalURL( '', $vCode );
}
@ -3513,6 +3512,10 @@ class Title {
$wgUser->spreadAnyEditBlock();
return $err;
}
// Check suppressredirect permission
if ( $auth && !$wgUser->isAllowed( 'suppressredirect' ) ) {
$createRedirect = true;
}
// If it is a file, move it first.
// It is done before all other moving stuff is done because it's hard to revert.
@ -3609,8 +3612,8 @@ class Title {
*
* @param $nt Title the page to move to, which should be a redirect or nonexistent
* @param $reason String The reason for the move
* @param $createRedirect Bool Whether to leave a redirect at the old title. Ignored
* if the user doesn't have the suppressredirect right
* @param $createRedirect Bool Whether to leave a redirect at the old title. Does not check
* if the user has the suppressredirect right
* @throws MWException
*/
private function moveToInternal( &$nt, $reason = '', $createRedirect = true ) {
@ -3624,7 +3627,7 @@ class Title {
$logType = 'move';
}
$redirectSuppressed = !$createRedirect && $wgUser->isAllowed( 'suppressredirect' );
$redirectSuppressed = !$createRedirect;
$logEntry = new ManualLogEntry( 'move', $logType );
$logEntry->setPerformer( $wgUser );

View file

@ -65,6 +65,11 @@ class User {
const MW_USER_VERSION = MW_USER_VERSION;
const EDIT_TOKEN_SUFFIX = EDIT_TOKEN_SUFFIX;
/**
* Maximum items in $mWatchedItems
*/
const MAX_WATCHED_ITEMS_CACHE = 100;
/**
* Array of Strings List of member variables which are saved to the
* shared cache (memcached). Any operation which changes the
@ -114,6 +119,7 @@ class User {
'delete',
'deletedhistory',
'deletedtext',
'deletelogentry',
'deleterevision',
'edit',
'editinterface',
@ -219,6 +225,11 @@ class User {
*/
private $mBlockedFromCreateAccount = false;
/**
* @var Array
*/
private $mWatchedItems = array();
static $idCacheByName = array();
/**
@ -517,7 +528,7 @@ class User {
* as 300.300.300.300 will return true because it looks like an IP
* address, despite not being strictly valid.
*
* We match \d{1,3}\.\d{1,3}\.\d{1,3}\.xxx as an anonymous IP
* We match "\d{1,3}\.\d{1,3}\.\d{1,3}\.xxx" as an anonymous IP
* address because the usemod software would "cloak" anonymous IP
* addresses like this, if we allowed accounts like this to be created
* new users could get the old edits of these anonymous users.
@ -2278,9 +2289,11 @@ class User {
$this->loadOptions();
// Explicitly NULL values should refer to defaults
global $wgDefaultUserOptions;
if( is_null( $val ) && isset( $wgDefaultUserOptions[$oname] ) ) {
$val = $wgDefaultUserOptions[$oname];
if( is_null( $val ) ) {
$defaultOption = self::getDefaultOption( $oname );
if( !is_null( $defaultOption ) ) {
$val = $defaultOption;
}
}
$this->mOptions[$oname] = $val;
@ -2613,14 +2626,34 @@ class User {
return RequestContext::getMain()->getSkin();
}
/**
* Get a WatchedItem for this user and $title.
*
* @param $title Title
* @return WatchedItem
*/
public function getWatchedItem( $title ) {
$key = $title->getNamespace() . ':' . $title->getDBkey();
if ( isset( $this->mWatchedItems[$key] ) ) {
return $this->mWatchedItems[$key];
}
if ( count( $this->mWatchedItems ) >= self::MAX_WATCHED_ITEMS_CACHE ) {
$this->mWatchedItems = array();
}
$this->mWatchedItems[$key] = WatchedItem::fromUserTitle( $this, $title );
return $this->mWatchedItems[$key];
}
/**
* Check the watched status of an article.
* @param $title Title of the article to look at
* @return Bool
*/
public function isWatched( $title ) {
$wl = WatchedItem::fromUserTitle( $this, $title );
return $wl->isWatched();
return $this->getWatchedItem( $title )->isWatched();
}
/**
@ -2628,8 +2661,7 @@ class User {
* @param $title Title of the article to look at
*/
public function addWatch( $title ) {
$wl = WatchedItem::fromUserTitle( $this, $title );
$wl->addWatch();
$this->getWatchedItem( $title )->addWatch();
$this->invalidateCache();
}
@ -2638,8 +2670,7 @@ class User {
* @param $title Title of the article to look at
*/
public function removeWatch( $title ) {
$wl = WatchedItem::fromUserTitle( $this, $title );
$wl->removeWatch();
$this->getWatchedItem( $title )->removeWatch();
$this->invalidateCache();
}

View file

@ -504,9 +504,9 @@ class WebRequest {
* Fetch a text string from the given array or return $default if it's not
* set. Carriage returns are stripped from the text, and with some language
* modules there is an input transliteration applied. This should generally
* be used for form <textarea> and <input> fields. Used for user-supplied
* freeform text input (for which input transformations may be required - e.g.
* Esperanto x-coding).
* be used for form "<textarea>" and "<input>" fields. Used for
* user-supplied freeform text input (for which input transformations may
* be required - e.g. Esperanto x-coding).
*
* @param $name String
* @param $default String: optional

View file

@ -392,23 +392,6 @@ class MediaWiki {
return $article;
}
/**
* Cleaning up request by doing deferred updates, DB transaction, and the output
*/
public function finalCleanup() {
wfProfileIn( __METHOD__ );
// Now commit any transactions, so that unreported errors after
// output() don't roll back the whole DB transaction
$factory = wfGetLBFactory();
$factory->commitMasterChanges();
// Output everything!
$this->context->getOutput()->output();
// Do any deferred jobs
DeferredUpdates::doUpdates( 'commit' );
$this->doJobs();
wfProfileOut( __METHOD__ );
}
/**
* Do a job from the job queue
*/
@ -447,12 +430,23 @@ class MediaWiki {
* Ends this task peacefully
*/
public function restInPeace() {
// Do any deferred jobs
DeferredUpdates::doUpdates( 'commit' );
// Execute a job from the queue
$this->doJobs();
// Log message usage, if $wgAdaptiveMessageCache is set to true
MessageCache::logMessages();
// Log profiling data, e.g. in the database or UDP
wfLogProfilingData();
// Commit and close up!
$factory = wfGetLBFactory();
$factory->commitMasterChanges();
$factory->shutdown();
wfDebug( "Request ended normally\n" );
}
@ -599,7 +593,13 @@ class MediaWiki {
}
$this->performRequest();
$this->finalCleanup();
// Now commit any transactions, so that unreported errors after
// output() don't roll back the whole DB transaction
wfGetLBFactory()->commitMasterChanges();
// Output everything!
$this->context->getOutput()->output();
wfProfileOut( __METHOD__ );
}

View file

@ -126,6 +126,13 @@ class WikiReference {
private $mServer; ///< server URL, may be protocol-relative, e.g. '//www.mediawiki.org'
private $mPath; ///< path, '/wiki/$1'
/**
* @param $major string
* @param $minor string
* @param $canonicalServer string
* @param $path string
* @param $server null|string
*/
public function __construct( $major, $minor, $canonicalServer, $path, $server = null ) {
$this->mMajor = $major;
$this->mMinor = $minor;
@ -186,8 +193,17 @@ class WikiReference {
return $this->mCanonicalServer . $this->getLocalUrl( $page );
}
/**
* Get a canonical server URL
* @return string
*/
public function getCanonicalServer() {
return $this->mCanonicalServer;
}
/**
* Alias for getCanonicalUrl(), for backwards compatibility.
* @param $page string
* @return String
*/
public function getUrl( $page ) {

View file

@ -34,30 +34,6 @@ abstract class Page {}
* @internal documentation reviewed 15 Mar 2010
*/
class WikiPage extends Page {
// doDeleteArticleReal() return values. Values less than zero indicate fatal errors,
// values greater than zero indicate that there were problems not resulting in page
// not being deleted
/**
* Delete operation aborted by hook
*/
const DELETE_HOOK_ABORTED = -1;
/**
* Deletion successful
*/
const DELETE_SUCCESS = 0;
/**
* Page not found
*/
const DELETE_NO_PAGE = 1;
/**
* No revisions found to delete
*/
const DELETE_NO_REVISIONS = 2;
// Constants for $mDataLoadedFrom and related
/**
@ -606,7 +582,14 @@ class WikiPage extends Page {
return; // page doesn't exist or is missing page_latest info
}
$revision = Revision::newFromPageId( $this->getId(), $latest );
// Bug 37225: if session S1 loads the page row FOR UPDATE, the result always includes the
// latest changes committed. This is true even within REPEATABLE-READ transactions, where
// S1 normally only sees changes committed before the first S1 SELECT. Thus we need S1 to
// also gets the revision row FOR UPDATE; otherwise, it may not find it since a page row
// UPDATE and revision row INSERT by S2 may have happened after the first S1 SELECT.
// http://dev.mysql.com/doc/refman/5.0/en/set-transaction.html#isolevel_repeatable-read.
$flags = ( $this->mDataLoadedFrom == self::DATA_FOR_UPDATE ) ? Revision::LOCKING_READ : 0;
$revision = Revision::newFromPageId( $this->getId(), $latest, $flags );
if ( $revision ) { // sanity
$this->setLastEdit( $revision );
}
@ -1288,7 +1271,7 @@ class WikiPage extends Page {
$conditions,
__METHOD__ );
$result = $dbw->affectedRows() != 0;
$result = $dbw->affectedRows() > 0;
if ( $result ) {
$this->updateRedirectOn( $dbw, $rt, $lastRevIsRedirect );
$this->setLastEdit( $revision );
@ -1724,6 +1707,10 @@ class WikiPage extends Page {
wfProfileOut( __METHOD__ );
return $status;
} elseif ( !$old_content ) {
# Sanity check for bug 37225
wfProfileOut( __METHOD__ );
throw new MWException( "Could not find text for current revision {$oldid}." );
}
$revision = new Revision( array(
@ -1740,6 +1727,10 @@ class WikiPage extends Page {
'content_format' => $serialisation_format,
) ); #XXX: pass content object?!
# Bug 37225: use accessor to get the text as Revision may trim it.
# After trimming, the text may be a duplicate of the current text.
$content = $revision->getContent(); // sanity; EditPage should trim already
$changed = !$content->equals( $old_content );
if ( $changed ) {
@ -1872,6 +1863,9 @@ class WikiPage extends Page {
) );
$revisionId = $revision->insertOn( $dbw );
# Bug 37225: use accessor to get the text as Revision may trim it
$text = $revision->getText(); // sanity; EditPage should trim already
# Update the page record with revision data
$this->updateRevisionOn( $dbw, $revision, 0 );
@ -2423,7 +2417,10 @@ class WikiPage extends Page {
}
/**
* Same as doDeleteArticleReal(), but returns more detailed success/failure status
* Same as doDeleteArticleReal(), but returns a simple boolean. This is kept around for
* backwards compatibility, if you care about error reporting you should use
* doDeleteArticleReal() instead.
*
* Deletes the article with database consistency, writes logs, purges caches
*
* @param $reason string delete reason for deletion log
@ -2441,8 +2438,8 @@ class WikiPage extends Page {
public function doDeleteArticle(
$reason, $suppress = false, $id = 0, $commit = true, &$error = '', User $user = null
) {
return $this->doDeleteArticleReal( $reason, $suppress, $id, $commit, $error, $user )
== WikiPage::DELETE_SUCCESS;
$status = $this->doDeleteArticleReal( $reason, $suppress, $id, $commit, $error, $user );
return $status->isGood();
}
/**
@ -2459,7 +2456,9 @@ class WikiPage extends Page {
* @param $commit boolean defaults to true, triggers transaction end
* @param &$error Array of errors to append to
* @param $user User The deleting user
* @return int: One of WikiPage::DELETE_* constants
* @return Status: Status object; if successful, $status->value is the log_id of the
* deletion log entry. If the page couldn't be deleted because it wasn't
* found, $status is a non-fatal 'cannotdelete' error
*/
public function doDeleteArticleReal(
$reason, $suppress = false, $id = 0, $commit = true, &$error = '', User $user = null
@ -2468,20 +2467,28 @@ class WikiPage extends Page {
wfDebug( __METHOD__ . "\n" );
$status = Status::newGood();
if ( $this->mTitle->getDBkey() === '' ) {
return WikiPage::DELETE_NO_PAGE;
$status->error( 'cannotdelete', wfEscapeWikiText( $this->getTitle()->getPrefixedText() ) );
return $status;
}
$user = is_null( $user ) ? $wgUser : $user;
if ( ! wfRunHooks( 'ArticleDelete', array( &$this, &$user, &$reason, &$error ) ) ) {
return WikiPage::DELETE_HOOK_ABORTED;
if ( ! wfRunHooks( 'ArticleDelete', array( &$this, &$user, &$reason, &$error, &$status ) ) ) {
if ( $status->isOK() ) {
// Hook aborted but didn't set a fatal status
$status->fatal( 'delete-hook-aborted' );
}
return $status;
}
if ( $id == 0 ) {
$this->loadPageData( 'forupdate' );
$id = $this->getID();
if ( $id == 0 ) {
return WikiPage::DELETE_NO_PAGE;
$status->error( 'cannotdelete', wfEscapeWikiText( $this->getTitle()->getPrefixedText() ) );
return $status;
}
}
@ -2551,7 +2558,8 @@ class WikiPage extends Page {
if ( !$ok ) {
$dbw->rollback( __METHOD__ );
return WikiPage::DELETE_NO_REVISIONS;
$status->error( 'cannotdelete', wfEscapeWikiText( $this->getTitle()->getPrefixedText() ) );
return $status;
}
$this->doDeleteUpdates( $id, $content );
@ -2571,7 +2579,8 @@ class WikiPage extends Page {
}
wfRunHooks( 'ArticleDeleteComplete', array( &$this, &$user, $reason, $id ) );
return WikiPage::DELETE_SUCCESS;
$status->value = $logid;
return $status;
}
/**

View file

@ -210,15 +210,18 @@ class Xml {
* @param string $selected The language code of the selected language
* @param boolean $customisedOnly If true only languages which have some content are listed
* @param string $inLanguage The ISO code of the language to display the select list in (optional)
* @param array $overrideAttrs Override the attributes of the select tag (since 1.20)
* @param Message|null $msg Label message key (since 1.20)
* @return array containing 2 items: label HTML and select list HTML
*/
public static function languageSelector( $selected, $customisedOnly = true, $inLanguage = null ) {
public static function languageSelector( $selected, $customisedOnly = true, $inLanguage = null, $overrideAttrs = array(), Message $msg = null ) {
global $wgLanguageCode;
$languages = Language::fetchLanguageNames( $inLanguage, $customisedOnly ? 'mwfile' : 'mw' );
$include = $customisedOnly ? 'mwfile' : 'mw';
$languages = Language::fetchLanguageNames( $inLanguage, $include );
// Make sure the site language is in the list; a custom language code might not have a
// defined name...
// Make sure the site language is in the list;
// a custom language code might not have a defined name...
if( !array_key_exists( $wgLanguageCode, $languages ) ) {
$languages[$wgLanguageCode] = $wgLanguageCode;
}
@ -228,7 +231,7 @@ class Xml {
/**
* If a bogus value is set, default to the content language.
* Otherwise, no default is selected and the user ends up
* with an Afrikaans interface since it's first in the list.
* with Afrikaans since it's first in the list.
*/
$selected = isset( $languages[$selected] ) ? $selected : $wgLanguageCode;
$options = "\n";
@ -236,12 +239,15 @@ class Xml {
$options .= Xml::option( "$code - $name", $code, ($code == $selected) ) . "\n";
}
$attrs = array( 'id' => 'wpUserLanguage', 'name' => 'wpUserLanguage' );
$attrs = array_merge( $attrs, $overrideAttrs );
if( $msg === null ) {
$msg = wfMessage( 'yourlanguage' );
}
return array(
Xml::label( wfMsg('yourlanguage'), 'wpUserLanguage' ),
Xml::tags( 'select',
array( 'id' => 'wpUserLanguage', 'name' => 'wpUserLanguage' ),
$options
)
Xml::label( $msg->text(), $attrs['id'] ),
Xml::tags( 'select', $attrs, $options )
);
}

View file

@ -2570,7 +2570,6 @@ $zh2Hant = array(
'龚' => '龔',
'龛' => '龕',
'龟' => '龜',
'' => '棡',
'𠮶' => '嗰',
'𡒄' => '壈',
'𦈖' => '䌈',
@ -10389,7 +10388,6 @@ $zh2Hans = array(
'棖' => '枨',
'棗' => '枣',
'棟' => '栋',
'棡' => '',
'棧' => '栈',
'棲' => '栖',
'棶' => '梾',

View file

@ -418,34 +418,11 @@ class HistoryPager extends ReverseChronologicalPager {
$batch->add( NS_USER_TALK, $row->rev_user_text );
}
}
$this->parentLens = $this->getParentLengths( $revIds );
$this->parentLens = Revision::getParentLengths( $this->mDb, $revIds );
$batch->execute();
$this->mResult->seek( 0 );
}
/**
* Do a batched query to get the parent revision lengths
* @param $revIds array
* @return array
* @TODO: stolen from Contributions, refactor
*/
private function getParentLengths( array $revIds ) {
$revLens = array();
if ( !$revIds ) {
return $revLens; // empty
}
wfProfileIn( __METHOD__ );
$res = $this->mDb->select( 'revision',
array( 'rev_id', 'rev_len' ),
array( 'rev_id' => $revIds ),
__METHOD__ );
foreach ( $res as $row ) {
$revLens[$row->rev_id] = $row->rev_len;
}
wfProfileOut( __METHOD__ );
return $revLens;
}
/**
* Creates begin of history list with a submit button
*
@ -635,18 +612,17 @@ class HistoryPager extends ReverseChronologicalPager {
if ( $notificationtimestamp && ( $row->rev_timestamp >= $notificationtimestamp ) ) {
$s2 .= ' <span class="updatedmarker">' . $this->msg( 'updatedmarker' )->escaped() . '</span>';
$classes[] = 'mw-history-line-updated';
}
$tools = array();
# Rollback and undo links
if ( $prevRev &&
!count( $this->getTitle()->getUserPermissionsErrors( 'edit', $this->getUser() ) ) )
{
if ( $latest && !count( $this->getTitle()->getUserPermissionsErrors( 'rollback', $this->getUser() ) ) ) {
if ( $prevRev && $this->getTitle()->quickUserCan( 'edit', $user ) ) {
if ( $latest && $this->getTitle()->quickUserCan( 'rollback', $user ) ) {
$this->preventClickjacking();
$tools[] = '<span class="mw-rollback-link">' .
Linker::buildRollbackLink( $rev ) . '</span>';
Linker::buildRollbackLink( $rev, $this->getContext() ) . '</span>';
}
if ( !$rev->isDeleted( Revision::DELETED_TEXT )

View file

@ -107,6 +107,7 @@ class InfoAction extends FormlessAction {
* @return mixed array or boolean false
*/
public static function pageCountInfo( $title ) {
wfProfileIn( __METHOD__ );
$id = $title->getArticleID();
$dbr = wfGetDB( DB_SLAVE );
@ -114,8 +115,8 @@ class InfoAction extends FormlessAction {
'watchlist',
'COUNT(*)',
array(
'wl_namespace' => $title->getNamespace(),
'wl_title' => $title->getDBkey(),
'wl_namespace' => $title->getNamespace()
),
__METHOD__
);
@ -133,15 +134,21 @@ class InfoAction extends FormlessAction {
array( 'rev_page' => $id ),
__METHOD__
);
$result = array( 'watchers' => $watchers, 'edits' => $edits,
'authors' => $authors );
$views = (int)$dbr->selectField(
'page',
'page_counter',
array( 'page_id' => $id ),
__METHOD__
);
global $wgDisableCounters;
if ( !$wgDisableCounters ) {
$views = (int)$dbr->selectField(
'page',
'page_counter',
array( 'page_id' => $id ),
__METHOD__
);
$result['views'] = $views;
}
return array( 'watchers' => $watchers, 'edits' => $edits,
'authors' => $authors, 'views' => $views );
wfProfileOut( __METHOD__ );
return $result;
}
}

View file

@ -44,6 +44,6 @@ class RevisiondeleteAction extends FormlessAction {
public function show() {
$special = SpecialPageFactory::getPage( 'Revisiondelete' );
$special->setContext( $this->getContext() );
$special->execute( '' );
$special->run( '' );
}
}

View file

@ -4,7 +4,7 @@
*
* Created on Sep 5, 2006
*
* Copyright © 2006, 2010 Yuri Astrakhan <Firstname><Lastname>@gmail.com
* Copyright © 2006, 2010 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -134,7 +134,7 @@ abstract class ApiBase extends ContextSource {
/**
* Get the name of the module as shown in the profiler log
*
* @param $db DatabaseBase
* @param $db DatabaseBase|bool
*
* @return string
*/
@ -825,7 +825,7 @@ abstract class ApiBase extends ContextSource {
*/
protected function getWatchlistValue ( $watchlist, $titleObj, $userOption = null ) {
$userWatching = $titleObj->userIsWatching();
$userWatching = $this->getUser()->isWatched( $titleObj );
switch ( $watchlist ) {
case 'watch':
@ -1184,7 +1184,8 @@ abstract class ApiBase extends ContextSource {
* @param $errorCode string Brief, arbitrary, stable string to allow easy
* automated identification of the error, e.g., 'unknown_action'
* @param $httpRespCode int HTTP response code
* @param $extradata array Data to add to the <error> element; array in ApiResult format
* @param $extradata array Data to add to the "<error>" element; array in ApiResult format
* @throws UsageException
*/
public function dieUsage( $description, $errorCode, $httpRespCode = 0, $extradata = null ) {
Profiler::instance()->close();
@ -1307,6 +1308,8 @@ abstract class ApiBase extends ContextSource {
'specialpage-cantexecute' => array( 'code' => 'specialpage-cantexecute', 'info' => "You don't have permission to view the results of this special page" ),
'invalidoldimage' => array( 'code' => 'invalidoldimage', 'info' => 'The oldimage parameter has invalid format' ),
'nodeleteablefile' => array( 'code' => 'nodeleteablefile', 'info' => 'No such old version of the file' ),
'fileexists-forbidden' => array( 'code' => 'fileexists-forbidden', 'info' => 'A file with name "$1" already exists, and cannot be overwritten.' ),
'fileexists-shared-forbidden' => array( 'code' => 'fileexists-shared-forbidden', 'info' => 'A file with name "$1" already exists in the shared file repository, and cannot be overwritten.' ),
// ApiEditPage messages
'noimageredirect-anon' => array( 'code' => 'noimageredirect-anon', 'info' => "Anonymous users can't create image redirects" ),

View file

@ -4,7 +4,7 @@
*
* Created on Sep 4, 2007
*
* Copyright © 2007 Roan Kattouw <Firstname>.<Lastname>@gmail.com
* Copyright © 2007 Roan Kattouw "<Firstname>.<Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -72,9 +72,9 @@ class ApiBlock extends ApiBase {
$data = array(
'Target' => $params['user'],
'Reason' => array(
is_null( $params['reason'] ) ? '' : $params['reason'],
$params['reason'],
'other',
is_null( $params['reason'] ) ? '' : $params['reason']
$params['reason']
),
'Expiry' => $params['expiry'] == 'never' ? 'infinite' : $params['expiry'],
'HardBlock' => !$params['anononly'],
@ -156,7 +156,7 @@ class ApiBlock extends ApiBase {
ApiBase::PARAM_DEPRECATED => true,
),
'expiry' => 'never',
'reason' => null,
'reason' => '',
'anononly' => false,
'nocreate' => false,
'autoblock' => false,
@ -174,7 +174,7 @@ class ApiBlock extends ApiBase {
'token' => 'A block token previously obtained through prop=info',
'gettoken' => 'If set, a block token will be returned, and no other action will be taken',
'expiry' => 'Relative expiry time, e.g. \'5 months\' or \'2 weeks\'. If set to \'infinite\', \'indefinite\' or \'never\', the block will never expire.',
'reason' => 'Reason for block (optional)',
'reason' => 'Reason for block',
'anononly' => 'Block anonymous users only (i.e. disable anonymous edits for this IP)',
'nocreate' => 'Prevent account creation',
'autoblock' => 'Automatically block the last used IP address, and any subsequent IP addresses they try to login from',

View file

@ -4,7 +4,7 @@
*
* Created on Jun 30, 2007
*
* Copyright © 2007 Roan Kattouw <Firstname>.<Lastname>@gmail.com
* Copyright © 2007 Roan Kattouw "<Firstname>.<Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -52,17 +52,18 @@ class ApiDelete extends ApiBase {
}
$titleObj = $pageObj->getTitle();
$reason = ( isset( $params['reason'] ) ? $params['reason'] : null );
$reason = $params['reason'];
$user = $this->getUser();
if ( $titleObj->getNamespace() == NS_FILE ) {
$retval = self::deleteFile( $pageObj, $user, $params['token'], $params['oldimage'], $reason, false );
$status = self::deleteFile( $pageObj, $user, $params['token'], $params['oldimage'], $reason, false );
} else {
$retval = self::delete( $pageObj, $user, $params['token'], $reason );
$status = self::delete( $pageObj, $user, $params['token'], $reason );
}
if ( count( $retval ) ) {
$this->dieUsageMsg( reset( $retval ) ); // We don't care about multiple errors, just report one of them
if ( !$status->isGood() ) {
$errors = $status->getErrorsArray();
$this->dieUsageMsg( $errors[0] ); // We don't care about multiple errors, just report one of them
}
// Deprecated parameters
@ -75,7 +76,11 @@ class ApiDelete extends ApiBase {
}
$this->setWatch( $watch, $titleObj, 'watchdeletion' );
$r = array( 'title' => $titleObj->getPrefixedText(), 'reason' => $reason );
$r = array(
'title' => $titleObj->getPrefixedText(),
'reason' => $reason,
'logid' => $status->value
);
$this->getResult()->addValue( null, $this->getModuleName(), $r );
}
@ -97,7 +102,7 @@ class ApiDelete extends ApiBase {
* @param $user User doing the action
* @param $token String: delete token (same as edit token)
* @param $reason String: reason for the deletion. Autogenerated if NULL
* @return Title::getUserPermissionsErrors()-like array
* @return Status
*/
public static function delete( Page $page, User $user, $token, &$reason = null ) {
$title = $page->getTitle();
@ -119,11 +124,7 @@ class ApiDelete extends ApiBase {
$error = '';
// Luckily, Article.php provides a reusable delete function that does the hard work for us
if ( $page->doDeleteArticle( $reason, false, 0, true, $error ) ) {
return array();
} else {
return array( array( 'cannotdelete', $title->getPrefixedText() ) );
}
return $page->doDeleteArticleReal( $reason, false, 0, true, $error );
}
/**
@ -133,7 +134,7 @@ class ApiDelete extends ApiBase {
* @param $oldimage
* @param $reason
* @param $suppress bool
* @return array|Title
* @return Status
*/
public static function deleteFile( Page $page, User $user, $token, $oldimage, &$reason = null, $suppress = false ) {
$title = $page->getTitle();
@ -162,12 +163,7 @@ class ApiDelete extends ApiBase {
if ( is_null( $reason ) ) { // Log and RC don't like null reasons
$reason = '';
}
$status = FileDeleteForm::doDelete( $title, $file, $oldimage, $reason, $suppress );
if ( !$status->isGood() ) {
return array( array( 'cannotdelete', $title->getPrefixedText() ) );
}
return array();
return FileDeleteForm::doDelete( $title, $file, $oldimage, $reason, $suppress );
}
public function mustBePosted() {
@ -184,7 +180,10 @@ class ApiDelete extends ApiBase {
'pageid' => array(
ApiBase::PARAM_TYPE => 'integer'
),
'token' => null,
'token' => array(
ApiBase::PARAM_TYPE => 'string',
ApiBase::PARAM_REQUIRED => true
),
'reason' => null,
'watch' => array(
ApiBase::PARAM_DFLT => false,
@ -225,7 +224,8 @@ class ApiDelete extends ApiBase {
return array(
'' => array(
'title' => 'string',
'reason' => 'string'
'reason' => 'string',
'logid' => 'integer'
)
);
}

View file

@ -4,7 +4,7 @@
*
* Created on Sep 25, 2008
*
* Copyright © 2008 Roan Kattouw <Firstname>.<Lastname>@gmail.com
* Copyright © 2008 Roan Kattouw "<Firstname>.<Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View file

@ -4,7 +4,7 @@
*
* Created on August 16, 2007
*
* Copyright © 2007 Iker Labarga <Firstname><Lastname>@gmail.com
* Copyright © 2007 Iker Labarga "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -464,7 +464,10 @@ class ApiEditPage extends ApiBase {
ApiBase::PARAM_REQUIRED => false,
),
'text' => null,
'token' => null,
'token' => array(
ApiBase::PARAM_TYPE => 'string',
ApiBase::PARAM_REQUIRED => true
),
'summary' => null,
'minor' => false,
'notminor' => false,

View file

@ -98,7 +98,10 @@ class ApiEmailUser extends ApiBase {
ApiBase::PARAM_TYPE => 'string',
ApiBase::PARAM_REQUIRED => true
),
'token' => null,
'token' => array(
ApiBase::PARAM_TYPE => 'string',
ApiBase::PARAM_REQUIRED => true
),
'ccme' => false,
);
}

View file

@ -4,7 +4,7 @@
*
* Created on Oct 05, 2007
*
* Copyright © 2007 Yuri Astrakhan <Firstname><Lastname>@gmail.com
* Copyright © 2007 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View file

@ -4,7 +4,7 @@
*
* Created on Oct 13, 2006
*
* Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
* Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View file

@ -126,7 +126,10 @@ class ApiFileRevert extends ApiBase {
ApiBase::PARAM_TYPE => 'string',
ApiBase::PARAM_REQUIRED => true,
),
'token' => null,
'token' => array(
ApiBase::PARAM_TYPE => 'string',
ApiBase::PARAM_REQUIRED => true
),
);
}

View file

@ -4,7 +4,7 @@
*
* Created on Sep 19, 2006
*
* Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
* Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View file

@ -4,7 +4,7 @@
*
* Created on Oct 22, 2006
*
* Copyright © 2008 Roan Kattouw <Firstname>.<Lastname>@gmail.com
* Copyright © 2008 Roan Kattouw "<Firstname>.<Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View file

@ -4,7 +4,7 @@
*
* Created on Sep 19, 2006
*
* Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
* Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View file

@ -4,7 +4,7 @@
*
* Created on Oct 22, 2006
*
* Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
* Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View file

@ -4,7 +4,7 @@
*
* Created on Feb 2, 2009
*
* Copyright © 2009 Roan Kattouw <Firstname>.<Lastname>@gmail.com
* Copyright © 2009 Roan Kattouw "<Firstname>.<Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View file

@ -4,7 +4,7 @@
*
* Created on Oct 22, 2006
*
* Copyright © 2008 Roan Kattouw <Firstname>.<Lastname>@gmail.com
* Copyright © 2008 Roan Kattouw "<Firstname>.<Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View file

@ -4,7 +4,7 @@
*
* Created on Oct 22, 2006
*
* Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
* Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View file

@ -4,7 +4,7 @@
*
* Created on Sep 19, 2006
*
* Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
* Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -83,16 +83,40 @@ class ApiFormatXml extends ApiFormatBase {
/**
* This method takes an array and converts it to XML.
*
* There are several noteworthy cases:
*
* If array contains a key '_element', then the code assumes that ALL other keys are not important and replaces them with the value['_element'].
* Example: name='root', value = array( '_element'=>'page', 'x', 'y', 'z') creates <root> <page>x</page> <page>y</page> <page>z</page> </root>
* If array contains a key '_element', then the code assumes that ALL
* other keys are not important and replaces them with the
* value['_element'].
*
* If any of the array's element key is '*', then the code treats all other key->value pairs as attributes, and the value['*'] as the element's content.
* Example: name='root', value = array( '*'=>'text', 'lang'=>'en', 'id'=>10) creates <root lang='en' id='10'>text</root>
* @par Example:
* @verbatim
* name='root', value = array( '_element'=>'page', 'x', 'y', 'z')
* @endverbatim
* creates:
* @verbatim
* <root> <page>x</page> <page>y</page> <page>z</page> </root>
* @endverbatim
*
* If neither key is found, all keys become element names, and values become element content.
* The method is recursive, so the same rules apply to any sub-arrays.
* If any of the array's element key is '*', then the code treats all
* other key->value pairs as attributes, and the value['*'] as the
* element's content.
*
* @par Example:
* @verbatim
* name='root', value = array( '*'=>'text', 'lang'=>'en', 'id'=>10)
* @endverbatim
* creates:
* @verbatim
* <root lang='en' id='10'>text</root>
* @endverbatim
*
* Finally neither key is found, all keys become element names, and values
* become element content.
*
* @note The method is recursive, so the same rules apply to any
* sub-arrays.
*
* @param $elemName
* @param $elemValue

View file

@ -4,7 +4,7 @@
*
* Created on Sep 19, 2006
*
* Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
* Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View file

@ -4,7 +4,7 @@
*
* Created on Sep 6, 2006
*
* Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
* Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View file

@ -4,7 +4,7 @@
*
* Created on Feb 4, 2009
*
* Copyright © 2009 Roan Kattouw <Firstname>.<Lastname>@gmail.com
* Copyright © 2009 Roan Kattouw "<Firstname>.<Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -98,7 +98,10 @@ class ApiImport extends ApiBase {
public function getAllowedParams() {
global $wgImportSources;
return array(
'token' => null,
'token' => array(
ApiBase::PARAM_TYPE => 'string',
ApiBase::PARAM_REQUIRED => true
),
'summary' => null,
'xml' => null,
'interwikisource' => array(

View file

@ -4,7 +4,7 @@
*
* Created on Sep 19, 2006
*
* Copyright © 2006-2007 Yuri Astrakhan <Firstname><Lastname>@gmail.com,
* Copyright © 2006-2007 Yuri Astrakhan "<Firstname><Lastname>@gmail.com",
* Daniel Cannon (cannon dot danielc at gmail dot com)
*
* This program is free software; you can redistribute it and/or modify

View file

@ -4,7 +4,7 @@
*
* Created on Jan 4, 2008
*
* Copyright © 2008 Yuri Astrakhan <Firstname><Lastname>@gmail.com,
* Copyright © 2008 Yuri Astrakhan "<Firstname><Lastname>@gmail.com",
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View file

@ -4,7 +4,7 @@
*
* Created on Sep 4, 2006
*
* Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
* Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -355,6 +355,12 @@ class ApiMain extends ApiBase {
* have been accumulated, and replace it with an error message and a help screen.
*/
protected function executeActionWithErrorHandling() {
// Verify the CORS header before executing the action
if ( !$this->handleCORS() ) {
// handleCORS() has sent a 403, abort
return;
}
// In case an error occurs during data output,
// clear the output buffer and print just the error information
ob_start();
@ -403,9 +409,94 @@ class ApiMain extends ApiBase {
ob_end_flush();
}
/**
* Check the &origin= query parameter against the Origin: HTTP header and respond appropriately.
*
* If no origin parameter is present, nothing happens.
* If an origin parameter is present but doesn't match the Origin header, a 403 status code
* is set and false is returned.
* If the parameter and the header do match, the header is checked against $wgCrossSiteAJAXdomains
* and $wgCrossSiteAJAXdomainExceptions, and if the origin qualifies, the appropriate CORS
* headers are set.
*
* @return bool False if the caller should abort (403 case), true otherwise (all other cases)
*/
protected function handleCORS() {
global $wgCrossSiteAJAXdomains, $wgCrossSiteAJAXdomainExceptions;
$response = $this->getRequest()->response();
$originParam = $this->getParameter( 'origin' ); // defaults to null
if ( $originParam === null ) {
// No origin parameter, nothing to do
return true;
}
// Origin: header is a space-separated list of origins, check all of them
$originHeader = isset( $_SERVER['HTTP_ORIGIN'] ) ? $_SERVER['HTTP_ORIGIN'] : '';
$origins = explode( ' ', $originHeader );
if ( !in_array( $originParam, $origins ) ) {
// origin parameter set but incorrect
// Send a 403 response
$message = HttpStatus::getMessage( 403 );
$response->header( "HTTP/1.1 403 $message", true, 403 );
$response->header( 'Cache-Control: no-cache' );
echo "'origin' parameter does not match Origin header\n";
return false;
}
if ( self::matchOrigin( $originParam, $wgCrossSiteAJAXdomains, $wgCrossSiteAJAXdomainExceptions ) ) {
$response->header( "Access-Control-Allow-Origin: $originParam" );
$response->header( 'Access-Control-Allow-Credentials: true' );
$this->getOutput()->addVaryHeader( 'Origin' );
}
return true;
}
/**
* Attempt to match an Origin header against a set of rules and a set of exceptions
* @param $value string Origin header
* @param $rules array Set of wildcard rules
* @param $exceptions array Set of wildcard rules
* @return bool True if $value matches a rule in $rules and doesn't match any rules in $exceptions, false otherwise
*/
protected static function matchOrigin( $value, $rules, $exceptions ) {
foreach ( $rules as $rule ) {
if ( preg_match( self::wildcardToRegex( $rule ), $value ) ) {
// Rule matches, check exceptions
foreach ( $exceptions as $exc ) {
if ( preg_match( self::wildcardToRegex( $exc ), $value ) ) {
return false;
}
}
return true;
}
}
return false;
}
/**
* Helper function to convert wildcard string into a regex
* '*' => '.*?'
* '?' => '.'
*
* @param $wildcard string String with wildcards
* @return string Regular expression
*/
protected static function wildcardToRegex( $wildcard ) {
$wildcard = preg_quote( $wildcard, '/' );
$wildcard = str_replace(
array( '\*', '\?' ),
array( '.*?', '.' ),
$wildcard
);
return "/https?:\/\/$wildcard/";
}
protected function sendCacheHeaders() {
global $wgUseXVO, $wgVaryOnXFP;
$response = $this->getRequest()->response();
$out = $this->getOutput();
if ( $wgVaryOnXFP ) {
$out->addVaryHeader( 'X-Forwarded-Proto' );
}
if ( $this->mCacheMode == 'private' ) {
$response->header( 'Cache-Control: private' );
@ -413,13 +504,9 @@ class ApiMain extends ApiBase {
}
if ( $this->mCacheMode == 'anon-public-user-private' ) {
$xfp = $wgVaryOnXFP ? ', X-Forwarded-Proto' : '';
$response->header( 'Vary: Accept-Encoding, Cookie' . $xfp );
$out->addVaryHeader( 'Cookie' );
$response->header( $out->getVaryHeader() );
if ( $wgUseXVO ) {
$out = $this->getOutput();
if ( $wgVaryOnXFP ) {
$out->addVaryHeader( 'X-Forwarded-Proto' );
}
$response->header( $out->getXVO() );
if ( $out->haveCacheVaryCookies() ) {
// Logged in, mark this request private
@ -436,12 +523,9 @@ class ApiMain extends ApiBase {
}
// Send public headers
if ( $wgVaryOnXFP ) {
$response->header( 'Vary: Accept-Encoding, X-Forwarded-Proto' );
if ( $wgUseXVO ) {
// Bleeeeegh. Our header setting system sucks
$response->header( 'X-Vary-Options: Accept-Encoding;list-contains=gzip, X-Forwarded-Proto' );
}
$response->header( $out->getVaryHeader() );
if ( $wgUseXVO ) {
$response->header( $out->getXVO() );
}
// If nobody called setCacheMaxAge(), use the (s)maxage parameters
@ -785,6 +869,7 @@ class ApiMain extends ApiBase {
),
'requestid' => null,
'servedby' => false,
'origin' => null,
);
}
@ -810,6 +895,12 @@ class ApiMain extends ApiBase {
'maxage' => 'Set the max-age header to this many seconds. Errors are never cached',
'requestid' => 'Request ID to distinguish requests. This will just be output back to you',
'servedby' => 'Include the hostname that served the request in the results. Unconditionally shown on error',
'origin' => array(
'When accessing the API using a cross-domain AJAX request (CORS), set this to the originating domain.',
'This must match one of the origins in the Origin: header exactly, so it has to be set to something like http://en.wikipedia.org or https://meta.wikimedia.org .',
'If this parameter does not match the Origin: header, a 403 response will be returned.',
'If this parameter matches the Origin: header and the origin is whitelisted, an Access-Control-Allow-Origin header will be set.',
),
);
}
@ -877,11 +968,11 @@ class ApiMain extends ApiBase {
protected function getCredits() {
return array(
'API developers:',
' Roan Kattouw <Firstname>.<Lastname>@gmail.com (lead developer Sep 2007-present)',
' Roan Kattouw "<Firstname>.<Lastname>@gmail.com" (lead developer Sep 2007-present)',
' Victor Vasiliev - vasilvv at gee mail dot com',
' Bryan Tong Minh - bryan . tongminh @ gmail . com',
' Sam Reed - sam @ reedyboy . net',
' Yuri Astrakhan <Firstname><Lastname>@gmail.com (creator, lead developer Sep 2006-Sep 2007)',
' Yuri Astrakhan "<Firstname><Lastname>@gmail.com" (creator, lead developer Sep 2006-Sep 2007)',
'',
'Please send your comments, suggestions and questions to mediawiki-api@lists.wikimedia.org',
'or file a bug report at https://bugzilla.wikimedia.org/'
@ -1070,8 +1161,18 @@ class ApiMain extends ApiBase {
class UsageException extends MWException {
private $mCodestr;
/**
* @var null|array
*/
private $mExtraData;
/**
* @param $message string
* @param $codestr string
* @param $code int
* @param $extradata array|null
*/
public function __construct( $message, $codestr, $code = 0, $extradata = null ) {
parent::__construct( $message, $code );
$this->mCodestr = $codestr;

View file

@ -4,7 +4,7 @@
*
* Created on Oct 31, 2007
*
* Copyright © 2007 Roan Kattouw <Firstname>.<Lastname>@gmail.com
* Copyright © 2007 Roan Kattouw "<Firstname>.<Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -37,9 +37,6 @@ class ApiMove extends ApiBase {
public function execute() {
$user = $this->getUser();
$params = $this->extractRequestParams();
if ( is_null( $params['reason'] ) ) {
$params['reason'] = '';
}
$this->requireOnlyOneParameter( $params, 'from', 'fromid' );
@ -180,8 +177,11 @@ class ApiMove extends ApiBase {
ApiBase::PARAM_TYPE => 'string',
ApiBase::PARAM_REQUIRED => true
),
'token' => null,
'reason' => null,
'token' => array(
ApiBase::PARAM_TYPE => 'string',
ApiBase::PARAM_REQUIRED => true
),
'reason' => '',
'movetalk' => false,
'movesubpages' => false,
'noredirect' => false,
@ -213,7 +213,7 @@ class ApiMove extends ApiBase {
'fromid' => "Page ID of the page you want to move. Cannot be used together with {$p}from",
'to' => 'Title you want to rename the page to',
'token' => 'A move token previously retrieved through prop=info',
'reason' => 'Reason for the move (optional)',
'reason' => 'Reason for the move',
'movetalk' => 'Move the talk page, if it exists',
'movesubpages' => 'Move subpages, if applicable',
'noredirect' => 'Don\'t create a redirect',

View file

@ -4,7 +4,7 @@
*
* Created on Oct 13, 2006
*
* Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
* Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View file

@ -4,7 +4,7 @@
*
* Created on Sep 24, 2006
*
* Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
* Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -52,7 +52,7 @@ class ApiPageSet extends ApiQueryBase {
/**
* Constructor
* @param $query ApiQueryBase
* @param $query ApiBase
* @param $resolveRedirects bool Whether redirects should be resolved
* @param $convertTitles bool
*/
@ -266,8 +266,8 @@ class ApiPageSet extends ApiQueryBase {
}
/**
* Returns the number of revisions (requested with revids= parameter)\
* @return int
* Returns the number of revisions (requested with revids= parameter).
* @return int Number of revisions.
*/
public function getRevisionCount() {
return count( $this->getRevisionIDs() );

View file

@ -4,7 +4,7 @@
*
* Created on Dec 01, 2007
*
* Copyright © 2008 Roan Kattouw <Firstname>.<Lastname>@gmail.com
* Copyright © 2008 Roan Kattouw "<Firstname>.<Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View file

@ -2,7 +2,7 @@
/**
* Created on Dec 01, 2007
*
* Copyright © 2007 Yuri Astrakhan <Firstname><Lastname>@gmail.com
* Copyright © 2007 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View file

@ -65,7 +65,10 @@ class ApiPatrol extends ApiBase {
public function getAllowedParams() {
return array(
'token' => null,
'token' => array(
ApiBase::PARAM_TYPE => 'string',
ApiBase::PARAM_REQUIRED => true
),
'rcid' => array(
ApiBase::PARAM_TYPE => 'integer',
ApiBase::PARAM_REQUIRED => true

View file

@ -4,7 +4,7 @@
*
* Created on Sep 1, 2007
*
* Copyright © 2007 Roan Kattouw <Firstname>.<Lastname>@gmail.com
* Copyright © 2007 Roan Kattouw "<Firstname>.<Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -139,7 +139,10 @@ class ApiProtect extends ApiBase {
'pageid' => array(
ApiBase::PARAM_TYPE => 'integer',
),
'token' => null,
'token' => array(
ApiBase::PARAM_TYPE => 'string',
ApiBase::PARAM_REQUIRED => true
),
'protections' => array(
ApiBase::PARAM_ISMULTI => true,
ApiBase::PARAM_REQUIRED => true,
@ -176,7 +179,7 @@ class ApiProtect extends ApiBase {
'protections' => 'Pipe-separated list of protection levels, formatted action=group (e.g. edit=sysop)',
'expiry' => array( 'Expiry timestamps. If only one timestamp is set, it\'ll be used for all protections.',
'Use \'infinite\', \'indefinite\' or \'never\', for a neverexpiring protection.' ),
'reason' => 'Reason for (un)protecting (optional)',
'reason' => 'Reason for (un)protecting',
'cascade' => array( 'Enable cascading protection (i.e. protect pages included in this page)',
'Ignored if not all protection levels are \'sysop\' or \'protect\'' ),
'watch' => 'If set, add the page being (un)protected to your watchlist',

View file

@ -4,7 +4,7 @@
*
* Created on Sep 7, 2006
*
* Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
* Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -103,6 +103,10 @@ class ApiQuery extends ApiBase {
protected $mAllowedGenerators = array();
/**
* @param $main ApiMain
* @param $action string
*/
public function __construct( $main, $action ) {
parent::__construct( $main, $action );
@ -202,6 +206,9 @@ class ApiQuery extends ApiBase {
return null;
}
/**
* @return ApiFormatRaw|null
*/
public function getCustomPrinter() {
// If &exportnowrap is set, use the raw formatter
if ( $this->getParameter( 'export' ) &&
@ -258,6 +265,9 @@ class ApiQuery extends ApiBase {
$this->outputGeneralPageInfo();
// Execute all requested modules.
/**
* @var $module ApiQueryBase
*/
foreach ( $modules as $module ) {
$params = $module->extractRequestParams();
$cacheMode = $this->mergeCacheMode(
@ -303,6 +313,9 @@ class ApiQuery extends ApiBase {
*/
private function addCustomFldsToPageSet( $modules, $pageSet ) {
// Query all requested modules.
/**
* @var $module ApiQueryBase
*/
foreach ( $modules as $module ) {
$module->requestExtraData( $pageSet );
}
@ -384,6 +397,9 @@ class ApiQuery extends ApiBase {
// Show redirect information
$redirValues = array();
/**
* @var $titleTo Title
*/
foreach ( $pageSet->getRedirectTitles() as $titleStrFrom => $titleTo ) {
$r = array(
'from' => strval( $titleStrFrom ),

View file

@ -4,7 +4,7 @@
*
* Created on December 12, 2007
*
* Copyright © 2007 Roan Kattouw <Firstname>.<Lastname>@gmail.com
* Copyright © 2007 Roan Kattouw "<Firstname>.<Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -58,6 +58,17 @@ class ApiQueryAllCategories extends ApiQueryGeneratorBase {
$this->addTables( 'category' );
$this->addFields( 'cat_title' );
if ( !is_null( $params['continue'] ) ) {
$cont = explode( '|', $params['continue'] );
if ( count( $cont ) != 1 ) {
$this->dieUsage( "Invalid continue param. You should pass the " .
"original value returned by the previous query", "_badcontinue" );
}
$op = $params['dir'] == 'descending' ? '<' : '>';
$cont_from = $db->addQuotes( $cont[0] );
$this->addWhere( "cat_title $op= $cont_from" );
}
$dir = ( $params['dir'] == 'descending' ? 'older' : 'newer' );
$from = ( is_null( $params['from'] ) ? null : $this->titlePartToKey( $params['from'] ) );
$to = ( is_null( $params['to'] ) ? null : $this->titlePartToKey( $params['to'] ) );
@ -104,8 +115,7 @@ class ApiQueryAllCategories extends ApiQueryGeneratorBase {
foreach ( $res as $row ) {
if ( ++ $count > $params['limit'] ) {
// We've reached the one extra which shows that there are additional cats to be had. Stop here...
// TODO: Security issue - if the user has no right to view next title, it will still be shown
$this->setContinueEnumParameter( 'from', $this->keyToTitle( $row->cat_title ) );
$this->setContinueEnumParameter( 'continue', $row->cat_title );
break;
}
@ -127,7 +137,7 @@ class ApiQueryAllCategories extends ApiQueryGeneratorBase {
}
$fit = $result->addValue( array( 'query', $this->getModuleName() ), null, $item );
if ( !$fit ) {
$this->setContinueEnumParameter( 'from', $this->keyToTitle( $row->cat_title ) );
$this->setContinueEnumParameter( 'continue', $row->cat_title );
break;
}
}
@ -143,6 +153,7 @@ class ApiQueryAllCategories extends ApiQueryGeneratorBase {
public function getAllowedParams() {
return array(
'from' => null,
'continue' => null,
'to' => null,
'prefix' => null,
'dir' => array(
@ -178,6 +189,7 @@ class ApiQueryAllCategories extends ApiQueryGeneratorBase {
public function getParamDescription() {
return array(
'from' => 'The category to start enumerating from',
'continue' => 'When more results are available, use this to continue',
'to' => 'The category to stop enumerating at',
'prefix' => 'Search for all category titles that begin with this value',
'dir' => 'Direction to sort in',
@ -213,6 +225,12 @@ class ApiQueryAllCategories extends ApiQueryGeneratorBase {
return 'Enumerate all categories';
}
public function getPossibleErrors() {
return array_merge( parent::getPossibleErrors(), array(
array( 'code' => '_badcontinue', 'info' => 'Invalid continue param. You should pass the original value returned by the previous query' ),
) );
}
public function getExamples() {
return array(
'api.php?action=query&list=allcategories&acprop=size',

View file

@ -85,6 +85,17 @@ class ApiQueryAllImages extends ApiQueryGeneratorBase {
$params = $this->extractRequestParams();
if ( !is_null( $params['continue'] ) ) {
$cont = explode( '|', $params['continue'] );
if ( count( $cont ) != 1 ) {
$this->dieUsage( "Invalid continue param. You should pass the " .
"original value returned by the previous query", "_badcontinue" );
}
$op = $params['dir'] == 'descending' ? '<' : '>';
$cont_from = $db->addQuotes( $cont[0] );
$this->addWhere( "img_name $op= $cont_from" );
}
// Image filters
$dir = ( $params['dir'] == 'descending' ? 'older' : 'newer' );
$from = ( is_null( $params['from'] ) ? null : $this->titlePartToKey( $params['from'] ) );
@ -148,8 +159,7 @@ class ApiQueryAllImages extends ApiQueryGeneratorBase {
foreach ( $res as $row ) {
if ( ++ $count > $limit ) {
// We've reached the one extra which shows that there are additional pages to be had. Stop here...
// TODO: Security issue - if the user has no right to view next title, it will still be shown
$this->setContinueEnumParameter( 'from', $this->keyToTitle( $row->img_name ) );
$this->setContinueEnumParameter( 'continue', $row->img_name );
break;
}
@ -161,11 +171,11 @@ class ApiQueryAllImages extends ApiQueryGeneratorBase {
$fit = $result->addValue( array( 'query', $this->getModuleName() ), null, $info );
if ( !$fit ) {
$this->setContinueEnumParameter( 'from', $this->keyToTitle( $row->img_name ) );
$this->setContinueEnumParameter( 'continue', $row->img_name );
break;
}
} else {
$titles[] = Title::makeTitle( NS_IMAGE, $row->img_name );
$titles[] = Title::makeTitle( NS_FILE, $row->img_name );
}
}
@ -179,6 +189,7 @@ class ApiQueryAllImages extends ApiQueryGeneratorBase {
public function getAllowedParams() {
return array (
'from' => null,
'continue' => null,
'to' => null,
'prefix' => null,
'minsize' => array(
@ -215,6 +226,7 @@ class ApiQueryAllImages extends ApiQueryGeneratorBase {
public function getParamDescription() {
return array(
'from' => 'The image title to start enumerating from',
'continue' => 'When more results are available, use this to continue',
'to' => 'The image title to stop enumerating at',
'prefix' => 'Search for all image titles that begin with this value',
'dir' => 'The direction in which to list',
@ -228,7 +240,7 @@ class ApiQueryAllImages extends ApiQueryGeneratorBase {
);
}
private $propertyFilter = array( 'archivename' );
private $propertyFilter = array( 'archivename', 'thumbmime' );
public function getResultProperties() {
return array_merge(
@ -254,6 +266,7 @@ class ApiQueryAllImages extends ApiQueryGeneratorBase {
array( 'code' => 'mimesearchdisabled', 'info' => 'MIME search disabled in Miser Mode' ),
array( 'code' => 'invalidsha1hash', 'info' => 'The SHA1 hash provided is not valid' ),
array( 'code' => 'invalidsha1base36hash', 'info' => 'The SHA1Base36 hash provided is not valid' ),
array( 'code' => '_badcontinue', 'info' => 'Invalid continue param. You should pass the original value returned by the previous query' ),
) );
}

View file

@ -4,7 +4,7 @@
*
* Created on July 7, 2007
*
* Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
* Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -77,16 +77,25 @@ class ApiQueryAllLinks extends ApiQueryGeneratorBase {
}
if ( !is_null( $params['continue'] ) ) {
$continueArr = explode( '|', $params['continue'] );
if ( count( $continueArr ) != 2 ) {
$this->dieUsage( 'Invalid continue parameter', 'badcontinue' );
$op = $params['dir'] == 'descending' ? '<' : '>';
if ( $params['unique'] ) {
if ( count( $continueArr ) != 1 ) {
$this->dieUsage( 'Invalid continue parameter', 'badcontinue' );
}
$continueTitle = $db->addQuotes( $continueArr[0] );
$this->addWhere( "pl_title $op= $continueTitle" );
} else {
if ( count( $continueArr ) != 2 ) {
$this->dieUsage( 'Invalid continue parameter', 'badcontinue' );
}
$continueTitle = $db->addQuotes( $continueArr[0] );
$continueFrom = intval( $continueArr[1] );
$this->addWhere(
"pl_title $op $continueTitle OR " .
"(pl_title = $continueTitle AND " .
"pl_from $op= $continueFrom)"
);
}
$continueTitle = $db->addQuotes( $this->titleToKey( $continueArr[0] ) );
$continueFrom = intval( $continueArr[1] );
$this->addWhere(
"pl_title > $continueTitle OR " .
"(pl_title = $continueTitle AND " .
"pl_from > $continueFrom)"
);
}
$from = ( is_null( $params['from'] ) ? null : $this->titlePartToKey( $params['from'] ) );
@ -104,12 +113,13 @@ class ApiQueryAllLinks extends ApiQueryGeneratorBase {
$limit = $params['limit'];
$this->addOption( 'LIMIT', $limit + 1 );
$sort = ( $params['dir'] == 'descending' ? ' DESC' : '' );
$orderBy = array();
$orderBy[] = 'pl_title' . $sort;
if ( !$params['unique'] ) {
$this->addOption( 'ORDER BY', array(
'pl_title',
'pl_from'
));
$orderBy[] = 'pl_from' . $sort;
}
$this->addOption( 'ORDER BY', $orderBy );
$res = $this->select( __METHOD__ );
@ -119,11 +129,10 @@ class ApiQueryAllLinks extends ApiQueryGeneratorBase {
foreach ( $res as $row ) {
if ( ++ $count > $limit ) {
// We've reached the one extra which shows that there are additional pages to be had. Stop here...
// TODO: Security issue - if the user has no right to view next title, it will still be shown
if ( $params['unique'] ) {
$this->setContinueEnumParameter( 'from', $this->keyToTitle( $row->pl_title ) );
$this->setContinueEnumParameter( 'continue', $row->pl_title );
} else {
$this->setContinueEnumParameter( 'continue', $this->keyToTitle( $row->pl_title ) . "|" . $row->pl_from );
$this->setContinueEnumParameter( 'continue', $row->pl_title . "|" . $row->pl_from );
}
break;
}
@ -140,9 +149,9 @@ class ApiQueryAllLinks extends ApiQueryGeneratorBase {
$fit = $result->addValue( array( 'query', $this->getModuleName() ), null, $vals );
if ( !$fit ) {
if ( $params['unique'] ) {
$this->setContinueEnumParameter( 'from', $this->keyToTitle( $row->pl_title ) );
$this->setContinueEnumParameter( 'continue', $row->pl_title );
} else {
$this->setContinueEnumParameter( 'continue', $this->keyToTitle( $row->pl_title ) . "|" . $row->pl_from );
$this->setContinueEnumParameter( 'continue', $row->pl_title . "|" . $row->pl_from );
}
break;
}
@ -183,7 +192,14 @@ class ApiQueryAllLinks extends ApiQueryGeneratorBase {
ApiBase::PARAM_MIN => 1,
ApiBase::PARAM_MAX => ApiBase::LIMIT_BIG1,
ApiBase::PARAM_MAX2 => ApiBase::LIMIT_BIG2
)
),
'dir' => array(
ApiBase::PARAM_DFLT => 'ascending',
ApiBase::PARAM_TYPE => array(
'ascending',
'descending'
)
),
);
}
@ -202,6 +218,7 @@ class ApiQueryAllLinks extends ApiQueryGeneratorBase {
'namespace' => 'The namespace to enumerate',
'limit' => 'How many total links to return',
'continue' => 'When more results are available, use this to continue',
'dir' => 'The direction in which to list',
);
}

View file

@ -4,7 +4,7 @@
*
* Created on Dec 1, 2007
*
* Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
* Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View file

@ -4,7 +4,7 @@
*
* Created on Sep 25, 2006
*
* Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
* Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -67,6 +67,17 @@ class ApiQueryAllPages extends ApiQueryGeneratorBase {
// Page filters
$this->addTables( 'page' );
if ( !is_null( $params['continue'] ) ) {
$cont = explode( '|', $params['continue'] );
if ( count( $cont ) != 1 ) {
$this->dieUsage( "Invalid continue param. You should pass the " .
"original value returned by the previous query", "_badcontinue" );
}
$op = $params['dir'] == 'descending' ? '<' : '>';
$cont_from = $db->addQuotes( $cont[0] );
$this->addWhere( "page_title $op= $cont_from" );
}
if ( $params['filterredir'] == 'redirects' ) {
$this->addWhereFld( 'page_is_redirect', 1 );
} elseif ( $params['filterredir'] == 'nonredirects' ) {
@ -165,13 +176,22 @@ class ApiQueryAllPages extends ApiQueryGeneratorBase {
$this->addOption( 'LIMIT', $limit + 1 );
$res = $this->select( __METHOD__ );
//Get gender information
if( MWNamespace::hasGenderDistinction( $params['namespace'] ) ) {
$users = array();
foreach ( $res as $row ) {
$users[] = $row->page_title;
}
GenderCache::singleton()->doQuery( $users, __METHOD__ );
$res->rewind(); //reset
}
$count = 0;
$result = $this->getResult();
foreach ( $res as $row ) {
if ( ++ $count > $limit ) {
// We've reached the one extra which shows that there are additional pages to be had. Stop here...
// TODO: Security issue - if the user has no right to view next title, it will still be shown
$this->setContinueEnumParameter( 'from', $this->keyToTitle( $row->page_title ) );
$this->setContinueEnumParameter( 'continue', $row->page_title );
break;
}
@ -184,7 +204,7 @@ class ApiQueryAllPages extends ApiQueryGeneratorBase {
);
$fit = $result->addValue( array( 'query', $this->getModuleName() ), null, $vals );
if ( !$fit ) {
$this->setContinueEnumParameter( 'from', $this->keyToTitle( $row->page_title ) );
$this->setContinueEnumParameter( 'continue', $row->page_title );
break;
}
} else {
@ -202,6 +222,7 @@ class ApiQueryAllPages extends ApiQueryGeneratorBase {
return array(
'from' => null,
'continue' => null,
'to' => null,
'prefix' => null,
'namespace' => array(
@ -275,6 +296,7 @@ class ApiQueryAllPages extends ApiQueryGeneratorBase {
$p = $this->getModulePrefix();
return array(
'from' => 'The page title to start enumerating from',
'continue' => 'When more results are available, use this to continue',
'to' => 'The page title to stop enumerating at',
'prefix' => 'Search for all page titles that begin with this value',
'namespace' => 'The namespace to enumerate',
@ -314,6 +336,7 @@ class ApiQueryAllPages extends ApiQueryGeneratorBase {
return array_merge( parent::getPossibleErrors(), array(
array( 'code' => 'params', 'info' => 'Use "gapfilterredir=nonredirects" option instead of "redirects" when using allpages as a generator' ),
array( 'code' => 'params', 'info' => 'prlevel may not be used without prtype' ),
array( 'code' => '_badcontinue', 'info' => 'Invalid continue param. You should pass the original value returned by the previous query' ),
) );
}

View file

@ -4,7 +4,7 @@
*
* Created on July 7, 2007
*
* Copyright © 2007 Yuri Astrakhan <Firstname><Lastname>@gmail.com
* Copyright © 2007 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View file

@ -4,7 +4,7 @@
*
* Created on Oct 16, 2006
*
* Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
* Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -40,7 +40,7 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase {
private $rootTitle;
private $params, $contID, $redirID, $redirect;
private $bl_ns, $bl_from, $bl_table, $bl_code, $bl_title, $bl_sort, $bl_fields, $hasNS;
private $bl_ns, $bl_from, $bl_table, $bl_code, $bl_title, $bl_fields, $hasNS;
/**
* Maps ns and title to pageid
@ -91,14 +91,12 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase {
$this->hasNS = $moduleName !== 'imageusage';
if ( $this->hasNS ) {
$this->bl_title = $prefix . '_title';
$this->bl_sort = "{$this->bl_ns}, {$this->bl_title}, {$this->bl_from}";
$this->bl_fields = array(
$this->bl_ns,
$this->bl_title
);
} else {
$this->bl_title = $prefix . '_to';
$this->bl_sort = "{$this->bl_title}, {$this->bl_from}";
$this->bl_fields = array(
$this->bl_title
);
@ -144,7 +142,8 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase {
$this->addWhereFld( 'page_namespace', $this->params['namespace'] );
if ( !is_null( $this->contID ) ) {
$this->addWhere( "{$this->bl_from}>={$this->contID}" );
$op = $this->params['dir'] == 'descending' ? '<' : '>';
$this->addWhere( "{$this->bl_from}$op={$this->contID}" );
}
if ( $this->params['filterredir'] == 'redirects' ) {
@ -155,7 +154,8 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase {
}
$this->addOption( 'LIMIT', $this->params['limit'] + 1 );
$this->addOption( 'ORDER BY', $this->bl_from );
$sort = ( $this->params['dir'] == 'descending' ? ' DESC' : '' );
$this->addOption( 'ORDER BY', $this->bl_from . $sort );
$this->addOption( 'STRAIGHT_JOIN' );
}
@ -186,28 +186,35 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase {
// We can't use LinkBatch here because $this->hasNS may be false
$titleWhere = array();
$allRedirNs = array();
$allRedirDBkey = array();
foreach ( $this->redirTitles as $t ) {
$titleWhere[] = "{$this->bl_title} = " . $db->addQuotes( $t->getDBkey() ) .
( $this->hasNS ? " AND {$this->bl_ns} = {$t->getNamespace()}" : '' );
$redirNs = $t->getNamespace();
$redirDBkey = $t->getDBkey();
$titleWhere[] = "{$this->bl_title} = " . $db->addQuotes( $redirDBkey ) .
( $this->hasNS ? " AND {$this->bl_ns} = {$redirNs}" : '' );
$allRedirNs[] = $redirNs;
$allRedirDBkey[] = $redirDBkey;
}
$this->addWhere( $db->makeList( $titleWhere, LIST_OR ) );
$this->addWhereFld( 'page_namespace', $this->params['namespace'] );
if ( !is_null( $this->redirID ) ) {
$op = $this->params['dir'] == 'descending' ? '<' : '>';
$first = $this->redirTitles[0];
$title = $db->addQuotes( $first->getDBkey() );
$ns = $first->getNamespace();
$from = $this->redirID;
if ( $this->hasNS ) {
$this->addWhere( "{$this->bl_ns} > $ns OR " .
$this->addWhere( "{$this->bl_ns} $op $ns OR " .
"({$this->bl_ns} = $ns AND " .
"({$this->bl_title} > $title OR " .
"({$this->bl_title} $op $title OR " .
"({$this->bl_title} = $title AND " .
"{$this->bl_from} >= $from)))" );
"{$this->bl_from} $op= $from)))" );
} else {
$this->addWhere( "{$this->bl_title} > $title OR " .
$this->addWhere( "{$this->bl_title} $op $title OR " .
"({$this->bl_title} = $title AND " .
"{$this->bl_from} >= $from)" );
"{$this->bl_from} $op= $from)" );
}
}
if ( $this->params['filterredir'] == 'redirects' ) {
@ -217,7 +224,17 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase {
}
$this->addOption( 'LIMIT', $this->params['limit'] + 1 );
$this->addOption( 'ORDER BY', $this->bl_sort );
$orderBy = array();
$sort = ( $this->params['dir'] == 'descending' ? ' DESC' : '' );
// Don't order by namespace/title if it's constant in the WHERE clause
if( $this->hasNS && count( array_unique( $allRedirNs ) ) != 1 ) {
$orderBy[] = $this->bl_ns . $sort;
}
if( count( array_unique( $allRedirDBkey ) ) != 1 ) {
$orderBy[] = $this->bl_title . $sort;
}
$orderBy[] = $this->bl_from . $sort;
$this->addOption( 'ORDER BY', $orderBy );
$this->addOption( 'USE INDEX', array( 'page' => 'PRIMARY' ) );
}
@ -277,7 +294,7 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase {
if ( $this->hasNS ) {
$parentID = $this->pageMap[$row-> { $this->bl_ns } ][$row-> { $this->bl_title } ];
} else {
$parentID = $this->pageMap[NS_IMAGE][$row-> { $this->bl_title } ];
$parentID = $this->pageMap[NS_FILE][$row-> { $this->bl_title } ];
}
$this->continueStr = $this->getContinueRedirStr( $parentID, $row->page_id );
break;
@ -438,6 +455,13 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase {
ApiBase::PARAM_ISMULTI => true,
ApiBase::PARAM_TYPE => 'namespace'
),
'dir' => array(
ApiBase::PARAM_DFLT => 'ascending',
ApiBase::PARAM_TYPE => array(
'ascending',
'descending'
)
),
'filterredir' => array(
ApiBase::PARAM_DFLT => 'all',
ApiBase::PARAM_TYPE => array(
@ -467,6 +491,7 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase {
'pageid' => "Pageid to search. Cannot be used together with {$this->bl_code}title",
'continue' => 'When more results are available, use this to continue',
'namespace' => 'The namespace to enumerate',
'dir' => 'The direction in which to list',
);
if ( $this->getModuleName() != 'embeddedin' ) {
return array_merge( $retval, array(

Some files were not shown because too many files have changed in this diff Show more