
guyvdb at svn
Aug 17, 2008, 5:10 PM
Post #1 of 1
(66 views)
Permalink
|
|
SVN: [39563] branches/visual_diff/phase3
|
|
Revision: 39563 Author: guyvdb Date: 2008-08-18 00:10:23 +0000 (Mon, 18 Aug 2008) Log Message: ----------- Merge with trunk Modified Paths: -------------- branches/visual_diff/phase3/RELEASE-NOTES branches/visual_diff/phase3/includes/Article.php branches/visual_diff/phase3/includes/AutoLoader.php branches/visual_diff/phase3/includes/ChangesList.php branches/visual_diff/phase3/includes/Credits.php branches/visual_diff/phase3/includes/DefaultSettings.php branches/visual_diff/phase3/includes/Diff.php branches/visual_diff/phase3/includes/DifferenceEngine.php branches/visual_diff/phase3/includes/LinkCache.php branches/visual_diff/phase3/includes/LinksUpdate.php branches/visual_diff/phase3/includes/MessageCache.php branches/visual_diff/phase3/includes/StringUtils.php branches/visual_diff/phase3/includes/Title.php branches/visual_diff/phase3/includes/ZhConversion.php branches/visual_diff/phase3/includes/api/ApiLogin.php branches/visual_diff/phase3/includes/api/ApiQuery.php branches/visual_diff/phase3/includes/api/ApiQueryCategories.php branches/visual_diff/phase3/includes/api/ApiQueryLinks.php branches/visual_diff/phase3/includes/api/ApiQuerySearch.php branches/visual_diff/phase3/includes/parser/CoreParserFunctions.php branches/visual_diff/phase3/includes/parser/Parser.php branches/visual_diff/phase3/includes/parser/Parser_DiffTest.php branches/visual_diff/phase3/includes/specials/SpecialAllpages.php branches/visual_diff/phase3/includes/specials/SpecialLog.php branches/visual_diff/phase3/includes/specials/SpecialPreferences.php branches/visual_diff/phase3/includes/specials/SpecialPrefixindex.php branches/visual_diff/phase3/includes/specials/SpecialRecentchanges.php branches/visual_diff/phase3/includes/specials/SpecialUserlogin.php branches/visual_diff/phase3/includes/specials/SpecialWatchlist.php branches/visual_diff/phase3/includes/templates/Userlogin.php branches/visual_diff/phase3/includes/zhtable/toCN.manual branches/visual_diff/phase3/includes/zhtable/toHK.manual branches/visual_diff/phase3/includes/zhtable/toSG.manual branches/visual_diff/phase3/includes/zhtable/toTW.manual branches/visual_diff/phase3/includes/zhtable/tradphrases.manual branches/visual_diff/phase3/languages/LanguageConverter.php branches/visual_diff/phase3/languages/messages/MessagesAf.php branches/visual_diff/phase3/languages/messages/MessagesAr.php branches/visual_diff/phase3/languages/messages/MessagesArn.php branches/visual_diff/phase3/languages/messages/MessagesArz.php branches/visual_diff/phase3/languages/messages/MessagesAst.php branches/visual_diff/phase3/languages/messages/MessagesBcc.php branches/visual_diff/phase3/languages/messages/MessagesBe_tarask.php branches/visual_diff/phase3/languages/messages/MessagesBg.php branches/visual_diff/phase3/languages/messages/MessagesBs.php branches/visual_diff/phase3/languages/messages/MessagesCa.php branches/visual_diff/phase3/languages/messages/MessagesCs.php branches/visual_diff/phase3/languages/messages/MessagesCu.php branches/visual_diff/phase3/languages/messages/MessagesCy.php branches/visual_diff/phase3/languages/messages/MessagesDa.php branches/visual_diff/phase3/languages/messages/MessagesDe.php branches/visual_diff/phase3/languages/messages/MessagesEn.php branches/visual_diff/phase3/languages/messages/MessagesEs.php branches/visual_diff/phase3/languages/messages/MessagesFa.php branches/visual_diff/phase3/languages/messages/MessagesFi.php branches/visual_diff/phase3/languages/messages/MessagesFr.php branches/visual_diff/phase3/languages/messages/MessagesFur.php branches/visual_diff/phase3/languages/messages/MessagesFy.php branches/visual_diff/phase3/languages/messages/MessagesGrc.php branches/visual_diff/phase3/languages/messages/MessagesHe.php branches/visual_diff/phase3/languages/messages/MessagesHif_latn.php branches/visual_diff/phase3/languages/messages/MessagesHr.php branches/visual_diff/phase3/languages/messages/MessagesHu.php branches/visual_diff/phase3/languages/messages/MessagesIa.php branches/visual_diff/phase3/languages/messages/MessagesId.php branches/visual_diff/phase3/languages/messages/MessagesIt.php branches/visual_diff/phase3/languages/messages/MessagesJa.php branches/visual_diff/phase3/languages/messages/MessagesKm.php branches/visual_diff/phase3/languages/messages/MessagesKo.php branches/visual_diff/phase3/languages/messages/MessagesKrj.php branches/visual_diff/phase3/languages/messages/MessagesKsh.php branches/visual_diff/phase3/languages/messages/MessagesLa.php branches/visual_diff/phase3/languages/messages/MessagesLv.php branches/visual_diff/phase3/languages/messages/MessagesMk.php branches/visual_diff/phase3/languages/messages/MessagesMs.php branches/visual_diff/phase3/languages/messages/MessagesMt.php branches/visual_diff/phase3/languages/messages/MessagesNds.php branches/visual_diff/phase3/languages/messages/MessagesNl.php branches/visual_diff/phase3/languages/messages/MessagesNn.php branches/visual_diff/phase3/languages/messages/MessagesNo.php branches/visual_diff/phase3/languages/messages/MessagesOc.php branches/visual_diff/phase3/languages/messages/MessagesPa.php branches/visual_diff/phase3/languages/messages/MessagesPl.php branches/visual_diff/phase3/languages/messages/MessagesPt.php branches/visual_diff/phase3/languages/messages/MessagesQqq.php branches/visual_diff/phase3/languages/messages/MessagesRif.php branches/visual_diff/phase3/languages/messages/MessagesRu.php branches/visual_diff/phase3/languages/messages/MessagesSv.php branches/visual_diff/phase3/languages/messages/MessagesTk.php branches/visual_diff/phase3/languages/messages/MessagesUk.php branches/visual_diff/phase3/languages/messages/MessagesVec.php branches/visual_diff/phase3/languages/messages/MessagesVi.php branches/visual_diff/phase3/languages/messages/MessagesYue.php branches/visual_diff/phase3/languages/messages/MessagesZh_classical.php branches/visual_diff/phase3/languages/messages/MessagesZh_hans.php branches/visual_diff/phase3/languages/messages/MessagesZh_hant.php branches/visual_diff/phase3/languages/messages/MessagesZh_tw.php branches/visual_diff/phase3/maintenance/language/messages.inc branches/visual_diff/phase3/maintenance/parserTests.inc branches/visual_diff/phase3/maintenance/parserTests.txt branches/visual_diff/phase3/skins/common/shared.css Added Paths: ----------- branches/visual_diff/phase3/includes/parser/LinkHolderArray.php Property Changed: ---------------- branches/visual_diff/phase3/skins/common/images/diffunderline.gif Modified: branches/visual_diff/phase3/RELEASE-NOTES =================================================================== --- branches/visual_diff/phase3/RELEASE-NOTES 2008-08-17 23:32:07 UTC (rev 39562) +++ branches/visual_diff/phase3/RELEASE-NOTES 2008-08-18 00:10:23 UTC (rev 39563) @@ -19,7 +19,6 @@ it from source control: http://www.mediawiki.org/wiki/Download_from_SVN === Configuration changes in 1.14 === - * $wgExemptFromUserRobotsControl is an array of namespaces to be exempt from the effect of the new __INDEX__/__NOINDEX__ magic words. (Default: null, ex- empt all content namespaces.) @@ -37,15 +36,18 @@ Backwards compatibility is maintained. * $wgEnablePersistentCookies has been added. Setting to false disables the setting of persistent cookies. Defaults to true. - +* $wgRestrictDisplayTitle controls if the use of the {{DISPLAYTITLE}} magic + word is restricted to titles equivalent to the actual page title. This + is true per default, but can be set to false to allow any title. + === New features in 1.14 === -* New URL syntaxes for Special:ListUsers - 'Special:ListUsers/USER' and - 'Special:ListUsers/GROUP/USER', in addition to the older syntax +* New URL syntaxes for Special:ListUsers - 'Special:ListUsers/USER' and + 'Special:ListUsers/GROUP/USER', in addition to the older syntax 'Special:ListUsers/GROUP' where GROUP is a valid group name. -* Configurable er-namespace and per-page notices for the edit form, +* Configurable er-namespace and per-page notices for the edit form, respectively MediaWiki:Editnotice-# where # is the namespace number, and - MediaWiki:Editnotice-#-PAGENAME where # is the page's namespace number and + MediaWiki:Editnotice-#-PAGENAME where # is the page's namespace number and PAGENAME is the page name minus the namespace prefix. * (bug 8068) New __INDEX__ and __NOINDEX__ magic words allow user control of search engine indexing on a per-article basis. @@ -80,7 +82,7 @@ * (bug 14929) removeUnusedAccounts.php now supports 'ignore-touched' and 'ignore-groups'. Patch by Louperivois * (bug 15127) Work around minor display glitch in Opera. -* By default, reject file uploads that look like ZIP files, to avoid the +* By default, reject file uploads that look like ZIP files, to avoid the so-called GIFAR vulnerability. * (bug 15141) Give ability to only list protected pages with the cascading option enabled on Special:ProtectedPages @@ -88,7 +90,11 @@ Show/Hide logged in users, Show/Hide anonymous, Invert namespace selection * Added hook 'UserrightsChangeableGroups' to allow modification of what groups may be added or removed via the Special:UserRights interface. - +* (bug 14468) Lines in classic RecentChanges and Watchlist have alternating + background colours based on classes "odd" and "even". +* (bug 14187) In Special:Userlogin the buttons "Log in" and "E-mail new + password" now have classes "mw-loginbutton" and "mw-mailmypasswordbutton". + === Bug fixes in 1.14 === * (bug 14907) DatabasePostgres::fieldType now defined. @@ -130,6 +136,7 @@ gives results * Avoid recursive crazy expansions in section edit comments for pages which contain '/*' in the title +* Fix excessive memory usage when parsing pages with lots of links === API changes in 1.14 === @@ -147,6 +154,9 @@ * (bug 15048) Added limit field for multivalue parameters to action=paraminfo output. * When the limit on multivalue parameters is exceeded, a warning is issued +* list=search doesn't list missing pages any more +* (bug 15178) Added clshow to prop=categories to allow filtering for hidden/ + non-hidden categories === Languages updated in 1.14 === Modified: branches/visual_diff/phase3/includes/Article.php =================================================================== --- branches/visual_diff/phase3/includes/Article.php 2008-08-17 23:32:07 UTC (rev 39562) +++ branches/visual_diff/phase3/includes/Article.php 2008-08-18 00:10:23 UTC (rev 39563) @@ -51,6 +51,16 @@ $this->mTitle =& $title; $this->mOldId = $oldId; } + + /** + * Constructor from an article article + * @param $id The article ID to load + */ + public static function newFromID( $id ) { + $t = Title::newFromID( $id ); + + return $t == null ? null : new Article( $t ); + } /** * Tell the page view functions that this view was redirected @@ -1390,8 +1400,12 @@ * * @return bool success */ - function doEdit( $text, $summary, $flags = 0, $baseRevId = false ) { + function doEdit( $text, $summary, $flags = 0, $baseRevId = false, $user = null ) { global $wgUser, $wgDBtransactions, $wgUseAutomaticEditSummaries; + + if ($user == null) { + $user = $wgUser; + } wfProfileIn( __METHOD__ ); $good = true; @@ -1405,7 +1419,7 @@ } } - if( !wfRunHooks( 'ArticleSave', array( &$this, &$wgUser, &$text, + if( !wfRunHooks( 'ArticleSave', array( &$this, &$user, &$text, &$summary, $flags & EDIT_MINOR, null, null, &$flags ) ) ) { @@ -1415,7 +1429,7 @@ } # Silently ignore EDIT_MINOR if not allowed - $isminor = ( $flags & EDIT_MINOR ) && $wgUser->isAllowed('minoredit'); + $isminor = ( $flags & EDIT_MINOR ) && $user->isAllowed('minoredit'); $bot = $flags & EDIT_FORCE_BOT; $oldtext = $this->getContent(); @@ -1484,17 +1498,17 @@ # Update recentchanges if( !( $flags & EDIT_SUPPRESS_RC ) ) { - $rcid = RecentChange::notifyEdit( $now, $this->mTitle, $isminor, $wgUser, $summary, + $rcid = RecentChange::notifyEdit( $now, $this->mTitle, $isminor, $user, $summary, $lastRevision, $this->getTimestamp(), $bot, '', $oldsize, $newsize, $revisionId ); # Mark as patrolled if the user can do so - if( $GLOBALS['wgUseRCPatrol'] && $wgUser->isAllowed( 'autopatrol' ) ) { + if( $GLOBALS['wgUseRCPatrol'] && $user->isAllowed( 'autopatrol' ) ) { RecentChange::markPatrolled( $rcid ); PatrolLog::record( $rcid, true ); } } - $wgUser->incEditCount(); + $user->incEditCount(); $dbw->commit(); } } else { @@ -1550,15 +1564,15 @@ wfRunHooks( 'NewRevisionFromEditComplete', array($this, $revision, false) ); if( !( $flags & EDIT_SUPPRESS_RC ) ) { - $rcid = RecentChange::notifyNew( $now, $this->mTitle, $isminor, $wgUser, $summary, $bot, + $rcid = RecentChange::notifyNew( $now, $this->mTitle, $isminor, $user, $summary, $bot, '', strlen( $text ), $revisionId ); # Mark as patrolled if the user can - if( ($GLOBALS['wgUseRCPatrol'] || $GLOBALS['wgUseNPPatrol']) && $wgUser->isAllowed( 'autopatrol' ) ) { + if( ($GLOBALS['wgUseRCPatrol'] || $GLOBALS['wgUseNPPatrol']) && $user->isAllowed( 'autopatrol' ) ) { RecentChange::markPatrolled( $rcid ); PatrolLog::record( $rcid, true ); } } - $wgUser->incEditCount(); + $user->incEditCount(); $dbw->commit(); # Update links, etc. @@ -1567,7 +1581,7 @@ # Clear caches Article::onArticleCreate( $this->mTitle ); - wfRunHooks( 'ArticleInsertComplete', array( &$this, &$wgUser, $text, $summary, + wfRunHooks( 'ArticleInsertComplete', array( &$this, &$user, $text, $summary, $flags & EDIT_MINOR, null, null, &$flags, $revision ) ); } @@ -1576,7 +1590,7 @@ } if ( $good ) { - wfRunHooks( 'ArticleSaveComplete', array( &$this, &$wgUser, $text, $summary, + wfRunHooks( 'ArticleSaveComplete', array( &$this, &$user, $text, $summary, $flags & EDIT_MINOR, null, null, &$flags, $revision ) ); } Modified: branches/visual_diff/phase3/includes/AutoLoader.php =================================================================== --- branches/visual_diff/phase3/includes/AutoLoader.php 2008-08-17 23:32:07 UTC (rev 39562) +++ branches/visual_diff/phase3/includes/AutoLoader.php 2008-08-18 00:10:23 UTC (rev 39563) @@ -32,6 +32,7 @@ 'ConcatenatedGzipHistoryBlob' => 'includes/HistoryBlob.php', 'ConstantDependency' => 'includes/CacheDependency.php', 'DBABagOStuff' => 'includes/BagOStuff.php', + 'DelegatingContentHandler' => 'includes/HTMLDiff.php', 'DependencyWrapper' => 'includes/CacheDependency.php', '_DiffEngine' => 'includes/DifferenceEngine.php', 'DifferenceEngine' => 'includes/DifferenceEngine.php', @@ -57,6 +58,7 @@ 'DumpOutput' => 'includes/Export.php', 'DumpPipeOutput' => 'includes/Export.php', 'eAccelBagOStuff' => 'includes/BagOStuff.php', + 'EchoingContentHandler' => 'includes/HTMLDiff.php', 'EditPage' => 'includes/EditPage.php', 'EmaillingJob' => 'includes/EmaillingJob.php', 'EmailNotification' => 'includes/UserMailer.php', @@ -64,6 +66,7 @@ 'EnotifNotifyJob' => 'includes/EnotifNotifyJob.php', 'ErrorPageError' => 'includes/Exception.php', 'Exif' => 'includes/Exif.php', + 'ExplodeIterator' => 'includes/StringUtils.php', 'ExternalEdit' => 'includes/ExternalEdit.php', 'ExternalStoreDB' => 'includes/ExternalStoreDB.php', 'ExternalStoreHttp' => 'includes/ExternalStoreHttp.php', @@ -89,7 +92,9 @@ 'HistoryBlobStub' => 'includes/HistoryBlob.php', 'HTMLCacheUpdate' => 'includes/HTMLCacheUpdate.php', 'HTMLCacheUpdateJob' => 'includes/HTMLCacheUpdate.php', + 'HTMLDiffer' => 'includes/HTMLDiff.php', 'HTMLFileCache' => 'includes/HTMLFileCache.php', + 'HTMLOutput' => 'includes/HTMLDiff.php', 'Http' => 'includes/HttpFunctions.php', '_HWLDF_WordAccumulator' => 'includes/DifferenceEngine.php', 'ImageGallery' => 'includes/ImageGallery.php', @@ -149,6 +154,7 @@ 'ProtectionForm' => 'includes/ProtectionForm.php', 'QueryPage' => 'includes/QueryPage.php', 'QuickTemplate' => 'includes/SkinTemplate.php', + 'RangeDifference' => 'includes/Diff.php', 'RawPage' => 'includes/RawPage.php', 'RCCacheEntry' => 'includes/ChangesList.php', 'RecentChange' => 'includes/RecentChange.php', @@ -206,6 +212,7 @@ 'WatchlistEditor' => 'includes/WatchlistEditor.php', 'WebRequest' => 'includes/WebRequest.php', 'WebResponse' => 'includes/WebResponse.php', + 'WikiDiff3' => 'includes/Diff.php', 'WikiError' => 'includes/WikiError.php', 'WikiErrorMsg' => 'includes/WikiError.php', 'WikiExporter' => 'includes/Export.php', @@ -351,6 +358,7 @@ # includes/parser 'CoreParserFunctions' => 'includes/parser/CoreParserFunctions.php', 'DateFormatter' => 'includes/parser/DateFormatter.php', + 'LinkHolderArray' => 'includes/parser/LinkHolderArray.php', 'OnlyIncludeReplacer' => 'includes/parser/Parser.php', 'PPDAccum_Hash' => 'includes/parser/Preprocessor_Hash.php', 'PPDPart' => 'includes/parser/Preprocessor_DOM.php', Modified: branches/visual_diff/phase3/includes/ChangesList.php =================================================================== --- branches/visual_diff/phase3/includes/ChangesList.php 2008-08-17 23:32:07 UTC (rev 39562) +++ branches/visual_diff/phase3/includes/ChangesList.php 2008-08-18 00:10:23 UTC (rev 39563) @@ -306,7 +306,7 @@ /** * Format a line using the old system (aka without any javascript). */ - public function recentChangesLine( &$rc, $watched = false ) { + public function recentChangesLine( &$rc, $watched = false, $linenumber = NULL ) { global $wgContLang, $wgRCShowChangedSize, $wgUser; $fname = 'ChangesList::recentChangesLineOld'; @@ -321,7 +321,17 @@ $this->insertDateHeader($s,$rc_timestamp); - $s .= '<li>'; + // use even/odd class only if linenumber is given (feature from bug 14468) + if( $linenumber ) { + if( $linenumber & 1 ) { + $s .= '<li class="odd">'; + } + else { + $s .= '<li class="even">'; + } + } else { + $s .= '<li>'; + } // Moved pages if( $rc_type == RC_MOVE || $rc_type == RC_MOVE_OVER_REDIRECT ) { Modified: branches/visual_diff/phase3/includes/Credits.php =================================================================== --- branches/visual_diff/phase3/includes/Credits.php 2008-08-17 23:32:07 UTC (rev 39562) +++ branches/visual_diff/phase3/includes/Credits.php 2008-08-18 00:10:23 UTC (rev 39563) @@ -144,7 +144,7 @@ # "ThisSite user(s) A, B and C" if (!empty($user)) { - $user = wfMsg('siteusers', $user); + $user = wfMsgExt('siteusers', array( 'parsemag' ), array( $user, count($contributors) ) ); } # This is the big list, all mooshed together. We sift for blank strings Modified: branches/visual_diff/phase3/includes/DefaultSettings.php =================================================================== --- branches/visual_diff/phase3/includes/DefaultSettings.php 2008-08-17 23:32:07 UTC (rev 39562) +++ branches/visual_diff/phase3/includes/DefaultSettings.php 2008-08-18 00:10:23 UTC (rev 39563) @@ -1382,7 +1382,7 @@ * to ensure that client-side caches don't keep obsolete copies of global * styles. */ -$wgStyleVersion = '165'; +$wgStyleVersion = '166'; # Server-side caching: @@ -2377,6 +2377,9 @@ /** Name of the external diff engine to use */ $wgExternalDiffEngine = false; +/** Whether to use inline diff */ +$wgEnableHtmlDiff = false; + /** Use RC Patrolling to check for vandalism */ $wgUseRCPatrol = true; @@ -3154,6 +3157,11 @@ $wgAllowDisplayTitle = true; /** + * for consistency, restrict DISPLAYTITLE to titles that normalize to the same canonical DB key + */ +$wgRestrictDisplayTitle = true; + +/** * Array of usernames which may not be registered or logged in from * Maintenance scripts can still use these */ Modified: branches/visual_diff/phase3/includes/Diff.php =================================================================== --- branches/visual_diff/phase3/includes/Diff.php 2008-08-17 23:32:07 UTC (rev 39562) +++ branches/visual_diff/phase3/includes/Diff.php 2008-08-18 00:10:23 UTC (rev 39563) @@ -566,4 +566,3 @@ $this->rightlength = $rightend-$rightstart; } } - Modified: branches/visual_diff/phase3/includes/DifferenceEngine.php =================================================================== --- branches/visual_diff/phase3/includes/DifferenceEngine.php 2008-08-17 23:32:07 UTC (rev 39562) +++ branches/visual_diff/phase3/includes/DifferenceEngine.php 2008-08-18 00:10:23 UTC (rev 39563) @@ -327,7 +327,7 @@ function renderHtmlDiff() { - global $wgOut, $IP; + global $wgOut; wfProfileIn( __METHOD__ ); $this->showDiffStyle(); @@ -378,7 +378,6 @@ unset($parserOutput,$popts); - require_once( "$IP/includes/HTMLDiff.php" ); $differ = new HTMLDiffer(new DelegatingContentHandler($wgOut)); $differ->htmlDiff($oldHtml, $newHtml); @@ -1042,8 +1041,6 @@ if($wgExternalDiffEngine == 'wikidiff3'){ // wikidiff3 - global $IP; - require_once( "$IP/includes/Diff.php" ); $wikidiff3 = new WikiDiff3(); $wikidiff3->diff($from_lines, $to_lines); $this->xchanged = $wikidiff3->removed; @@ -2089,4 +2086,4 @@ } wfProfileOut( __METHOD__ ); } -} +} \ No newline at end of file Modified: branches/visual_diff/phase3/includes/LinkCache.php =================================================================== --- branches/visual_diff/phase3/includes/LinkCache.php 2008-08-17 23:32:07 UTC (rev 39562) +++ branches/visual_diff/phase3/includes/LinkCache.php 2008-08-18 00:10:23 UTC (rev 39563) @@ -9,7 +9,6 @@ // becomes incompatible with the new version. /* private */ var $mClassVer = 4; - /* private */ var $mPageLinks; /* private */ var $mGoodLinks, $mBadLinks; /* private */ var $mForUpdate; @@ -26,7 +25,6 @@ function __construct() { $this->mForUpdate = false; - $this->mPageLinks = array(); $this->mGoodLinks = array(); $this->mGoodLinkFields = array(); $this->mBadLinks = array(); @@ -78,14 +76,12 @@ $dbkey = $title->getPrefixedDbKey(); $this->mGoodLinks[$dbkey] = $id; $this->mGoodLinkFields[$dbkey] = array( 'length' => $len, 'redirect' => $redir ); - $this->mPageLinks[$dbkey] = $title; } public function addBadLinkObj( $title ) { $dbkey = $title->getPrefixedDbKey(); if ( ! $this->isBadLink( $dbkey ) ) { $this->mBadLinks[$dbkey] = 1; - $this->mPageLinks[$dbkey] = $title; } } @@ -96,7 +92,6 @@ /* obsolete, for old $wgLinkCacheMemcached stuff */ public function clearLink( $title ) {} - public function getPageLinks() { return $this->mPageLinks; } public function getGoodLinks() { return $this->mGoodLinks; } public function getBadLinks() { return array_keys( $this->mBadLinks ); } @@ -181,7 +176,6 @@ * Clears cache */ public function clear() { - $this->mPageLinks = array(); $this->mGoodLinks = array(); $this->mGoodLinkFields = array(); $this->mBadLinks = array(); Modified: branches/visual_diff/phase3/includes/LinksUpdate.php =================================================================== --- branches/visual_diff/phase3/includes/LinksUpdate.php 2008-08-17 23:32:07 UTC (rev 39562) +++ branches/visual_diff/phase3/includes/LinksUpdate.php 2008-08-18 00:10:23 UTC (rev 39563) @@ -87,7 +87,7 @@ } - function doIncrementalUpdate() { + protected function doIncrementalUpdate() { wfProfileIn( __METHOD__ ); # Page links @@ -158,7 +158,7 @@ * May be slower or faster depending on level of lock contention and write speed of DB * Also useful where link table corruption needs to be repaired, e.g. in refreshLinks.php */ - function doDumbUpdate() { + protected function doDumbUpdate() { wfProfileIn( __METHOD__ ); # Refresh category pages and image description pages Modified: branches/visual_diff/phase3/includes/MessageCache.php =================================================================== --- branches/visual_diff/phase3/includes/MessageCache.php 2008-08-17 23:32:07 UTC (rev 39562) +++ branches/visual_diff/phase3/includes/MessageCache.php 2008-08-18 00:10:23 UTC (rev 39563) @@ -44,7 +44,6 @@ /** * ParserOptions is lazy initialised. - * Access should probably be protected. */ function getParserOptions() { if ( !$this->mParserOptions ) { Modified: branches/visual_diff/phase3/includes/StringUtils.php =================================================================== --- branches/visual_diff/phase3/includes/StringUtils.php 2008-08-17 23:32:07 UTC (rev 39562) +++ branches/visual_diff/phase3/includes/StringUtils.php 2008-08-18 00:10:23 UTC (rev 39563) @@ -167,6 +167,18 @@ $string = str_replace( '$', '\\$', $string ); return $string; } + + /** + * Workalike for explode() with limited memory usage. + * Returns an Iterator + */ + static function explode( $separator, $subject ) { + if ( substr_count( $subject, $separator ) > 1000 ) { + return new ExplodeIterator( $separator, $subject ); + } else { + return new ArrayIterator( explode( $separator, $subject ) ); + } + } } /** @@ -310,3 +322,90 @@ return $result; } } + +/** + * An iterator which works exactly like: + * + * foreach ( explode( $delim, $s ) as $element ) { + * ... + * } + * + * Except it doesn't use 193 byte per element + */ +class ExplodeIterator implements Iterator { + // The subject string + var $subject, $subjectLength; + + // The delimiter + var $delim, $delimLength; + + // The position of the start of the line + var $curPos; + + // The position after the end of the next delimiter + var $endPos; + + // The current token + var $current; + + /** + * Construct a DelimIterator + */ + function __construct( $delim, $s ) { + $this->subject = $s; + $this->delim = $delim; + + // Micro-optimisation (theoretical) + $this->subjectLength = strlen( $s ); + $this->delimLength = strlen( $delim ); + + $this->rewind(); + } + + function rewind() { + $this->curPos = 0; + $this->endPos = strpos( $this->subject, $this->delim ); + $this->refreshCurrent(); + } + + + function refreshCurrent() { + if ( $this->curPos === false ) { + $this->current = false; + } elseif ( $this->curPos >= $this->subjectLength ) { + $this->current = ''; + } elseif ( $this->endPos === false ) { + $this->current = substr( $this->subject, $this->curPos ); + } else { + $this->current = substr( $this->subject, $this->curPos, $this->endPos - $this->curPos ); + } + } + + function current() { + return $this->current; + } + + function key() { + return $this->curPos; + } + + function next() { + if ( $this->endPos === false ) { + $this->curPos = false; + } else { + $this->curPos = $this->endPos + $this->delimLength; + if ( $this->curPos >= $this->subjectLength ) { + $this->endPos = false; + } else { + $this->endPos = strpos( $this->subject, $this->delim, $this->curPos ); + } + } + $this->refreshCurrent(); + return $this->current; + } + + function valid() { + return $this->curPos !== false; + } +} + Modified: branches/visual_diff/phase3/includes/Title.php =================================================================== --- branches/visual_diff/phase3/includes/Title.php 2008-08-17 23:32:07 UTC (rev 39562) +++ branches/visual_diff/phase3/includes/Title.php 2008-08-18 00:10:23 UTC (rev 39563) @@ -10,12 +10,6 @@ define ( 'GAID_FOR_UPDATE', 1 ); -/** - * Title::newFromText maintains a cache to avoid expensive re-normalization of - * commonly used titles. On a batch operation this can become a memory leak - * if not bounded. After hitting this many titles reset the cache. - */ -define( 'MW_TITLECACHE_MAX', 1000 ); /** * Constants for pr_cascade bitfield @@ -36,6 +30,14 @@ //@} /** + * Title::newFromText maintains a cache to avoid expensive re-normalization of + * commonly used titles. On a batch operation this can become a memory leak + * if not bounded. After hitting this many titles reset the cache. + */ + const CACHE_MAX = 1000; + + + /** * @name Private member variables * Please use the accessor functions instead. * @private @@ -131,7 +133,7 @@ static $cachedcount = 0 ; if( $t->secureAndSplit() ) { if( $defaultNamespace == NS_MAIN ) { - if( $cachedcount >= MW_TITLECACHE_MAX ) { + if( $cachedcount >= self::CACHE_MAX ) { # Avoid memory leaks on mass operations... Title::$titleCache = array(); $cachedcount=0; Modified: branches/visual_diff/phase3/includes/ZhConversion.php =================================================================== --- branches/visual_diff/phase3/includes/ZhConversion.php 2008-08-17 23:32:07 UTC (rev 39562) +++ branches/visual_diff/phase3/includes/ZhConversion.php 2008-08-18 00:10:23 UTC (rev 39563) @@ -6228,7 +6228,6 @@ "退烧药" => "退燒藥", "逋发" => "逋髮", "透辟" => "透闢", -"这么着" => "這么著", "这里" => "這裏", "这里" => "這裡", "这只" => "這隻", @@ -6922,7 +6921,6 @@ "斗斗" => "鬥鬥", "斗鱼" => "鬥魚", "斗鹌鹑" => "鬥鵪鶉", -"闹着玩儿" => "鬧著玩儿", "闹着玩儿" => "鬧著玩兒", "闹钟" => "鬧鐘", "哄动" => "鬨動", @@ -10695,7 +10693,6 @@ "服务器" => "伺服器", "等于" => "等於", "局域网" => "區域網", -"计算机" => "電腦", "扫瞄仪" => "掃瞄器", "宽带" => "寬頻", "数据库" => "資料庫", @@ -10753,7 +10750,6 @@ "伯利兹" => "貝里斯", "伯利茲" => "貝里斯", "佛得角" => "維德角", -"佛得角" => "維德角", "克罗地亚" => "克羅埃西亞", "克羅地亞" => "克羅埃西亞", "冈比亚" => "甘比亞", @@ -10822,17 +10818,14 @@ "尼日尔" => "尼日", "尼日爾" => "尼日", "巴巴多斯" => "巴貝多", -"巴巴多斯" => "巴貝多", "巴布亚新几内亚" => "巴布亞紐幾內亞", "巴布亞新畿內亞" => "巴布亞紐幾內亞", "布基纳法索" => "布吉納法索", "布基納法索" => "布吉納法索", "布隆迪" => "蒲隆地", "布隆迪" => "蒲隆地", -"希腊" => "希臘", "帕劳" => "帛琉", "意大利" => "義大利", -"意大利" => "義大利", "所罗门群岛" => "索羅門群島", "所羅門群島" => "索羅門群島", "文莱" => "汶萊", @@ -10886,7 +10879,6 @@ "赞比亚" => "尚比亞", "贊比亞" => "尚比亞", "阿塞拜疆" => "亞塞拜然", -"阿塞拜疆" => "亞塞拜然", "阿拉伯联合酋长国" => "阿拉伯聯合大公國", "阿拉伯聯合酋長國" => "阿拉伯聯合大公國", "马尔代夫" => "馬爾地夫", @@ -10917,7 +10909,6 @@ "積架" => "捷豹", "福士" => "福斯", "雪铁龙" => "雪鐵龍", -"马自达" => "馬自達", "萬事得" => "馬自達", "拿破仑" => "拿破崙", "拿破侖" => "拿破崙", @@ -10937,29 +10928,17 @@ "“" => "「", "‘" => "『", "’" => "』", -"凶殺" => "兇殺", -"凶殘" => "兇殘", -"緝凶" => "緝兇", -"買凶" => "買兇", -"打印机" => "打印機", "印表機" => "打印機", "字节" => "位元組", "字節" => "位元組", -"打印" => "打印", "列印" => "打印", "硬件" => "硬件", "硬體" => "硬件", -"二极管" => "二極管", "二極體" => "二極管", -"三极管" => "三極管", "三極體" => "三極管", -"数码" => "數碼", "數位" => "數碼", -"软件" => "軟件", "軟體" => "軟件", -"网络" => "網絡", "網路" => "網絡", -"人工智能" => "人工智能", "人工智慧" => "人工智能", "航天飞机" => "穿梭機", "太空梭" => "穿梭機", @@ -10969,141 +10948,88 @@ "機器人" => "機械人", "移动电话" => "流動電話", "行動電話" => "流動電話", -"调制解调器" => "調制解調器", "數據機" => "調制解調器", "短信" => "短訊", "簡訊" => "短訊", -"乍得" => "乍得", "查德" => "乍得", -"也门" => "也門", "葉門" => "也門", -"伯利兹" => "伯利茲", "貝里斯" => "伯利茲", -"佛得角" => "佛得角", "維德角" => "佛得角", -"克罗地亚" => "克羅地亞", "克羅埃西亞" => "克羅地亞", -"冈比亚" => "岡比亞", "甘比亞" => "岡比亞", -"几内亚比绍" => "幾內亞比紹", "幾內亞比索" => "幾內亞比紹", -"列支敦士登" => "列支敦士登", "列支敦斯登" => "列支敦士登", -"利比里亚" => "利比里亞", "賴比瑞亞" => "利比里亞", -"加纳" => "加納", "迦納" => "加納", -"加蓬" => "加蓬", "加彭" => "加蓬", -"博茨瓦纳" => "博茨瓦納", "波札那" => "博茨瓦納", -"卡塔尔" => "卡塔爾", "卡達" => "卡塔爾", -"卢旺达" => "盧旺達", "盧安達" => "盧旺達", -"危地马拉" => "危地馬拉", "瓜地馬拉" => "危地馬拉", "厄瓜多尔" => "厄瓜多爾", +"厄瓜多爾" => "厄瓜多爾", "厄瓜多" => "厄瓜多爾", -"厄立特里亚" => "厄立特里亞", "厄利垂亞" => "厄立特里亞", -"吉布提" => "吉布堤", "吉布地" => "吉布堤", -"哥斯达黎加" => "哥斯達黎加", "哥斯大黎加" => "哥斯達黎加", -"图瓦卢" => "圖瓦盧", "吐瓦魯" => "圖瓦盧", -"圣卢西亚" => "聖盧西亞", "聖露西亞" => "聖盧西亞", "圣基茨和尼维斯" => "聖吉斯納域斯", "聖克里斯多福及尼維斯" => "聖吉斯納域斯", -"圣文森特和格林纳丁斯" => "聖文森特和格林納丁斯", "聖文森及格瑞那丁" => "聖文森特和格林納丁斯", -"圣马力诺" => "聖馬力諾", "聖馬利諾" => "聖馬力諾", -"圭亚那" => "圭亞那", "蓋亞那" => "圭亞那", -"坦桑尼亚" => "坦桑尼亞", "坦尚尼亞" => "坦桑尼亞", -"埃塞俄比亚" => "埃塞俄比亞", "衣索匹亞" => "埃塞俄比亞", "衣索比亞" => "埃塞俄比亞", -"基里巴斯" => "基里巴斯", "吉里巴斯" => "基里巴斯", -"狮子山" => "獅子山", "塞普勒斯" => "塞浦路斯", -"塞舌尔" => "塞舌爾", "塞席爾" => "塞舌爾", "多米尼加" => "多明尼加共和國", "多明尼加" => "多明尼加共和國", "多米尼加联邦" => "多明尼加聯邦", "多米尼克" => "多明尼加聯邦", -"安提瓜和巴布达" => "安提瓜和巴布達", "安地卡及巴布達" => "安提瓜和巴布達", "尼日利亚" => "尼日利亞", +"尼日利亞" => "尼日利亞", "奈及利亞" => "尼日利亞", "尼日尔" => "尼日爾", +"尼日爾" => "尼日爾", "尼日" => "尼日爾", -"巴巴多斯" => "巴巴多斯", "巴貝多" => "巴巴多斯", -"巴布亚新几内亚" => "巴布亞新畿內亞", "巴布亞紐幾內亞" => "巴布亞新畿內亞", -"布基纳法索" => "布基納法索", "布吉納法索" => "布基納法索", -"布隆迪" => "布隆迪", "蒲隆地" => "布隆迪", +"帕劳" => "帛琉", "義大利" => "意大利", -"所罗门群岛" => "所羅門群島", "索羅門群島" => "所羅門群島", -"斯威士兰" => "斯威士蘭", +"文莱" => "汶萊", "史瓦濟蘭" => "斯威士蘭", -"斯洛文尼亚" => "斯洛文尼亞", "斯洛維尼亞" => "斯洛文尼亞", -"新西兰" => "新西蘭", "紐西蘭" => "新西蘭", -"格林纳达" => "格林納達", "格瑞那達" => "格林納達", -"格鲁吉亚" => "喬治亞", -"格魯吉亞" => "喬治亞", -"梵蒂冈" => "梵蒂岡", -"毛里塔尼亚" => "毛里塔尼亞", "茅利塔尼亞" => "毛里塔尼亞", "毛里求斯" => "毛里裘斯", "模里西斯" => "毛里裘斯", +"沙地阿拉伯" => "沙特阿拉伯", "沙烏地阿拉伯" => "沙特阿拉伯", -"波斯尼亚和黑塞哥维那" => "波斯尼亞黑塞哥維那", "波士尼亞赫塞哥維納" => "波斯尼亞黑塞哥維那", -"津巴布韦" => "津巴布韋", "辛巴威" => "津巴布韋", -"洪都拉斯" => "洪都拉斯", "宏都拉斯" => "洪都拉斯", -"特立尼达和托巴哥" => "特立尼達和多巴哥", "千里達托貝哥" => "特立尼達和多巴哥", -"瑙鲁" => "瑙魯", "諾魯" => "瑙魯", -"瓦努阿图" => "瓦努阿圖", "萬那杜" => "瓦努阿圖", -"科摩罗" => "科摩羅", "葛摩" => "科摩羅", -"索马里" => "索馬里", "索馬利亞" => "索馬里", -"老挝" => "老撾", "寮國" => "老撾", "肯尼亚" => "肯雅", "肯亞" => "肯雅", -"莫桑比克" => "莫桑比克", "莫三比克" => "莫桑比克", -"莱索托" => "萊索托", "賴索托" => "萊索托", -"贝宁" => "貝寧", "貝南" => "貝寧", -"赞比亚" => "贊比亞", "尚比亞" => "贊比亞", -"阿塞拜疆" => "阿塞拜疆", "亞塞拜然" => "阿塞拜疆", -"阿拉伯联合酋长国" => "阿拉伯聯合酋長國", "阿拉伯聯合大公國" => "阿拉伯聯合酋長國", -"马尔代夫" => "馬爾代夫", "馬爾地夫" => "馬爾代夫", "馬利共和國" => "馬里共和國", "方便面" => "即食麵", @@ -11135,11 +11061,9 @@ "拿破崙" => "拿破侖", "布什" => "布殊", "布希" => "布殊", -"克林顿" => "克林頓", "柯林頓" => "克林頓", "萨达姆" => "薩達姆", "海珊" => "侯賽因", -"侯赛因" => "侯賽因", "大卫·贝克汉姆" => "大衛碧咸", "迈克尔·欧文" => "米高奧雲", "珍妮弗·卡普里亚蒂" => "卡佩雅蒂", @@ -11262,150 +11186,95 @@ "簡訊" => "短信", "烏茲別克" => "乌兹别克斯坦", "查德" => "乍得", -"乍得" => "乍得", -"也門" => "", "葉門" => "也门", "伯利茲" => "伯利兹", "貝里斯" => "伯利兹", "維德角" => "佛得角", -"佛得角" => "佛得角", -"克羅地亞" => "克罗地亚", "克羅埃西亞" => "克罗地亚", -"岡比亞" => "冈比亚", "甘比亞" => "冈比亚", -"幾內亞比紹" => "几内亚比绍", "幾內亞比索" => "几内亚比绍", "列支敦斯登" => "列支敦士登", -"列支敦士登" => "列支敦士登", -"利比里亞" => "利比里亚", "賴比瑞亞" => "利比里亚", -"加納" => "加纳", "迦納" => "加纳", "加彭" => "加蓬", -"加蓬" => "加蓬", -"博茨瓦納" => "博茨瓦纳", "波札那" => "博茨瓦纳", -"卡塔爾" => "卡塔尔", "卡達" => "卡塔尔", -"盧旺達" => "卢旺达", "盧安達" => "卢旺达", -"危地馬拉" => "危地马拉", "瓜地馬拉" => "危地马拉", "厄瓜多爾" => "厄瓜多尔", +"厄瓜多尔" => "厄瓜多尔", "厄瓜多" => "厄瓜多尔", -"厄立特里亞" => "厄立特里亚", "厄利垂亞" => "厄立特里亚", -"吉布堤" => "吉布提", "吉布地" => "吉布提", "哈薩克" => "哈萨克斯坦", -"哥斯達黎加" => "哥斯达黎加", "哥斯大黎加" => "哥斯达黎加", -"圖瓦盧" => "图瓦卢", "吐瓦魯" => "图瓦卢", "土庫曼" => "土库曼斯坦", -"聖盧西亞" => "圣卢西亚", "聖露西亞" => "圣卢西亚", "聖吉斯納域斯" => "圣基茨和尼维斯", "聖克里斯多福及尼維斯" => "圣基茨和尼维斯", -"聖文森特和格林納丁斯" => "圣文森特和格林纳丁斯", "聖文森及格瑞那丁" => "圣文森特和格林纳丁斯", -"聖馬力諾" => "圣马力诺", "聖馬利諾" => "圣马力诺", -"圭亞那" => "圭亚那", "蓋亞那" => "圭亚那", -"坦桑尼亞" => "坦桑尼亚", "坦尚尼亞" => "坦桑尼亚", -"埃塞俄比亞" => "埃塞俄比亚", "衣索匹亞" => "埃塞俄比亚", "衣索比亞" => "埃塞俄比亚", "吉里巴斯" => "基里巴斯", -"基里巴斯" => "基里巴斯", "塔吉克" => "塔吉克斯坦", "塞拉利昂" => "塞拉利昂", "塞普勒斯" => "塞浦路斯", -"塞浦路斯" => "塞浦路斯", -"塞舌爾" => "塞舌尔", "塞席爾" => "塞舌尔", "多明尼加共和國" => "多米尼加", "多明尼加" => "多米尼加", "多明尼加聯邦" => "多米尼加联邦", "多米尼克" => "多米尼加联邦", -"安提瓜和巴布達" => "安提瓜和巴布达", "安地卡及巴布達" => "安提瓜和巴布达", "尼日利亞" => "尼日利亚", +"尼日利亚" => "尼日利亚", "奈及利亞" => "尼日利亚", "尼日爾" => "尼日尔", +"尼日尔" => "尼日尔", "尼日" => "尼日尔", "巴貝多" => "巴巴多斯", -"巴巴多斯" => "巴巴多斯", -"巴布亞新畿內亞" => "巴布亚新几内亚", "巴布亞紐幾內亞" => "巴布亚新几内亚", "布基納法索" => "布基纳法索", "布吉納法索" => "布基纳法索", "蒲隆地" => "布隆迪", -"布隆迪" => "布隆迪", -"希臘" => "希腊", "帛琉" => "帕劳", "義大利" => "意大利", -"意大利" => "意大利", -"所羅門群島" => "所罗门群岛", "索羅門群島" => "所罗门群岛", "汶萊" => "文莱", -"斯威士蘭" => "斯威士兰", "史瓦濟蘭" => "斯威士兰", -"斯洛文尼亞" => "斯洛文尼亚", "斯洛維尼亞" => "斯洛文尼亚", -"新西蘭" => "新西兰", "紐西蘭" => "新西兰", -"格林納達" => "格林纳达", "格瑞那達" => "格林纳达", -"格魯吉亞" => "乔治亚", -"喬治亞" => "乔治亚", -"梵蒂岡" => "梵蒂冈", -"毛里塔尼亞" => "毛里塔尼亚", "茅利塔尼亞" => "毛里塔尼亚", "毛里裘斯" => "毛里求斯", "模里西斯" => "毛里求斯", "沙地阿拉伯" => "沙特阿拉伯", "沙烏地阿拉伯" => "沙特阿拉伯", -"波斯尼亞黑塞哥維那" => "波斯尼亚和黑塞哥维那", "波士尼亞赫塞哥維納" => "波斯尼亚和黑塞哥维那", -"津巴布韋" => "津巴布韦", "辛巴威" => "津巴布韦", "宏都拉斯" => "洪都拉斯", -"洪都拉斯" => "洪都拉斯", -"特立尼達和多巴哥" => "特立尼达和托巴哥", "千里達托貝哥" => "特立尼达和托巴哥", -"瑙魯" => "瑙鲁", "諾魯" => "瑙鲁", -"瓦努阿圖" => "瓦努阿图", "萬那杜" => "瓦努阿图", "溫納圖" => "瓦努阿图", -"科摩羅" => "科摩罗", "葛摩" => "科摩罗", "象牙海岸" => "科特迪瓦", "突尼西亞" => "突尼斯", -"索馬里" => "索马里", "索馬利亞" => "索马里", -"老撾" => "老挝", "寮國" => "老挝", "肯雅" => "肯尼亚", "肯亞" => "肯尼亚", "蘇利南" => "苏里南", "莫三比克" => "莫桑比克", -"莫桑比克" => "莫桑比克", -"萊索托" => "莱索托", "賴索托" => "莱索托", -"貝寧" => "贝宁", "貝南" => "贝宁", -"贊比亞" => "赞比亚", "尚比亞" => "赞比亚", "亞塞拜然" => "阿塞拜疆", -"阿塞拜疆" => "阿塞拜疆", -"阿拉伯聯合酋長國" => "阿拉伯联合酋长国", "阿拉伯聯合大公國" => "阿拉伯联合酋长国", "南韓" => "韩国", -"馬爾代夫" => "马尔代夫", "馬爾地夫" => "马尔代夫", "馬爾他" => "马耳他", "馬利共和國" => "马里共和国", @@ -11415,7 +11284,7 @@ "泡麵" => "方便面", "笨豬跳" => "蹦极跳", "绑紧跳" => "蹦极跳", -"冷盤 " => "凉菜", +"冷盤" => "凉菜", "冷菜" => "凉菜", "散钱" => "零钱", "谐星" => "笑星", @@ -11443,16 +11312,12 @@ "積架" => "捷豹", "福斯" => "大众", "福士" => "大众", -"雪鐵龍" => "雪铁龙", "萬事得" => "马自达", -"馬自達" => "马自达", "寶獅" => "标志", "拿破崙" => "拿破仑", "布殊" => "布什", "布希" => "布什", "柯林頓" => "克林顿", -"克林頓" => "克林顿", -"薩達姆" => "萨达姆", "海珊" => "萨达姆", "梵谷" => "凡高", "大衛碧咸" => "大卫·贝克汉姆", @@ -11472,6 +11337,7 @@ "方便面" => "快速面", "速食麵" => "快速面", "即食麵" => "快速面", +"泡麵" => "快速面", "蹦极跳" => "绑紧跳", "笨豬跳" => "绑紧跳", "凉菜" => "冷菜", @@ -11483,6 +11349,5 @@ "民乐" => "华乐", "住房" => "住屋", "房价" => "屋价", -"泡麵" => "快速面", ); \ No newline at end of file Modified: branches/visual_diff/phase3/includes/api/ApiLogin.php =================================================================== --- branches/visual_diff/phase3/includes/api/ApiLogin.php 2008-08-17 23:32:07 UTC (rev 39562) +++ branches/visual_diff/phase3/includes/api/ApiLogin.php 2008-08-18 00:10:23 UTC (rev 39563) @@ -139,6 +139,9 @@ $result['result'] = 'CreateBlocked'; $result['details'] = 'Your IP address is blocked from account creation'; break; + case LoginForm :: THROTTLED : + $result['result'] = 'Throttled'; + break; default : ApiBase :: dieDebug(__METHOD__, 'Unhandled case value'); } Modified: branches/visual_diff/phase3/includes/api/ApiQuery.php =================================================================== --- branches/visual_diff/phase3/includes/api/ApiQuery.php 2008-08-17 23:32:07 UTC (rev 39562) +++ branches/visual_diff/phase3/includes/api/ApiQuery.php 2008-08-18 00:10:23 UTC (rev 39563) @@ -229,8 +229,8 @@ * Create instances of all modules requested by the client */ private function InstantiateModules(&$modules, $param, $moduleList) { - $list = $this->params[$param]; - if (isset ($list)) + $list = @$this->params[$param]; + if (!is_null ($list)) foreach ($list as $moduleName) $modules[] = new $moduleList[$moduleName] ($this, $moduleName); } Modified: branches/visual_diff/phase3/includes/api/ApiQueryCategories.php =================================================================== --- branches/visual_diff/phase3/includes/api/ApiQueryCategories.php 2008-08-17 23:32:07 UTC (rev 39562) +++ branches/visual_diff/phase3/includes/api/ApiQueryCategories.php 2008-08-18 00:10:23 UTC (rev 39563) @@ -54,6 +54,7 @@ $params = $this->extractRequestParams(); $prop = $params['prop']; + $show = array_flip((array)$params['show']); $this->addFields(array ( 'cl_from', @@ -91,6 +92,15 @@ "(cl_from = $clfrom AND ". "cl_to >= '$clto')"); } + if(isset($show['hidden']) && isset($show['!hidden'])) + $this->dieUsage("Incorrect parameter - mutually exclusive values may not be supplied", 'show'); + if(isset($show['hidden']) || isset($show['!hidden'])) + { + $this->addTables('category'); + $this->addWhere(array( 'cl_to = cat_title', + 'cat_hidden' => isset($show['hidden']))); + } + # Don't order by cl_from if it's constant in the WHERE clause if(count($this->getPageSet()->getGoodTitles()) == 1) $this->addOption('ORDER BY', 'cl_to'); @@ -166,6 +176,13 @@ 'timestamp', ) ), + 'show' => array( + ApiBase :: PARAM_ISMULTI => true, + ApiBase :: PARAM_TYPE => array( + 'hidden', + '!hidden', + ) + ), 'limit' => array( ApiBase :: PARAM_DFLT => 10, ApiBase :: PARAM_TYPE => 'limit', @@ -181,6 +198,7 @@ return array ( 'prop' => 'Which additional properties to get for each category.', 'limit' => 'How many categories to return', + 'show' => 'Which kind of categories to show', 'continue' => 'When more results are available, use this to continue', ); } Modified: branches/visual_diff/phase3/includes/api/ApiQueryLinks.php =================================================================== --- branches/visual_diff/phase3/includes/api/ApiQueryLinks.php 2008-08-17 23:32:07 UTC (rev 39562) +++ branches/visual_diff/phase3/includes/api/ApiQueryLinks.php 2008-08-18 00:10:23 UTC (rev 39563) @@ -76,9 +76,9 @@ $params = $this->extractRequestParams(); $this->addFields(array ( - $this->prefix . '_from pl_from', - $this->prefix . '_namespace pl_namespace', - $this->prefix . '_title pl_title' + $this->prefix . '_from AS pl_from', + $this->prefix . '_namespace AS pl_namespace', + $this->prefix . '_title AS pl_title' )); $this->addTables($this->table); Modified: branches/visual_diff/phase3/includes/api/ApiQuerySearch.php =================================================================== --- branches/visual_diff/phase3/includes/api/ApiQuerySearch.php 2008-08-17 23:32:07 UTC (rev 39562) +++ branches/visual_diff/phase3/includes/api/ApiQuerySearch.php 2008-08-18 00:10:23 UTC (rev 39563) @@ -93,8 +93,9 @@ break; } - // Silently skip broken titles - if ($result->isBrokenTitle()) continue; + // Silently skip broken and missing titles + if ($result->isBrokenTitle() || $result->isMissingRevision()) + continue; $title = $result->getTitle(); if (is_null($resultPageSet)) { Modified: branches/visual_diff/phase3/includes/parser/CoreParserFunctions.php =================================================================== --- branches/visual_diff/phase3/includes/parser/CoreParserFunctions.php 2008-08-17 23:32:07 UTC (rev 39562) +++ branches/visual_diff/phase3/includes/parser/CoreParserFunctions.php 2008-08-18 00:10:23 UTC (rev 39563) @@ -56,7 +56,10 @@ static function intFunction( $parser, $part1 = '' /*, ... */ ) { if ( strval( $part1 ) !== '' ) { $args = array_slice( func_get_args(), 2 ); - return wfMsgReal( $part1, $args, true ); + $message = wfMsgGetKey( $part1, true, false, false ); + $message = $parser->replaceVariables( $message ); // like $wgMessageCache->transform() + $message = wfMsgReplaceArgs( $message, $args ); + return $message; } else { return array( 'found' => false ); } @@ -167,10 +170,16 @@ * @return string */ static function displaytitle( $parser, $text = '' ) { + global $wgRestrictDisplayTitle; $text = trim( Sanitizer::decodeCharReferences( $text ) ); - $title = Title::newFromText( $text ); - if( $title instanceof Title && $title->getFragment() == '' && $title->equals( $parser->mTitle ) ) + + if ( !$wgRestrictDisplayTitle ) { $parser->mOutput->setDisplayTitle( $text ); + } else { + $title = Title::newFromText( $text ); + if( $title instanceof Title && $title->getFragment() == '' && $title->equals( $parser->mTitle ) ) + $parser->mOutput->setDisplayTitle( $text ); + } return ''; } Copied: branches/visual_diff/phase3/includes/parser/LinkHolderArray.php (from rev 39562, trunk/phase3/includes/parser/LinkHolderArray.php) =================================================================== --- branches/visual_diff/phase3/includes/parser/LinkHolderArray.php (rev 0) +++ branches/visual_diff/phase3/includes/parser/LinkHolderArray.php 2008-08-18 00:10:23 UTC (rev 39563) @@ -0,0 +1,406 @@ +<?php + +class LinkHolderArray { + var $batchSize = 1000; + + var $internals = array(), $interwikis = array(); + var $size = 0; + var $parent; + + function __construct( $parent ) { + $this->parent = $parent; + } + + /** + * Merge another LinkHolderArray into this one + */ + function merge( $other ) { + foreach ( $other->internals as $ns => $entries ) { + $this->size += count( $entries ); + if ( !isset( $this->internals[$ns] ) ) { + $this->internals[$ns] = $entries; + } else { + $this->internals[$ns] += $entries; + } + } + $this->interwikis += $other->interwikis; + } + + /** + * Returns true if the memory requirements of this object are getting large + */ + function isBig() { + return $this->size > $this->batchSize; + } + + /** + * Clear all stored link holders. + * Make sure you don't have any text left using these link holders, before you call this + */ + function clear() { + $this->internals = array(); + $this->interwikis = array(); + $this->size = 0; + } + + /** + * Make a link placeholder. The text returned can be later resolved to a real link with + * replaceLinkHolders(). This is done for two reasons: firstly to avoid further + * parsing of interwiki links, and secondly to allow all existence checks and + * article length checks (for stub links) to be bundled into a single query. + * + */ + function makeHolder( $nt, $text = '', $query = '', $trail = '', $prefix = '' ) { + wfProfileIn( __METHOD__ ); + if ( ! is_object($nt) ) { + # Fail gracefully + $retVal = "<!-- ERROR -->{$prefix}{$text}{$trail}"; + } else { + # Separate the link trail from the rest of the link + list( $inside, $trail ) = Linker::splitTrail( $trail ); + + $entry = array( + 'title' => $nt, + 'text' => $prefix.$text.$inside, + 'pdbk' => $nt->getPrefixedDBkey(), + ); + if ( $query !== '' ) { + $entry['query'] = $query; + } + + if ( $nt->isExternal() ) { + // Use a globally unique ID to keep the objects mergable + $key = $this->parent->nextLinkID(); + $this->interwikis[$key] = $entry; + $retVal = "<!--IWLINK $key-->{$trail}"; + } else { + $key = $this->parent->nextLinkID(); + $ns = $nt->getNamespace(); + $this->internals[$ns][$key] = $entry; + $retVal = "<!--LINK $ns:$key-->{$trail}"; + } + $this->size++; + } + wfProfileOut( __METHOD__ ); + return $retVal; + } + + /** + * Replace <!--LINK--> link placeholders with actual links, in the buffer + * Placeholders created in Skin::makeLinkObj() + * Returns an array of link CSS classes, indexed by PDBK. + */ + function replace( &$text ) { + wfProfileIn( __METHOD__ ); + + $colours = $this->replaceInternal( $text ); + $this->replaceInterwiki( $text ); + + wfProfileOut( __METHOD__ ); + return $colours; + } + + /** + * Replace internal links + */ + protected function replaceInternal( &$text ) { + if ( !$this->internals ) { + return; + } + + wfProfileIn( __METHOD__ ); + global $wgUser, $wgContLang; + + $pdbks = array(); + $colours = array(); + $linkcolour_ids = array(); + $sk = $this->parent->getOptions()->getSkin(); + $linkCache = LinkCache::singleton(); + $output = $this->parent->getOutput(); + + wfProfileIn( __METHOD__.'-check' ); + $dbr = wfGetDB( DB_SLAVE ); + $page = $dbr->tableName( 'page' ); + $threshold = $wgUser->getOption('stubthreshold'); + + # Sort by namespace + ksort( $this->internals ); + + # Generate query + $query = false; + $current = null; + foreach ( $this->internals as $ns => $entries ) { + foreach ( $entries as $index => $entry ) { + $key = "$ns:$index"; + $title = $entry['title']; + $pdbk = $entry['pdbk']; + + # Skip invalid entries. + # Result will be ugly, but prevents crash. + if ( is_null( $title ) ) { + continue; + } + + # Check if it's a static known link, e.g. interwiki + if ( $title->isAlwaysKnown() ) { + $colours[$pdbk] = ''; + } elseif ( ( $id = $linkCache->getGoodLinkID( $pdbk ) ) != 0 ) { + $colours[$pdbk] = ''; + $output->addLink( $title, $id ); + } elseif ( $linkCache->isBadLink( $pdbk ) ) { + $colours[$pdbk] = 'new'; + } elseif ( $title->getNamespace() == NS_SPECIAL && !SpecialPage::exists( $pdbk ) ) { + $colours[$pdbk] = 'new'; + } else { + # Not in the link cache, add it to the query + if ( !isset( $current ) ) { + $current = $ns; + $query = "SELECT page_id, page_namespace, page_title, page_is_redirect, page_len"; + $query .= " FROM $page WHERE (page_namespace=$ns AND page_title IN("; + } elseif ( $current != $ns ) { + $current = $ns; + $query .= ")) OR (page_namespace=$ns AND page_title IN("; + } else { + $query .= ', '; + } + + $query .= $dbr->addQuotes( $title->getDBkey() ); + } + } + } + if ( $query ) { + $query .= '))'; + + $res = $dbr->query( $query, __METHOD__ ); + + # Fetch data and form into an associative array + # non-existent = broken + while ( $s = $dbr->fetchObject($res) ) { + $title = Title::makeTitle( $s->page_namespace, $s->page_title ); + $pdbk = $title->getPrefixedDBkey(); + $linkCache->addGoodLinkObj( $s->page_id, $title, $s->page_len, $s->page_is_redirect ); + $output->addLink( $title, $s->page_id ); + $colours[$pdbk] = $sk->getLinkColour( $title, $threshold ); + //add id to the extension todolist + $linkcolour_ids[$s->page_id] = $pdbk; + } + unset( $res ); + //pass an array of page_ids to an extension + wfRunHooks( 'GetLinkColours', array( $linkcolour_ids, &$colours ) ); + } + wfProfileOut( __METHOD__.'-check' ); + + # Do a second query for different language variants of links and categories + if($wgContLang->hasVariants()){ + $linkBatch = new LinkBatch(); + $variantMap = array(); // maps $pdbkey_Variant => $keys (of link holders) + $categoryMap = array(); // maps $category_variant => $category (dbkeys) + $varCategories = array(); // category replacements oldDBkey => newDBkey + + $categories = $output->getCategoryLinks(); + + // Add variants of links to link batch + foreach ( $this->internals as $ns => $entries ) { + foreach ( $entries as $index => $entry ) { + $key = "$ns:$index"; + $pdbk = $entry['pdbk']; + $title = $entry['title']; + $titleText = $title->getText(); + + // generate all variants of the link title text + $allTextVariants = $wgContLang->convertLinkToAllVariants($titleText); + + // if link was not found (in first query), add all variants to query + if ( !isset($colours[$pdbk]) ){ + foreach($allTextVariants as $textVariant){ + if($textVariant != $titleText){ + $variantTitle = Title::makeTitle( $ns, $textVariant ); + if(is_null($variantTitle)) continue; + $linkBatch->addObj( $variantTitle ); + $variantMap[$variantTitle->getPrefixedDBkey()][] = $key; + } + } + } + } + } + + // process categories, check if a category exists in some variant + foreach( $categories as $category ){ + $variants = $wgContLang->convertLinkToAllVariants($category); + foreach($variants as $variant){ + if($variant != $category){ + $variantTitle = Title::newFromDBkey( Title::makeName(NS_CATEGORY,$variant) ); + if(is_null($variantTitle)) continue; + $linkBatch->addObj( $variantTitle ); + $categoryMap[$variant] = $category; + } + } + } + + + if(!$linkBatch->isEmpty()){ + // construct query + $titleClause = $linkBatch->constructSet('page', $dbr); + + $variantQuery = "SELECT page_id, page_namespace, page_title, page_is_redirect, page_len"; + + $variantQuery .= " FROM $page WHERE $titleClause"; + + $varRes = $dbr->query( $variantQuery, __METHOD__ ); + + // for each found variants, figure out link holders and replace + while ( $s = $dbr->fetchObject($varRes) ) { + + $variantTitle = Title::makeTitle( $s->page_namespace, $s->page_title ); + $varPdbk = $variantTitle->getPrefixedDBkey(); + $vardbk = $variantTitle->getDBkey(); + + $holderKeys = array(); + if(isset($variantMap[$varPdbk])){ + $holderKeys = $variantMap[$varPdbk]; + $linkCache->addGoodLinkObj( $s->page_id, $variantTitle, $s->page_len, $s->page_is_redirect ); + $output->addLink( $variantTitle, $s->page_id ); + } + + // loop over link holders + foreach($holderKeys as $key){ + list( $ns, $index ) = explode( ':', $key, 2 ); + $entry =& $this->internals[$ns][$index]; + $pdbk = $entry['pdbk']; + + if(!isset($colours[$pdbk])){ + // found link in some of the variants, replace the link holder data + $entry['title'] = $variantTitle; + $entry['pdbk'] = $varPdbk; + + // set pdbk and colour + $colours[$varPdbk] = $sk->getLinkColour( $variantTitle, $threshold ); + $linkcolour_ids[$s->page_id] = $pdbk; + } + wfRunHooks( 'GetLinkColours', array( $linkcolour_ids, &$colours ) ); + } + + // check if the object is a variant of a category + if(isset($categoryMap[$vardbk])){ + $oldkey = $categoryMap[$vardbk]; + if($oldkey != $vardbk) + $varCategories[$oldkey]=$vardbk; + } + } + + // rebuild the categories in original order (if there are replacements) + if(count($varCategories)>0){ + $newCats = array(); + $originalCats = $output->getCategories(); + foreach($originalCats as $cat => $sortkey){ + // make the replacement + if( array_key_exists($cat,$varCategories) ) + $newCats[$varCategories[$cat]] = $sortkey; + else $newCats[$cat] = $sortkey; + } + $this->mOutput->parent->setCategoryLinks($newCats); + } + } + } + + # Construct search and replace arrays + wfProfileIn( __METHOD__.'-construct' ); + $replacePairs = array(); + foreach ( $this->internals as $ns => $entries ) { + foreach ( $entries as $index => $entry ) { + $pdbk = $entry['pdbk']; + $title = $entry['title']; + $query = isset( $entry['query'] ) ? $entry['query'] : ''; + $key = "$ns:$index"; + $searchkey = "<!--LINK $key-->"; + if ( !isset( $colours[$pdbk] ) || $colours[$pdbk] == 'new' ) { + $linkCache->addBadLinkObj( $title ); + $colours[$pdbk] = 'new'; + $output->addLink( $title, 0 ); + $replacePairs[$searchkey] = $sk->makeBrokenLinkObj( $title, + $entry['text'], + $query ); + } else { + $replacePairs[$searchkey] = $sk->makeColouredLinkObj( $title, $colours[$pdbk], + $entry['text'], + $query ); + } + } + } + $replacer = new HashtableReplacer( $replacePairs, 1 ); + wfProfileOut( __METHOD__.'-construct' ); + + # Do the thing + wfProfileIn( __METHOD__.'-replace' ); + $text = preg_replace_callback( + '/(<!--LINK .*?-->)/', + $replacer->cb(), + $text); + + wfProfileOut( __METHOD__.'-replace' ); + wfProfileOut( __METHOD__ ); + } + + /** + * Replace interwiki links + */ + protected function replaceInterwiki( &$text ) { + if ( empty( $this->interwikis ) ) { + return; + } + + wfProfileIn( __METHOD__ ); + # Make interwiki link HTML + $sk = $this->parent->getOptions()->getSkin(); + $replacePairs = array(); + foreach( $this->interwikis as $key => $link ) { + $replacePairs[$key] = $sk->link( $link['title'], $link['text'] ); + } + $replacer = new HashtableReplacer( $replacePairs, 1 ); + + $text = preg_replace_callback( + '/<!--IWLINK (.*?)-->/', + $replacer->cb(), + $text ); + wfProfileOut( __METHOD__ ); + } + + /** + * Replace <!--LINK--> link placeholders with plain text of links + * (not HTML-formatted). + * @param string $text + * @return string + */ + function replaceText( $text ) { + wfProfileIn( __METHOD__ ); + + $text = preg_replace_callback( + '/<!--(LINK|IWLINK) (.*?)-->/', + array( &$this, 'replaceTextCallback' ), + $text ); + + wfProfileOut( __METHOD__ ); + return $text; + } + + /** + * @param array $matches + * @return string + * @private + */ + function replaceTextCallback( $matches ) { + $type = $matches[1]; + $key = $matches[2]; + if( $type == 'LINK' ) { + list( $ns, $index ) = explode( ':', $key, 2 ); + if( isset( $this->internals[$ns][$index]['text'] ) ) { + return $this->internals[$ns][$index]['text']; + } + } elseif( $type == 'IWLINK' ) { + if( isset( $this->interwikis[$key]['text'] ) ) { + return $this->interwikis[$key]['text']; + } + } + return $matches[0]; + } +} Modified: branches/visual_diff/phase3/includes/parser/Parser.php =================================================================== --- branches/visual_diff/phase3/includes/parser/Parser.php 2008-08-17 23:32:07 UTC (rev 39562) +++ branches/visual_diff/phase3/includes/parser/Parser.php 2008-08-18 00:10:23 UTC (rev 39563) @@ -98,7 +98,7 @@ # Cleared with clearState(): var $mOutput, $mAutonumber, $mDTopen, $mStripState; var $mIncludeCount, $mArgStack, $mLastSection, $mInPre; - var $mInterwikiLinkHolders, $mLinkHolders; + var $mLinkHolders, $mLinkID; var $mIncludeSizes, $mPPNodeCount, $mDefaultSort; var $mTplExpandCache; // empty-frame expansion cache var $mTplRedirCache, $mTplDomCache, $mHeadings, $mDoubleUnderscores; @@ -179,17 +179,8 @@ $this->mStripState = new StripState; $this->mArgStack = false; $this->mInPre = false; - $this->mInterwikiLinkHolders = array( - 'texts' => array(), - 'titles' => array() - ); - $this->mLinkHolders = array( - 'namespaces' => array(), - 'dbkeys' => array(), - 'queries' => array(), - 'texts' => array(), - 'titles' => array() - ); + $this->mLinkHolders = new LinkHolderArray( $this ); + $this->mLinkID = 0; $this->mRevisionTimestamp = $this->mRevisionId = null; /** @@ -204,7 +195,7 @@ */ #$this->mUniqPrefix = "\x07UNIQ" . Parser::getRandomString(); # Changed to \x7f to allow XML double-parsing -- TS - $this->mUniqPrefix = "\x7fUNIQ" . Parser::getRandomString(); + $this->mUniqPrefix = "\x7fUNIQ" . self::getRandomString(); # Clear these on every parse, bug 4549 @@ -294,7 +285,7 @@ */ global $wgUseTidy, $wgAlwaysUseTidy, $wgContLang; - $fname = 'Parser::parse-' . wfGetCaller(); + $fname = __METHOD__.'-' . wfGetCaller(); wfProfileIn( __METHOD__ ); wfProfileIn( $fname ); @@ -328,7 +319,6 @@ ); $text = preg_replace( array_keys($fixtags), array_values($fixtags), $text ); - # only once and last $text = $this->doBlockLevels( $text, $linestart ); $this->replaceLinkHolders( $text ); @@ -348,7 +338,7 @@ $uniq_prefix = $this->mUniqPrefix; $matches = array(); $elements = array_keys( $this->mTransparentTagHooks ); - $text = Parser::extractTagsAndParams( $elements, $text, $matches, $uniq_prefix ); + $text = self::extractTagsAndParams( $elements, $text, $matches, $uniq_prefix ); foreach( $matches as $marker => $data ) { list( $element, $content, $params, $tag ) = $data; @@ -366,7 +356,7 @@ $text = Sanitizer::normalizeCharReferences( $text ); if (($wgUseTidy and $this->mOptions->mTidy) or $wgAlwaysUseTidy) { - $text = Parser::tidy($text); + $text = self::tidy($text); } else { # attempt to sanitize at least some nesting problems # (bug #2702 and quite a few others) @@ -471,6 +461,8 @@ function &getTitle() { return $this->mTitle; } function getOptions() { return $this->mOptions; } function getRevisionId() { return $this->mRevisionId; } + function getOutput() { return $this->mOutput; } + function nextLinkID() { return $this->mLinkID++; } function getFunctionLang() { global $wgLang, $wgContLang; @@ -658,9 +650,9 @@ ' "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html>'. '<head><title>test</title></head><body>'.$text.'</body></html>'; if( $wgTidyInternal ) { - $correctedtext = Parser::internalTidy( $wrappedtext ); + $correctedtext = self::internalTidy( $wrappedtext ); } else { - $correctedtext = Parser::externalTidy( $wrappedtext ); + $correctedtext = self::externalTidy( $wrappedtext ); } if( is_null( $correctedtext ) ) { wfDebug( "Tidy error detected!\n" ); @@ -677,8 +669,7 @@ */ function externalTidy( $text ) { global $wgTidyConf, $wgTidyBin, $wgTidyOpts; - $fname = 'Parser::externalTidy'; - wfProfileIn( $fname ); + wfProfileIn( __METHOD__ ); $cleansource = ''; $opts = ' -utf8'; @@ -707,7 +698,7 @@ } } - wfProfileOut( $fname ); + wfProfileOut( __METHOD__ ); if( $cleansource == '' && $text != '') { // Some kind of error happened, so we couldn't get the corrected text. @@ -729,8 +720,7 @@ */ function internalTidy( $text ) { global $wgTidyConf, $IP, $wgDebugTidy; - $fname = 'Parser::internalTidy'; - wfProfileIn( $fname ); + wfProfileIn( __METHOD__ ); $tidy = new tidy; $tidy->parseString( $text, $wgTidyConf, 'utf8' ); @@ -748,7 +738,7 @@ "\n-->"; } - wfProfileOut( $fname ); + wfProfileOut( __METHOD__ ); return $cleansource; } @@ -758,34 +748,35 @@ * @private */ function doTableStuff ( $text ) { - $fname = 'Parser::doTableStuff'; - wfProfileIn( $fname ); + wfProfileIn( __METHOD__ ); - $lines = explode ( "\n" , $text ); + $lines = StringUtils::explode( "\n", $text ); + $out = ''; $td_history = array (); // Is currently a td tag open? $last_tag_history = array (); // Save history of last lag activated (td, th or caption) $tr_history = array (); // Is currently a tr tag open? $tr_attributes = array (); // history of tr attributes $has_opened_tr = array(); // Did this table open a <tr> element? $indent_level = 0; // indent level of the table - foreach ( $lines as $key => $line ) - { - $line = trim ( $line ); + foreach ( $lines as $outLine ) { + $line = trim( $outLine ); + if( $line == '' ) { // empty line, go to next line + $out .= "\n"; continue; } - $first_character = $line{0}; + $first_character = $line[0]; $matches = array(); - if ( preg_match( '/^(:*)\{\|(.*)$/' , $line , $matches ) ) { + if ( preg_match( '/^(:*)\{\|(.*)$/', $line , $matches ) ) { // First check if we are starting a new table $indent_level = strlen( $matches[1] ); $attributes = $this->mStripState->unstripBoth( $matches[2] ); $attributes = Sanitizer::fixTagAttributes ( $attributes , 'table' ); - $lines[$key] = str_repeat( '<dl><dd>' , $indent_level ) . "<table{$attributes}>"; + $outLine = str_repeat( '<dl><dd>' , $indent_level ) . "<table{$attributes}>"; array_push ( $td_history , false ); array_push ( $last_tag_history , '' ); array_push ( $tr_history , false ); @@ -793,6 +784,7 @@ array_push ( $has_opened_tr , false ); } else if ( count ( $td_history ) == 0 ) { // Don't do any of the following + $out .= $outLine."\n"; continue; } else if ( substr ( $line , 0 , 2 ) == '|}' ) { // We are ending a table @@ -811,7 +803,7 @@ $line = "</{$last_tag}>{$line}"; } array_pop ( $tr_attributes ); - $lines[$key] = $line . str_repeat( '</dd></dl>' , $indent_level ); + $outLine = $line . str_repeat( '</dd></dl>' , $indent_level ); } else if ( substr ( $line , 0 , 2 ) == '|-' ) { // Now we have a table row $line = preg_replace( '#^\|-+#', '', $line ); @@ -835,7 +827,7 @@ $line = "</{$last_tag}>{$line}"; } - $lines[$key] = $line; + $outLine = $line; array_push ( $tr_history , false ); array_push ( $td_history , false ); array_push ( $last_tag_history , '' ); @@ -859,7 +851,7 @@ // attribute values containing literal "||". $cells = StringUtils::explodeMarkup( '||' , $line ); - $lines[$key] = ''; + $outLine = ''; // Loop through each table cell foreach ( $cells as $cell ) @@ -910,38 +902,42 @@ $cell = "{$previous}<{$last_tag}{$attributes}>{$cell_data[1]}"; } - $lines[$key] .= $cell; + $outLine .= $cell; array_push ( $td_history , true ); } } + $out .= $outLine . "\n"; } // Closing open td, tr && table while ( count ( $td_history ) > 0 ) { if ( array_pop ( $td_history ) ) { - $lines[] = '</td>' ; + $out .= "</td>\n"; } if ( array_pop ( $tr_history ) ) { - $lines[] = '</tr>' ; + $out .= "</tr>\n"; } if ( !array_pop ( $has_opened_tr ) ) { - $lines[] = "<tr><td></td></tr>" ; + $out .= "<tr><td></td></tr>\n" ; } - $lines[] = '</table>' ; + $out .= "</table>\n"; } - $output = implode ( "\n" , $lines ) ; + // Remove trailing line-ending (b/c) + if ( substr( $out, -1 ) == "\n" ) { + $out = substr( $out, 0, -1 ); + } // special case: don't return empty table - if( $output == "<table>\n<tr><td></td></tr>\n</table>" ) { - $output = ''; + if( $out == "<table>\n<tr><td></td></tr>\n</table>" ) { + $out = ''; } - wfProfileOut( $fname ); + wfProfileOut( __METHOD__ ); - return $output; + return $out; } /** @@ -952,12 +948,11 @@ */ function internalParse( $text ) { $isMain = true; - $fname = 'Parser::internalParse'; - wfProfileIn( $fname ); + wfProfileIn( __METHOD__ ); # Hook to suspend the parser in this state if ( !wfRunHooks( 'ParserBeforeInternalParse', array( &$this, &$text, &$this->mStripState ) ) ) { - wfProfileOut( $fname ); + wfProfileOut( __METHOD__ ); return $text ; } @@ -990,7 +985,7 @@ $text = $this->doMagicLinks( $text ); $text = $this->formatHeadings( $text, $isMain ); - wfProfileOut( $fname ); + wfProfileOut( __METHOD__ ); return $text; } @@ -1060,14 +1055,13 @@ * @private */ function doHeadings( $text ) { - $fname = 'Parser::doHeadings'; - wfProfileIn( $fname ); + wfProfileIn( __METHOD__ ); for ( $i = 6; $i >= 1; --$i ) { $h = str_repeat( '=', $i ); $text = preg_replace( "/^$h(.+)$h\\s*$/m", "<h$i>\\1</h$i>", $text ); } - wfProfileOut( $fname ); + wfProfileOut( __METHOD__ ); return $text; } @@ -1077,15 +1071,14 @@ * @return string the altered text */ function doAllQuotes( $text ) { - $fname = 'Parser::doAllQuotes'; - wfProfileIn( $fname ); + wfProfileIn( __METHOD__ ); $outtext = ''; - $lines = explode( "\n", $text ); + $lines = StringUtils::explode( "\n", $text ); foreach ( $lines as $line ) { - $outtext .= $this->doQuotes ( $line ) . "\n"; + $outtext .= $this->doQuotes( $line ) . "\n"; } $outtext = substr($outtext, 0,-1); - wfProfileOut( $fname ); + wfProfileOut( __METHOD__ ); return $outtext; } @@ -1264,8 +1257,7 @@ */ function replaceExternalLinks( $text ) { global $wgContLang; - $fname = 'Parser::replaceExternalLinks'; - wfProfileIn( $fname ); + wfProfileIn( __METHOD__ ); $sk = $this->mOptions->getSkin(); @@ -1335,11 +1327,11 @@ # Register link in the output object. # Replace unnecessary URL escape codes with the referenced character # This prevents spammers from hiding links from the filters - $pasteurized = Parser::replaceUnusualEscapes( $url ); + $pasteurized = self::replaceUnusualEscapes( $url ); $this->mOutput->addExternalLink( $pasteurized ); } - wfProfileOut( $fname ); + wfProfileOut( __METHOD__ ); return $s; } @@ -1349,8 +1341,7 @@ */ function replaceFreeExternalLinks( $text ) { global $wgContLang; - $fname = 'Parser::replaceFreeExternalLinks'; - wfProfileIn( $fname ); + wfProfileIn( __METHOD__ ); $bits = preg_split( '/(\b(?:' . wfUrlProtocols() . '))/S', $text, -1, PREG_SPLIT_DELIM_CAPTURE ); $s = array_shift( $bits ); @@ -1412,7 +1403,7 @@ $text = $sk->makeExternalLink( $url, $wgContLang->markNoConversion($url), true, 'free', $this->mTitle->getNamespace() ); # Register it in the output object... # Replace unnecessary URL escape codes with their equivalent characters - $pasteurized = Parser::replaceUnusualEscapes( $url ); + $pasteurized = self::replaceUnusualEscapes( $url ); $this->mOutput->addExternalLink( $pasteurized ); } $s .= $text . $trail; @@ -1420,7 +1411,7 @@ $s .= $protocol . $remainder; } } - wfProfileOut( $fname ); + wfProfileOut( __METHOD__ ); return $s; } @@ -1436,7 +1427,7 @@ */ static function replaceUnusualEscapes( $url ) { return preg_replace_callback( '/%[0-9A-Fa-f]{2}/', - array( 'Parser', 'replaceUnusualEscapesCallback' ), $url ); + array( __CLASS__, 'replaceUnusualEscapesCallback' ), $url ); } /** @@ -1480,35 +1471,48 @@ /** * Process [[ ]] wikilinks + * @return processed text * * @private */ function replaceInternalLinks( $s ) { + $this->mLinkHolders->merge( $this->replaceInternalLinks2( $s ) ); + return $s; + } + + /** + * Process [[ ]] wikilinks + * @return LinkHolderArray + * + * @private + */ + function replaceInternalLinks2( &$s ) { global $wgContLang; - static $fname = 'Parser::replaceInternalLinks' ; - wfProfileIn( $fname ); + wfProfileIn( __METHOD__ ); - wfProfileIn( $fname.'-setup' ); - static $tc = FALSE; + wfProfileIn( __METHOD__.'-setup' ); + static $tc = FALSE, $e1, $e1_img; # the % is needed to support urlencoded titles as well - if ( !$tc ) { $tc = Title::legalChars() . '#%'; } + if ( !$tc ) { + $tc = Title::legalChars() . '#%'; + # Match a link having the form [[namespace:link|alternate]]trail + $e1 = "/^([{$tc}]+)(?:\\|(.+?))?]](.*)\$/sD"; + # Match cases where there is no "]]", which might still be images + $e1_img = "/^([{$tc}]+)\\|(.*)\$/sD"; + } $sk = $this->mOptions->getSkin(); + $holders = new LinkHolderArray( $this ); #split the entire text string on occurences of [.[. - $a = explode( '[.[.', ' ' . $s ); + $a = StringUtils::explode( '[.[.', ' ' . $s ); #get the first element (all text up to first [.[.), and remove the space we added - $s = array_shift( $a ); + $s = $a->current(); + $a->next(); + $line = $a->current(); # Workaround for broken ArrayIterator::next() that returns "void" $s = substr( $s, 1 ); - # Match a link having the form [[namespace:link|alternate]]trail - static $e1 = FALSE; - if ( !$e1 ) { $e1 = "/^([{$tc}]+)(?:\\|(.+?))?]](.*)\$/sD"; } - # Match cases where there is no "]]", which might still be images - static $e1_img = FALSE; - if ( !$e1_img ) { $e1_img = "/^([{$tc}]+)\\|(.*)\$/sD"; } - $useLinkPrefixExtension = $wgContLang->linkPrefixExtension(); $e2 = null; if ( $useLinkPrefixExtension ) { @@ -1518,8 +1522,8 @@ } if( is_null( $this->mTitle ) ) { - wfProfileOut( $fname ); - wfProfileOut( $fname.'-setup' ); + wfProfileOut( __METHOD__ ); + wfProfileOut( __METHOD__.'-setup' ); throw new MWException( __METHOD__.": \$this->mTitle is null\n" ); } $nottalk = !$this->mTitle->isTalkPage(); @@ -1541,13 +1545,20 @@ $selflink = array($this->mTitle->getPrefixedText()); } $useSubpages = $this->areSubpagesAllowed(); - wfProfileOut( $fname.'-setup' ); + wfProfileOut( __METHOD__.'-setup' ); # Loop for each link - for ($k = 0; isset( $a[$k] ); $k++) { - $line = $a[$k]; + for ( ; $line !== false && $line !== null ; $a->next(), $line = $a->current() ) { + # Check for excessive memory usage + if ( $holders->isBig() ) { + # Too big + # Do the existence check, replace the link holders and clear the array + $holders->replace( $s ); + $holders->clear(); + } + if ( $useLinkPrefixExtension ) { - wfProfileIn( $fname.'-prefixhandling' ); + wfProfileIn( __METHOD__.'-prefixhandling' ); if ( preg_match( $e2, $s, $m ) ) { $prefix = $m[2]; $s = $m[1]; @@ -1559,12 +1570,12 @@ $prefix = $first_prefix; $first_prefix = false; } - wfProfileOut( $fname.'-prefixhandling' ); + wfProfileOut( __METHOD__.'-prefixhandling' ); } $might_be_img = false; - wfProfileIn( "$fname-e1" ); + wfProfileIn( __METHOD__."-e1" ); if ( preg_match( $e1, $line, $m ) ) { # page with normal text or alt $text = $m[2]; # If we get a ] at the beginning of $m[3] that means we have a link that's something like: @@ -1598,18 +1609,18 @@ $trail = ""; } else { # Invalid form; output directly $s .= $prefix . '[.[.' . $line ; - wfProfileOut( "$fname-e1" ); + wfProfileOut( __METHOD__."-e1" ); continue; } - wfProfileOut( "$fname-e1" ); - wfProfileIn( "$fname-misc" ); + wfProfileOut( __METHOD__."-e1" ); + wfProfileIn( __METHOD__."-misc" ); # Don't allow internal links to pages containing # PROTO: where PROTO is a valid URL protocol; these # should be external links. if (preg_match('/^\b(?:' . wfUrlProtocols() . ')/', $m[1])) { $s .= $prefix . '[.[.' . $line ; - wfProfileOut( "$fname-misc" ); + wfProfileOut( __METHOD__."-misc" ); continue; } @@ -1626,27 +1637,30 @@ $link = substr($link, 1); } - wfProfileOut( "$fname-misc" ); - wfProfileIn( "$fname-title" ); + wfProfileOut( __METHOD__."-misc" ); + wfProfileIn( __METHOD__."-title" ); $nt = Title::newFromText( $this->mStripState->unstripNoWiki($link) ); if( !$nt ) { $s .= $prefix . '[.[.' . $line; - wfProfileOut( "$fname-title" ); + wfProfileOut( __METHOD__."-title" ); continue; } $ns = $nt->getNamespace(); $iw = $nt->getInterWiki(); - wfProfileOut( "$fname-title" ); + wfProfileOut( __METHOD__."-title" ); if ($might_be_img) { # if this is actually an invalid link - wfProfileIn( "$fname-might_be_img" ); + wfProfileIn( __METHOD__."-might_be_img" ); if ($ns == NS_IMAGE && $noforce) { #but might be an image $found = false; - while (isset ($a[$k+1]) ) { + while ( true ) { #look at the next 'line' to see if we can close it there - $spliced = array_splice( $a, $k + 1, 1 ); - $next_line = array_shift( $spliced ); + $a->next(); + $next_line = $a->current(); + if ( $next_line === false || $next_line === null ) { + break; + } $m = explode( ']]', $next_line, 3 ); if ( count( $m ) == 3 ) { # the first ]] closes the inner link, the second the image @@ -1666,19 +1680,19 @@ if ( !$found ) { # we couldn't find the end of this imageLink, so output it raw #but don't ignore what might be perfectly normal links in the text we've examined - $text = $this->replaceInternalLinks($text); + $holders->merge( $this->replaceInternalLinks2( $text ) ); $s .= "{$prefix}[[$link|$text"; # note: no $trail, because without an end, there *is* no trail - wfProfileOut( "$fname-might_be_img" ); + wfProfileOut( __METHOD__."-might_be_img" ); continue; } } else { #it's not an image, so output it raw $s .= "{$prefix}[[$link|$text"; # note: no $trail, because without an end, there *is* no trail - wfProfileOut( "$fname-might_be_img" ); + wfProfileOut( __METHOD__."-might_be_img" ); continue; } - wfProfileOut( "$fname-might_be_img" ); + wfProfileOut( __METHOD__."-might_be_img" ); } $wasblank = ( '' == $text ); @@ -1688,41 +1702,38 @@ if( $noforce ) { # Interwikis - wfProfileIn( "$fname-interwiki" ); + wfProfileIn( __METHOD__."-interwiki" ); if( $iw && $this->mOptions->getInterwikiMagic() && $nottalk && $wgContLang->getLanguageName( $iw ) ) { $this->mOutput->addLanguageLink( $nt->getFullText() ); $s = rtrim($s . $prefix); $s .= trim($trail, "\n") == '' ? '': $prefix . $trail; - wfProfileOut( "$fname-interwiki" ); + wfProfileOut( __METHOD__."-interwiki" ); continue; } - wfProfileOut( "$fname-interwiki" ); + wfProfileOut( __METHOD__."-interwiki" ); if ( $ns == NS_IMAGE ) { - wfProfileIn( "$fname-image" ); + wfProfileIn( __METHOD__."-image" ); if ( !wfIsBadImage( $nt->getDBkey(), $this->mTitle ) ) { # recursively parse links inside the image caption # actually, this will parse them in any other parameters, too, # but it might be hard to fix that, and it doesn't matter ATM $text = $this->replaceExternalLinks($text); - $text = $this->replaceInternalLinks($text); + $holders->merge( $this->replaceInternalLinks2( $text ) ); # cloak any absolute URLs inside the image markup, so replaceExternalLinks() won't touch them - $s .= $prefix . $this->armorLinks( $this->makeImage( $nt, $text ) ) . $trail; - $this->mOutput->addImage( $nt->getDBkey() ); + $s .= $prefix . $this->armorLinks( $this->makeImage( $nt, $text, $holders ) ) . $trail; - wfProfileOut( "$fname-image" ); - continue; - } else { - # We still need to record the image's presence on the page - $this->mOutput->addImage( $nt->getDBkey() ); + wfProfileOut( __METHOD__."-image" ); } - wfProfileOut( "$fname-image" ); + $this->mOutput->addImage( $nt->getDBkey() ); + wfProfileOut( __METHOD__."-image" ); + continue; } if ( $ns == NS_CATEGORY ) { - wfProfileIn( "$fname-category" ); + wfProfileIn( __METHOD__."-category" ); $s = rtrim($s . "\n"); # bug 87 if ( $wasblank ) { @@ -1741,7 +1752,7 @@ */ $s .= trim($prefix . $trail, "\n") == '' ? '': $prefix . $trail; - wfProfileOut( "$fname-category" ); + |