diff options
Diffstat (limited to 'includes')
56 files changed, 1268 insertions, 602 deletions
diff --git a/includes/Article.php b/includes/Article.php index e0bd2016f242..23ae0bd21ff8 100644 --- a/includes/Article.php +++ b/includes/Article.php @@ -173,6 +173,7 @@ class Article { $striparray=array(); $parser=new Parser(); $parser->mOutputType=OT_WIKI; + $parser->mOptions = new ParserOptions(); $striptext=$parser->strip($text, $striparray, true); # now that we can be sure that no pseudo-sections are in the source, @@ -428,15 +429,17 @@ class Article { return false; } } - $redirData = $this->pageDataFromTitle( $dbr, $rt ); - if( $redirData ) { - $redirRev = Revision::newFromId( $redirData->page_latest ); - if( !is_null( $redirRev ) ) { - $this->mRedirectedFrom = $this->mTitle->getPrefixedText(); - $this->mTitle = $rt; - $data = $redirData; - $this->loadPageData( $data ); - $revision = $redirRev; + if( $rt->getInterwiki() == '' ) { + $redirData = $this->pageDataFromTitle( $dbr, $rt ); + if( $redirData ) { + $redirRev = Revision::newFromId( $redirData->page_latest ); + if( !is_null( $redirRev ) ) { + $this->mRedirectedFrom = $this->mTitle->getPrefixedText(); + $this->mTitle = $rt; + $data = $redirData; + $this->loadPageData( $data ); + $revision = $redirRev; + } } } } @@ -755,14 +758,14 @@ class Article { $wgOut->setRobotpolicy( 'noindex,follow' ); } if ( '' != $this->mRedirectedFrom ) { - $sk = $wgUser->getSkin(); - $redir = $sk->makeKnownLink( $this->mRedirectedFrom, '', - 'redirect=no' ); - $s = wfMsg( 'redirectedfrom', $redir ); - $wgOut->setSubtitle( $s ); - - # Can't cache redirects - $pcache = false; + if ( wfRunHooks( 'ArticleViewRedirect', array( &$this ) ) ) { + $sk = $wgUser->getSkin(); + $redir = $sk->makeKnownLink( $this->mRedirectedFrom, '', 'redirect=no' ); + $s = wfMsg( 'redirectedfrom', $redir ); + $wgOut->setSubtitle( $s ); + # Can't cache redirects + $pcache = false; + } } elseif ( !empty( $rdfrom ) ) { global $wgRedirectSources; if( $wgRedirectSources && preg_match( $wgRedirectSources, $rdfrom ) ) { @@ -864,7 +867,7 @@ class Article { $tbtext = ""; while ($o = $dbr->fetchObject($tbs)) { - $rmvtext = ""; + $rmvtxt = ""; if ($wgUser->isSysop()) { $delurl = $this->mTitle->getFullURL("action=deletetrackback&tbid=" . $o->tb_id . "&token=" . $wgUser->editToken()); @@ -939,6 +942,7 @@ class Article { 'page_random' => wfRandom(), 'page_touched' => $dbw->timestamp(), 'page_latest' => 0, # Fill this in shortly... + 'page_len' => 0, # Fill this in shortly... ), $fname ); $newid = $dbw->insertId(); @@ -970,13 +974,14 @@ class Article { # An extra check against threads stepping on each other $conditions['page_latest'] = $lastRevision; } + $text = $revision->getText(); $dbw->update( 'page', array( /* SET */ 'page_latest' => $revision->getId(), 'page_touched' => $dbw->timestamp(), 'page_is_new' => ($lastRevision === 0) ? 1 : 0, - 'page_is_redirect' => Article::isRedirect( $text ), + 'page_is_redirect' => Article::isRedirect( $text ) ? 1 : 0, 'page_len' => strlen( $text ), ), $conditions, @@ -1118,6 +1123,7 @@ class Article { $striparray=array(); $parser=new Parser(); $parser->mOutputType=OT_WIKI; + $parser->mOptions = new ParserOptions(); $oldtext=$parser->strip($oldtext, $striparray, true); # now that we can be sure that no pseudo-sections are in the source, @@ -1582,7 +1588,7 @@ class Article { wfDebug( "Article::confirmProtect\n" ); - $sub = $this->mTitle->getPrefixedText(); + $sub = htmlspecialchars( $this->mTitle->getPrefixedText() ); $wgOut->setRobotpolicy( 'noindex,nofollow' ); $check = ''; @@ -1599,7 +1605,7 @@ class Article { $wgOut->setPageTitle( wfMsg( 'confirmprotect' ) ); $wgOut->setSubtitle( wfMsg( 'protectsub', $sub ) ); $wgOut->addWikiText( wfMsg( 'confirmprotecttext' ) ); - $moveonly = htmlspecialchars( wfMsg( 'protectmoveonly' ) ); + $moveonly = wfMsg( 'protectmoveonly' ) ; // add it using addWikiText to prevent xss. bug:3991 $protcom = htmlspecialchars( wfMsg( 'protectcomment' ) ); $formaction = $this->mTitle->escapeLocalURL( 'action=protect' . $par ); } @@ -1625,7 +1631,10 @@ class Article { <input type='checkbox' name='wpMoveOnly' value='1' id='wpMoveOnly' /> </td> <td align='left'> - <label for='wpMoveOnly'>{$moveonly}</label> + <label for='wpMoveOnly'> "); + $wgOut->addWikiText( $moveonly ); // bug 3991 + $wgOut->addHTML( " + </label> </td> </tr> " ); } @@ -1706,7 +1715,7 @@ class Article { } # Fetch cur_text - $rev =& Revision::newFromTitle( $this->mTitle ); + $rev = Revision::newFromTitle( $this->mTitle ); # Fetch name(s) of contributors $rev_name = ''; @@ -2354,8 +2363,14 @@ class Article { } } - function onArticleDelete($title_obj) { - $title_obj->touchLinks(); + function onArticleDelete( $title ) { + global $wgMessageCache; + + $title->touchLinks(); + + if( $title->getNamespace() == NS_MEDIAWIKI) { + $wgMessageCache->replace( $title->getDBkey(), false ); + } } function onArticleEdit($title_obj) { diff --git a/includes/ChangesList.php b/includes/ChangesList.php index b224c0a68105..3578d2720137 100644 --- a/includes/ChangesList.php +++ b/includes/ChangesList.php @@ -205,8 +205,8 @@ class ChangesList { $r .= ' </tt>' ; $o = '' ; - if ( $rc_last_oldid != 0 ) { - $o = 'oldid='.$rc_last_oldid ; + if ( $rc_this_oldid != 0 ) { + $o = 'oldid='.$rc_this_oldid ; } if ( $rc_type == RC_LOG ) { $link = $rcObj->timestamp ; @@ -365,7 +365,7 @@ class ChangesList { $rc_user_text, 'target=' . $rc_user_text ); } else { $userPage =& Title::makeTitle( NS_USER, $rc_user_text ); - $userLink = $this->skin->makeLinkObj( $userPage, $rc_user_text ); + $userLink = $this->skin->makeLinkObj( $userPage, htmlspecialchars( $rc_user_text ) ); } $s .= $userLink; @@ -376,14 +376,14 @@ class ChangesList { $userTalkLink = ''; } else { $userTalkPage =& Title::makeTitle( NS_USER_TALK, $rc_user_text ); - $userTalkLink= $this->skin->makeLinkObj( $userTalkPage, $talkname ); + $userTalkLink= $this->skin->makeLinkObj( $userTalkPage, htmlspecialchars( $talkname ) ); } # Block link $blockLink=''; if ( ( $wgSysopUserBans || 0 == $rc_user ) && $wgUser->isAllowed('block') ) { $blockLinkPage = Title::makeTitle( NS_SPECIAL, 'Blockip' ); $blockLink = $this->skin->makeKnownLinkObj( $blockLinkPage, - $message['blocklink'], 'ip='.$rc_user_text ); + htmlspecialchars( $message['blocklink'] ), 'ip=' . urlencode( $rc_user_text ) ); } if($blockLink) { @@ -475,16 +475,17 @@ class ChangesList { } else { $rcIdQuery = ''; } - $query = $curIdEq."&diff=$rc_this_oldid&oldid=$rc_last_oldid"; + $querycur = $curIdEq."&diff=0&oldid=$rc_this_oldid"; + $querydiff = $curIdEq."&diff=$rc_this_oldid&oldid=$rc_last_oldid"; $aprops = ' tabindex="'.$baseRC->counter.'"'; - $curLink = $this->skin->makeKnownLinkObj( $rc->getTitle(), $message['cur'], $query, '' ,'' , $aprops ); + $curLink = $this->skin->makeKnownLinkObj( $rc->getTitle(), $message['cur'], $querycur, '' ,'' , $aprops ); if( $rc_type == RC_NEW || $rc_type == RC_LOG || $rc_type == RC_MOVE || $rc_type == RC_MOVE_OVER_REDIRECT ) { if( $rc_type != RC_NEW ) { $curLink = $message['cur']; } $diffLink = $message['diff']; } else { - $diffLink = $this->skin->makeKnownLinkObj( $rc->getTitle(), $message['diff'], $query . $rcIdQuery, '' ,'' , $aprops ); + $diffLink = $this->skin->makeKnownLinkObj( $rc->getTitle(), $message['diff'], $querydiff . $rcIdQuery, '' ,'' , $aprops ); } # Make "last" link diff --git a/includes/Credits.php b/includes/Credits.php index 8699c79f768f..7131789b0cd4 100644 --- a/includes/Credits.php +++ b/includes/Credits.php @@ -68,15 +68,14 @@ function getCredits($article, $cnt, $showIfMax=true) { * */ function getAuthorCredits($article) { - global $wgLang; + global $wgLang, $wgAllowRealName; $last_author = $article->getUser(); if ($last_author == 0) { $author_credit = wfMsg('anonymous'); } else { - - $real_name = User::whoIsReal($last_author); + if($wgAllowRealName) { $real_name = User::whoIsReal($last_author); } $user_name = User::whoIs($last_author); if (!empty($real_name)) { diff --git a/includes/Database.php b/includes/Database.php index b66e7178dc8c..724a6e98d8c4 100644 --- a/includes/Database.php +++ b/includes/Database.php @@ -37,7 +37,7 @@ class Database { */ var $mLastQuery = ''; - var $mServer, $mUser, $mPassword, $mConn, $mDBname; + var $mServer, $mUser, $mPassword, $mConn = null, $mDBname; var $mOut, $mOpened = false; var $mFailFunction; @@ -215,7 +215,14 @@ class Database { @/**/$this->mConn = mysql_pconnect( $server, $user, $password ); } else { # Create a new connection... - @/**/$this->mConn = mysql_connect( $server, $user, $password, true ); + if( version_compare( PHP_VERSION, '4.2.0', 'ge' ) ) { + @/**/$this->mConn = mysql_connect( $server, $user, $password, true ); + } else { + # On PHP 4.1 the new_link parameter is not available. We cannot + # guarantee that we'll actually get a new connection, and this + # may cause some operations to fail possibly. + @/**/$this->mConn = mysql_connect( $server, $user, $password ); + } } if ( $dbName != '' ) { @@ -237,8 +244,15 @@ class Database { if ( !$success ) { $this->reportConnectionError(); - $this->close(); } + + global $wgDBmysql5; + if( $wgDBmysql5 ) { + // Tell the server we're communicating with it in UTF-8. + // This may engage various charset conversions. + $this->query( 'SET NAMES utf8' ); + } + $this->mOpened = $success; return $success; } @@ -266,16 +280,15 @@ class Database { /** * @access private * @param string $msg error message ? - * @todo parameter $msg is not used */ - function reportConnectionError( $msg = '') { + function reportConnectionError() { if ( $this->mFailFunction ) { if ( !is_int( $this->mFailFunction ) ) { $ff = $this->mFailFunction; $ff( $this, mysql_error() ); } } else { - wfEmergencyAbort( $this, mysql_error() ); + wfEmergencyAbort( $this, $this->lastError() ); } } @@ -591,7 +604,13 @@ class Database { */ function lastError() { if ( $this->mConn ) { - $error = mysql_error( $this->mConn ); + # Even if it's non-zero, it can still be invalid + wfSuppressWarnings(); + $error = mysql_error( $this->mConn ); + if ( !$error ) { + $error = mysql_error(); + } + wfRestoreWarnings(); } else { $error = mysql_error(); } @@ -1120,7 +1139,10 @@ class Database { * PostgreSQL doesn't have them and returns "" */ function useIndexClause( $index ) { - return "FORCE INDEX ($index)"; + global $wgDBmysql4; + return $wgDBmysql4 + ? "FORCE INDEX ($index)" + : "USE INDEX ($index)"; } /** @@ -1458,7 +1480,7 @@ class Database { /** * @todo document */ - function resultObject( &$result ) { + function resultObject( $result ) { if( empty( $result ) ) { return NULL; } else { @@ -1606,13 +1628,12 @@ class ResultWrapper { */ function wfEmergencyAbort( &$conn, $error ) { global $wgTitle, $wgUseFileCache, $title, $wgInputEncoding, $wgOutputEncoding; - global $wgSitename, $wgServer; - + global $wgSitename, $wgServer, $wgMessageCache, $wgLogo; + # I give up, Brion is right. Getting the message cache to work when there is no DB is tricky. # Hard coding strings instead. - $noconnect = 'Sorry! The wiki is experiencing some technical difficulties, and cannot contact the database server. <br /> -$1'; + $noconnect = "<h1><img src='$wgLogo' style='float:left;margin-right:1em' alt=''>$wgSitename has a problem</h1><p><strong>Sorry! This site is experiencing technical difficulties.</strong></p><p>Try waiting a few minutes and reloading.</p><p><small>(Can't contact the database server: $1)</small></p>"; $mainpage = 'Main Page'; $searchdisabled = <<<EOT <p style="margin: 1.5em 2em 1em">$wgSitename search is disabled for performance reasons. You can search via Google in the meantime. @@ -1648,9 +1669,15 @@ border=\"0\" ALT=\"Google\"></A> header( 'Cache-control: none' ); header( 'Pragma: nocache' ); } + + # No database access + if ( is_object( $wgMessageCache ) ) { + $wgMessageCache->disable(); + } + $msg = wfGetSiteNotice(); if($msg == '') { - $msg = str_replace( '$1', $error, $noconnect ); + $msg = str_replace( '$1', htmlspecialchars( $error ), $noconnect ); } $text = $msg; diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php index 7690ddc86abe..190cd4918eb9 100644 --- a/includes/DefaultSettings.php +++ b/includes/DefaultSettings.php @@ -1,8 +1,11 @@ <?php /** - * DO NOT EDIT THIS FILE! * - * To customize your installation, edit "LocalSettings.php". + * DO NOT EVER EDIT THIS FILE! + * + * + * To customize your installation, edit "LocalSettings.php". If you make + * changes here, they will be lost on next upgrade of MediaWiki! * * Note that since all these string interpolations are expanded * before LocalSettings is included, if you localize something @@ -28,7 +31,7 @@ require_once( 'includes/SiteConfiguration.php' ); $wgConf = new SiteConfiguration; /** MediaWiki version number */ -$wgVersion = '1.5beta4'; +$wgVersion = '1.5.5'; /** Name of the site. It must be changed in LocalSettings.php */ $wgSitename = 'MediaWiki'; @@ -451,6 +454,24 @@ $wgDBtransactions = false; $wgDBmysql4 = false; /** + * Set to true to engage MySQL 4.1/5.0 charset-related features; + * for now will just cause sending of 'SET NAMES=utf8' on connect. + * + * WARNING: THIS IS EXPERIMENTAL! + * + * May break if you're not using the table defs from mysql5/tables.sql. + * May break if you're upgrading an existing wiki if set differently. + * Broken symptoms likely to include incorrect behavior with page titles, + * usernames, comments etc containing non-ASCII characters. + * Might also cause failures on the object cache and other things. + * + * Even correct usage may cause failures with Unicode supplementary + * characters (those not in the Basic Multilingual Plane) unless MySQL + * has enhanced their Unicode support. + */ +$wgDBmysql5 = false; + +/** * Other wikis on this site, can be administered from a single developer * account. * Array, interwiki prefix => database name @@ -616,6 +637,14 @@ $wgLogQueries = false; $wgDebugDumpSql = false; /** + * Set to an array of log group keys to filenames. + * If set, wfDebugLog() output for that group will go to that file instead + * of the regular $wgDebugLogFile. Useful for enabling selective logging + * in production. + */ +$wgDebugLogGroups = array(); + +/** * Whether to show "we're sorry, but there has been a database error" pages. * Displaying errors aids in debugging, but may display information useful * to an attacker. @@ -1009,7 +1038,9 @@ $wgMimeTypeBlacklist= array( # PHP scripts may execute arbitrary code on the server 'application/x-php', 'text/x-php', # Other types that may be interpreted by some servers - 'text/x-python', 'text/x-perl', 'text/x-bash', 'text/x-sh', 'text/x-csh' + 'text/x-python', 'text/x-perl', 'text/x-bash', 'text/x-sh', 'text/x-csh', + # Windows metafile, client-side vulnerability on some systems + 'application/x-msmetafile' ); /** This is a flag to determine whether or not to check file extensions on upload. */ @@ -1078,11 +1109,14 @@ $wgSVGConverters = array( 'sodipodi' => '$path/sodipodi -z -w $width -f $input -e $output', 'inkscape' => '$path/inkscape -z -w $width -f $input -e $output', 'batik' => 'java -Djava.awt.headless=true -jar $path/batik-rasterizer.jar -w $width -d $output $input', + 'rsvg' => '$path/rsvg -w$width -h$height $input $output', ); /** Pick one of the above */ $wgSVGConverter = 'ImageMagick'; /** If not in the executable PATH, specify */ $wgSVGConverterPath = ''; +/** Don't scale a SVG larger than this unless its native size is larger */ +$wgSVGMaxSize = 1024; /** Set $wgCommandLineMode if it's not set already, to avoid notices */ if( !isset( $wgCommandLineMode ) ) { @@ -1378,6 +1412,9 @@ $wgBrowserBlackList = array( * Fake out the timezone that the server thinks it's in. This will be used for * date display and not for what's stored in the DB. Leave to null to retain * your server's OS-based timezone value. This is the same as the timezone. + * + * This variable is currently used ONLY for signature formatting, not for + * anything else. */ # $wgLocaltimezone = 'GMT'; # $wgLocaltimezone = 'PST8PDT'; @@ -1385,6 +1422,27 @@ $wgBrowserBlackList = array( # $wgLocaltimezone = 'CET'; $wgLocaltimezone = null; +/** + * Set an offset from UTC in hours to use for the default timezone setting + * for anonymous users and new user accounts. + * + * This setting is used for most date/time displays in the software, and is + * overrideable in user preferences. It is *not* used for signature timestamps. + * + * You can set it to match the configured server timezone like this: + * $wgLocalTZoffset = date("Z") / 3600; + * + * If your server is not configured for the timezone you want, you can set + * this in conjunction with the signature timezone and override the TZ + * environment variable like so: + * $wgLocaltimezone="Europe/Berlin"; + * putenv("TZ=$wgLocaltimezone"); + * $wgLocalTZoffset = date("Z") / 3600; + * + * Leave at NULL to show times in universal time (UTC/GMT). + */ +$wgLocalTZoffset = null; + /** * When translating messages with wfMsg(), it is not always clear what should be @@ -1557,6 +1615,12 @@ $wgCountCategorizedImagesAsUsed = false; $wgExternalStores = false; /** + * An array of external mysql servers, e.g. + * $wgExternalServers = array( 'cluster1' => array( 'srv28', 'srv29', 'srv30' ) ); + */ +$wgExternalServers = array(); + +/** * list of trusted media-types and mime types. * Use the MEDIATYPE_xxx constants to represent media types. * This list is used by Image::isSafeFile diff --git a/includes/Defines.php b/includes/Defines.php index 67789ec96636..e17dc2b7809e 100644 --- a/includes/Defines.php +++ b/includes/Defines.php @@ -129,4 +129,15 @@ define( 'ALF_NO_LINK_LOCK', 4 ); define( 'ALF_NO_BLOCK_LOCK', 8 ); /**#@-*/ +/**#@+ + * Date format selectors; used in user preference storage and by + * Language::date() and co. + */ +define( 'MW_DATE_DEFAULT', '0' ); +define( 'MW_DATE_MDY', '1' ); +define( 'MW_DATE_DMY', '2' ); +define( 'MW_DATE_YMD', '3' ); +define( 'MW_DATE_ISO', 'ISO 8601' ); +/**#@-*/ + ?> diff --git a/includes/DifferenceEngine.php b/includes/DifferenceEngine.php index e78fb878388c..e056eee1fd67 100644 --- a/includes/DifferenceEngine.php +++ b/includes/DifferenceEngine.php @@ -169,11 +169,13 @@ CONTROL; $patrol = ''; } - $prevlink = $sk->makeKnownLinkObj( $wgTitle, wfMsg( 'previousdiff' ), 'diff=prev&oldid='.$this->mOldid ); + $prevlink = $sk->makeKnownLinkObj( $wgTitle, wfMsgHtml( 'previousdiff' ), + 'diff=prev&oldid='.$this->mOldid, '', '', 'id="differences-prevlink"' ); if ( $this->newRev->isCurrent() ) { $nextlink = ''; } else { - $nextlink = $sk->makeKnownLinkObj( $wgTitle, wfMsg( 'nextdiff' ), 'diff=next&oldid='.$this->mNewid ); + $nextlink = $sk->makeKnownLinkObj( $wgTitle, wfMsgHtml( 'nextdiff' ), + 'diff=next&oldid='.$this->mNewid, '', '', 'id="differences-nextlink"' ); } $oldHeader = "<strong>{$this->mOldtitle}</strong><br />$oldUserLink " . @@ -240,7 +242,7 @@ CONTROL; $userLink = $sk->makeLinkObj( Title::makeTitleSafe( NS_USER, $this->mOldUser ), $this->mOldUser ); $contribs = $sk->makeKnownLinkObj( Title::makeTitle( NS_SPECIAL, 'Contributions' ), wfMsg( 'contribslink' ), 'target=' . urlencode($this->mOldUser) ); - $nextlink = $sk->makeKnownLinkObj( $wgTitle, wfMsg( 'nextdiff' ), 'diff=next&oldid='.$this->mNewid ); + $nextlink = $sk->makeKnownLinkObj( $wgTitle, wfMsgHtml( 'nextdiff' ), 'diff=next&oldid='.$this->mNewid, '', '', 'id="differences-nextlink"' ); $header = "<div class=\"firstrevisionheader\" style=\"text-align: center\"><strong>{$this->mOldtitle}</strong><br />$userLink " . "($uTLink | $contribs)<br />" . $this->mOldComment . '<br />' . $nextlink. "</div>\n"; diff --git a/includes/EditPage.php b/includes/EditPage.php index 1bf5650be9ed..09468a2b9f5a 100644 --- a/includes/EditPage.php +++ b/includes/EditPage.php @@ -526,13 +526,12 @@ class EditPage { } $wgOut->setPageTitle( $s ); if ( !$this->checkUnicodeCompliantBrowser() ) { - $this->mArticle->setOldSubtitle(); $wgOut->addWikiText( wfMsg( 'nonunicodebrowser') ); } if ( isset( $this->mArticle ) && isset( $this->mArticle->mRevision ) && !$this->mArticle->mRevision->isCurrent() ) { - $this->mArticle->setOldSubtitle(); + $this->mArticle->setOldSubtitle( $this->mArticle->mRevision->getId() ); $wgOut->addWikiText( wfMsg( 'editingold' ) ); } } @@ -960,9 +959,19 @@ END } } - + /** + * Check if the browser is on a blacklist of user-agents known to + * mangle UTF-8 data on form submission. Returns true if Unicode + * should make it through, false if it's known to be a problem. + * @return bool + * @access private + */ function checkUnicodeCompliantBrowser() { global $wgBrowserBlackList; + if( empty( $_SERVER["HTTP_USER_AGENT"] ) ) { + // 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) ) { diff --git a/includes/ExternalStoreDB.php b/includes/ExternalStoreDB.php index 00f8c481ea89..3c61726523fb 100644 --- a/includes/ExternalStoreDB.php +++ b/includes/ExternalStoreDB.php @@ -10,31 +10,74 @@ require_once( 'LoadBalancer.php' ); /** @package MediaWiki */ + class ExternalStoreDB { + var $loadBalancers = array(); + /** * Fetch data from given URL * @param string $url An url */ + + function &getLoadBalancer( $cluster ) { + global $wgExternalServers; + if ( !array_key_exists( $cluster, $this->loadBalancers ) ) { + $this->loadBalancers[$cluster] = LoadBalancer::newFromParams( $wgExternalServers[$cluster] ); + } + return $this->loadBalancers[$cluster]; + } + + function &getSlave( $cluster ) { + $lb =& $this->getLoadBalancer( $cluster ); + return $lb->getConnection( DB_SLAVE ); + } + + function &getMaster( $cluster ) { + $lb =& $this->getLoadBalancer( $cluster ); + return $lb->getConnection( DB_MASTER ); + } + function fetchFromURL($url) { global $wgExternalServers; # - # URLs have the form DB://cluster/id, e.g. - # DB://cluster1/3298247 + # URLs have the form DB://cluster/id or DB://cluster/id/itemid for concatenated storage # $path = explode( '/', $url ); $cluster = $path[2]; $id = $path[3]; + if ( isset( $path[4] ) ) { + $itemID = $path[4]; + } else { + $itemID = false; + } - $lb = LoadBalancer::NewFromParams( $wgExternalServers[$cluster] ); - $db = $lb->getConnection( DB_SLAVE ); + $dbr =& $this->getSlave( $cluster ); + $ret = $dbr->selectField( 'blobs', 'blob_text', array( 'blob_id' => $id ) ); - $ret = $db->selectField( 'blobs', 'blob_text', array( 'blob_id' => $id ) ); - + if ( $itemID !== false ) { + # Unserialise object and get item + $obj = unserialize( $ret ); + $ret = $obj->getItem( $itemID ); + } return $ret; } - /* @fixme XXX: may require other methods, for store, delete, - * whatever, for initial ext storage + /** + * Insert a data item into a given cluster + * + * @param string $cluster The cluster name + * @param string $data The data item + * @return string URL */ + function store( $cluster, $data ) { + global $wgExternalServers; + $fname = 'ExternalStoreDB::store'; + + $dbw =& $this->getMaster( $cluster ); + + $id = $dbw->nextSequenceValue( 'blob_blob_id_seq' ); + $dbw->insert( 'blobs', array( 'blob_id' => $id, 'blob_text' => $data ), $fname ); + return "DB://$cluster/" . $dbw->insertId(); + } } ?> diff --git a/includes/Feed.php b/includes/Feed.php index a798ce44ec7c..6a2862762104 100644 --- a/includes/Feed.php +++ b/includes/Feed.php @@ -240,7 +240,7 @@ class AtomFeed extends ChannelFeed { global $wgVersion, $wgOut; $this->outXmlHeader(); - ?><feed version="0.3" xml:lang="<?php print $this->getLanguage() ?>"> + ?><feed version="0.3" xmlns="http://purl.org/atom/ns#" xml:lang="<?php print $this->getLanguage() ?>"> <title><?php print $this->getTitle() ?></title> <link rel="alternate" type="text/html" href="<?php print $this->getUrl() ?>"/> <modified><?php print $this->formatTime( wfTimestampNow() ) ?>Z</modified> diff --git a/includes/GlobalFunctions.php b/includes/GlobalFunctions.php index a694c96cfefc..afb7fc026086 100644 --- a/includes/GlobalFunctions.php +++ b/includes/GlobalFunctions.php @@ -176,6 +176,22 @@ function wfDebug( $text, $logonly = false ) { } /** + * Send a line to a supplementary debug log file, if configured, or main debug log if not. + * $wgDebugLogGroups[$logGroup] should be set to a filename to send to a separate log. + * @param string $logGroup + * @param string $text + */ +function wfDebugLog( $logGroup, $text ) { + global $wgDebugLogGroups, $wgDBname; + if( $text{strlen( $text ) - 1} != "\n" ) $text .= "\n"; + if( isset( $wgDebugLogGroups[$logGroup] ) ) { + @error_log( "$wgDBname: $text", 3, $wgDebugLogGroups[$logGroup] ); + } else { + wfDebug( $text, true ); + } +} + +/** * Log for database errors * @param string $text Database error message. */ @@ -238,7 +254,7 @@ function wfReadOnly() { // Set $wgReadOnly and unset $wgReadOnlyFile, for faster access next time if ( is_file( $wgReadOnlyFile ) ) { - $wgReadOnly = true; + $wgReadOnly = file_get_contents( $wgReadOnlyFile ); } else { $wgReadOnly = false; } @@ -402,8 +418,8 @@ function wfAbruptExit( $error = false ){ if( function_exists( 'debug_backtrace' ) ){ // PHP >= 4.3 $bt = debug_backtrace(); for($i = 0; $i < count($bt) ; $i++){ - $file = $bt[$i]['file']; - $line = $bt[$i]['line']; + $file = isset($bt[$i]['file']) ? $bt[$i]['file'] : "unknown"; + $line = isset($bt[$i]['line']) ? $bt[$i]['line'] : "unknown"; wfDebug("WARNING: Abrupt exit in $file at line $line\n"); } } else { @@ -523,7 +539,7 @@ function wfViewPrevNext( $offset, $limit, $link, $query = '', $atend = false ) { if( is_object( $link ) ) { $title =& $link; } else { - $title =& Title::newFromText( $link ); + $title = Title::newFromText( $link ); if( is_null( $title ) ) { return false; } @@ -774,6 +790,7 @@ function wfMerge( $old, $mine, $yours, &$result ){ # This check may also protect against code injection in # case of broken installations. if(! file_exists( $wgDiff3 ) ){ + wfDebug( "diff3 not found\n" ); return false; } @@ -788,7 +805,7 @@ function wfMerge( $old, $mine, $yours, &$result ){ fwrite( $yourtextFile, $yours ); fclose( $yourtextFile ); # Check for a conflict - $cmd = wfEscapeShellArg( $wgDiff3 ) . ' -a --overlap-only ' . + $cmd = $wgDiff3 . ' -a --overlap-only ' . wfEscapeShellArg( $mytextName ) . ' ' . wfEscapeShellArg( $oldtextName ) . ' ' . wfEscapeShellArg( $yourtextName ); @@ -802,7 +819,7 @@ function wfMerge( $old, $mine, $yours, &$result ){ pclose( $handle ); # Merge differences - $cmd = wfEscapeShellArg( $wgDiff3 ) . ' -a -e --merge ' . + $cmd = $wgDiff3 . ' -a -e --merge ' . wfEscapeShellArg( $mytextName, $oldtextName, $yourtextName ); $handle = popen( $cmd, 'r' ); $result = ''; @@ -815,6 +832,11 @@ function wfMerge( $old, $mine, $yours, &$result ){ } while ( true ); pclose( $handle ); unlink( $mytextName ); unlink( $oldtextName ); unlink( $yourtextName ); + + if ( $result === '' && $old !== '' && $conflict == false ) { + wfDebug( "Unexpected null result from diff3. Command: $cmd\n" ); + $conflict = true; + } return ! $conflict; } @@ -1042,8 +1064,9 @@ define('TS_EXIF', 4); * @return string Time in the format specified in $outputtype */ function wfTimestamp($outputtype=TS_UNIX,$ts=0) { - if ($ts==0) { - $uts=time(); + $uts = 0; + if ($ts==0) { + $uts=time(); } elseif (preg_match("/^(\d{4})\-(\d\d)\-(\d\d) (\d\d):(\d\d):(\d\d)$/",$ts,$da)) { # TS_DB $uts=gmmktime((int)$da[4],(int)$da[5],(int)$da[6], @@ -1151,7 +1174,7 @@ function wfGetSiteNotice() { * * @param string $element * @param array $attribs Name=>value pairs. Values will be escaped. - * @param bool $contents NULL to make an open tag only; '' for a contentless closed tag (default) + * @param string $contents NULL to make an open tag only; '' for a contentless closed tag (default) * @return string */ function wfElement( $element, $attribs = null, $contents = '') { @@ -1182,7 +1205,7 @@ function wfElement( $element, $attribs = null, $contents = '') { * * @param string $element * @param array $attribs Name=>value pairs. Values will be escaped. - * @param bool $contents NULL to make an open tag only; '' for a contentless closed tag (default) + * @param string $contents NULL to make an open tag only; '' for a contentless closed tag (default) * @return string */ function wfElementClean( $element, $attribs = array(), $contents = '') { @@ -1195,6 +1218,37 @@ function wfElementClean( $element, $attribs = array(), $contents = '') { return wfElement( $element, $attribs, $contents ); } +/** + * Create a namespace selector + * + * @param mixed $selected The namespace which should be selected, default '' + * @param string $allnamespaces Value of a special item denoting all namespaces. Null to not include (default) + * @return Html string containing the namespace selector + */ +function &HTMLnamespaceselector($selected = '', $allnamespaces = null) { + global $wgContLang; + $s = "<select name='namespace' class='namespaceselector'>\n"; + $arr = $wgContLang->getFormattedNamespaces(); + if( !is_null($allnamespaces) ) { + $arr = array($allnamespaces => wfMsgHtml('namespacesall')) + $arr; + } + foreach ($arr as $index => $name) { + if ($index < NS_MAIN) continue; + + $name = $index !== 0 ? $name : wfMsgHtml('blanknamespace'); + + if ($index === $selected) { + $s .= wfElement("option", + array("value" => $index, "selected" => "selected"), + $name); + } else { + $s .= wfElement("option", array("value" => $index), $name); + } + } + $s .= "</select>\n"; + return $s; +} + /** Global singleton instance of MimeMagic. This is initialized on demand, * please always use the wfGetMimeMagic() function to get the instance. * @@ -1304,4 +1358,28 @@ function wfEncryptPassword( $userid, $password ) { return $p; } +/** + * Appends to second array if $value differs from that in $default + */ +function wfAppendToArrayIfNotDefault( $key, $value, $default, &$changed ) { + if ( is_null( $changed ) ) { + wfDebugDieBacktrace('GlobalFunctions::wfAppendToArrayIfNotDefault got null'); + } + if ( $default[$key] !== $value ) { + $changed[$key] = $value; + } +} + +/** + * Since wfMsg() and co suck, they don't return false if the message key they + * looked up didn't exist but a XHTML string, this function checks for the + * nonexistance of messages by looking at wfMsg() output + * + * @param $msg The message key looked up + * @param $wfMsgOut The output of wfMsg*() + * @return bool + */ +function wfNoMsg( $msg, $wfMsgOut ) { + return $wfMsgOut === "<$msg>"; +} ?> diff --git a/includes/HistoryBlob.php b/includes/HistoryBlob.php index f7fa77afd304..3eb2b6f5335e 100644 --- a/includes/HistoryBlob.php +++ b/includes/HistoryBlob.php @@ -182,7 +182,7 @@ $wgBlobCache = array(); * @package MediaWiki */ class HistoryBlobStub { - var $mOldId, $mHash; + var $mOldId, $mHash,$mRef; /** @todo document */ function HistoryBlobStub( $hash = '', $oldid = 0 ) { @@ -197,6 +197,20 @@ class HistoryBlobStub { $this->mOldId = $id; } + /** + * Sets the location (old_id) of the referring object + */ + function setReferrer( $id ) { + $this->mRef = $id; + } + + /** + * Gets the location of the referring object + */ + function getReferrer() { + return $this->mRef; + } + /** @todo document */ function getText() { global $wgBlobCache; diff --git a/includes/Hooks.php b/includes/Hooks.php index 021d799cf066..3e337f80959c 100644 --- a/includes/Hooks.php +++ b/includes/Hooks.php @@ -30,12 +30,12 @@ if (defined('MEDIAWIKI')) { * in here than would normally be necessary. */ - function wfRunHooks($event, $args) { + function wfRunHooks($event, $args = null) { global $wgHooks; if (!is_array($wgHooks)) { - wfDieDebugBacktrace("Global hooks array is not an array!\n"); + wfDebugDieBacktrace("Global hooks array is not an array!\n"); return false; } @@ -44,7 +44,7 @@ if (defined('MEDIAWIKI')) { } if (!is_array($wgHooks[$event])) { - wfDieDebugBacktrace("Hooks array for event '$event' is not an array!\n"); + wfDebugDieBacktrace("Hooks array for event '$event' is not an array!\n"); return false; } @@ -63,7 +63,7 @@ if (defined('MEDIAWIKI')) { if (is_array($hook)) { if (count($hook) < 1) { - wfDieDebugBacktrace("Empty array in hooks for " . $event . "\n"); + wfDebugDieBacktrace("Empty array in hooks for " . $event . "\n"); } else if (is_object($hook[0])) { $object = $hook[0]; if (count($hook) < 2) { @@ -82,7 +82,7 @@ if (defined('MEDIAWIKI')) { $have_data = true; } } else { - wfDieDebugBacktrace("Unknown datatype in hooks for " . $event . "\n"); + wfDebugDieBacktrace("Unknown datatype in hooks for " . $event . "\n"); } } else if (is_string($hook)) { # functions look like strings, too $func = $hook; @@ -90,7 +90,7 @@ if (defined('MEDIAWIKI')) { $object = $hook; $method = "on" . $event; } else { - wfDieDebugBacktrace("Unknown datatype in hooks for " . $event . "\n"); + wfDebugDieBacktrace("Unknown datatype in hooks for " . $event . "\n"); } /* We put the first data element on, if needed. */ diff --git a/includes/Image.php b/includes/Image.php index 3e7362132438..15e45937ef25 100644 --- a/includes/Image.php +++ b/includes/Image.php @@ -582,7 +582,7 @@ class Image if (!$mime || $mime==='unknown' || $mime==='unknown/unknown') return false; - #if it's SVG, check if ther's a converter enabled + #if it's SVG, check if there's a converter enabled if ($mime === 'image/svg') { global $wgSVGConverters, $wgSVGConverter; @@ -854,7 +854,7 @@ class Image * @return ThumbnailImage * @access public */ - function &getThumbnail( $width, $height=-1 ) { + function getThumbnail( $width, $height=-1 ) { if ( $height == -1 ) { return $this->renderThumb( $width ); } @@ -915,6 +915,9 @@ class Image function renderThumb( $width, $useScript = true ) { global $wgUseSquid, $wgInternalServer; global $wgThumbnailScriptPath, $wgSharedThumbnailScriptPath; + + $fname = 'Image::renderThumb'; + wfProfileIn( $fname ); $width = IntVal( $width ); @@ -922,18 +925,26 @@ class Image if ( ! $this->exists() ) { # If there is no image, there will be no thumbnail + wfProfileOut( $fname ); return null; } # Sanity check $width if( $width <= 0 || $this->width <= 0) { # BZZZT + wfProfileOut( $fname ); return null; } - if( $width >= $this->width && !$this->mustRender() ) { + global $wgSVGMaxSize; + $maxsize = $this->mustRender() + ? max( $this->width, $wgSVGMaxSize ) + : $this->width - 1; + if( $width > $maxsize ) { # Don't make an image bigger than the source - return new ThumbnailImage( $this->getViewURL(), $this->getWidth(), $this->getHeight() ); + $thumb = new ThumbnailImage( $this->getViewURL(), $this->getWidth(), $this->getHeight() ); + wfProfileOut( $fname ); + return $thumb; } $height = floor( $this->height * ( $width/$this->width ) ); @@ -941,7 +952,9 @@ class Image list( $isScriptUrl, $url ) = $this->thumbUrl( $width ); if ( $isScriptUrl && $useScript ) { // Use thumb.php to render the image - return new ThumbnailImage( $url, $width, $height ); + $thumb = new ThumbnailImage( $url, $width, $height ); + wfProfileOut( $fname ); + return $thumb; } $thumbName = $this->thumbName( $width, $this->fromSharedDirectory ); @@ -976,7 +989,9 @@ class Image } } - return new ThumbnailImage( $url, $width, $height, $thumbPath ); + $thumb = new ThumbnailImage( $url, $width, $height, $thumbPath ); + wfProfileOut( $fname ); + return $thumb; } // END OF function renderThumb /** @@ -998,12 +1013,14 @@ class Image if( isset( $wgSVGConverters[$wgSVGConverter] ) ) { global $wgSVGConverterPath; $cmd = str_replace( - array( '$path/', '$width', '$input', '$output' ), - array( $wgSVGConverterPath, - $width, + array( '$path/', '$width', '$height', '$input', '$output' ), + array( $wgSVGConverterPath ? "$wgSVGConverterPath/" : "", + intval( $width ), + intval( $height ), wfEscapeShellArg( $this->imagePath ), wfEscapeShellArg( $thumbPath ) ), $wgSVGConverters[$wgSVGConverter] ); + wfDebug( "reallyRenderThumb SVG: $cmd\n" ); $conv = shell_exec( $cmd ); } else { $conv = false; @@ -1016,7 +1033,7 @@ class Image " -quality 85 -background white -geometry {$width} ". wfEscapeShellArg($this->imagePath) . " " . wfEscapeShellArg($thumbPath); - wfDebug("reallyRenderThumb: running ImageMagick: $cmd"); + wfDebug("reallyRenderThumb: running ImageMagick: $cmd\n"); $conv = shell_exec( $cmd ); } else { # Use PHP's builtin GD library functions. @@ -1244,7 +1261,7 @@ class Image /** * Record an image upload in the upload log and the image table */ - function recordUpload( $oldver, $desc, $copyStatus = '', $source = '' ) { + function recordUpload( $oldver, $desc, $copyStatus = '', $source = '', $watch = false ) { global $wgUser, $wgLang, $wgTitle, $wgDeferredUpdateList; global $wgUseCopyrightUpload, $wgUseSquid, $wgPostCommitUpdateList; @@ -1304,15 +1321,7 @@ class Image $descTitle = $this->getTitle(); $purgeURLs = array(); - if ( $dbw->affectedRows() ) { - # Successfully inserted, this is a new image - $id = $descTitle->getArticleID(); - - if ( $id == 0 ) { - $article = new Article( $descTitle ); - $article->insertNewArticle( $textdesc, $desc, false, false, true ); - } - } else { + if( $dbw->affectedRows() == 0 ) { # Collision, this is an update of an image # Insert previous contents into oldimage $dbw->insertSelect( 'oldimage', 'image', @@ -1349,12 +1358,27 @@ class Image 'img_name' => $this->name ), $fname ); + } + + $article = new Article( $descTitle ); + $minor = false; + $watch = $watch || $wgUser->isWatched( $descTitle ); + $suppressRC = true; // There's already a log entry, so don't double the RC load + + if( $descTitle->exists() ) { + // TODO: insert a null revision into the page history for this update. + if( $watch ) { + $wgUser->addWatch( $descTitle ); + } # Invalidate the cache for the description page $descTitle->invalidateCache(); $purgeURLs[] = $descTitle->getInternalURL(); + } else { + // New image; create the description page. + $article->insertNewArticle( $textdesc, $desc, $minor, $watch, $suppressRC ); } - + # Invalidate cache for all pages using this image $linksTo = $this->getLinksTo(); diff --git a/includes/ImagePage.php b/includes/ImagePage.php index 58085809521e..b6524e191107 100644 --- a/includes/ImagePage.php +++ b/includes/ImagePage.php @@ -132,7 +132,7 @@ class ImagePage extends Article { global $wgOut, $wgUser, $wgImageLimits, $wgRequest, $wgUseImageResize, $wgRepositoryBaseUrl, $wgUseExternalEditor, $wgServer, $wgFetchCommonsDescriptions; - $full_url = $this->img->getViewURL(); + $full_url = $this->img->getURL(); $anchoropen = ''; $anchorclose = ''; @@ -168,25 +168,29 @@ class ImagePage extends Article { $width = floor( $width * $maxHeight / $height ); $height = $maxHeight; } - if ( !$this->img->mustRender() - && ( $width != $this->img->getWidth() || $height != $this->img->getHeight() ) ) { + if ( $width != $this->img->getWidth() || $height != $this->img->getHeight() ) { if( $wgUseImageResize ) { $thumbnail = $this->img->getThumbnail( $width ); if ( $thumbnail == null ) { - $url = $full_url; + $url = $this->img->getViewURL(); } else { - $url = $thumbnail->getUrl(); + $url = $thumbnail->getURL(); } } else { # No resize ability? Show the full image, but scale # it down in the browser so it fits on the page. - $url = $full_url; + $url = $this->img->getViewURL(); } $anchoropen = "<a href=\"{$full_url}\">"; - $anchorclose = "</a><br />\n$anchoropen{$msg}</a>"; + $anchorclose = "</a><br />"; + if( $this->img->mustRender() ) { + $showLink = true; + } else { + $anchorclose .= "\n$anchoropen{$msg}</a>"; + } } else { - $url = $full_url; - $showLink = $this->img->mustRender(); + $url = $this->img->getViewURL(); + $showLink = true; } $wgOut->addHTML( '<div class="fullImageLink" id="file">' . $anchoropen . "<img border=\"0\" src=\"{$url}\" width=\"{$width}\" height=\"{$height}\" alt=\"" . @@ -200,40 +204,35 @@ class ImagePage extends Article { $icon->toHtml() . '</a></div>' ); } - + $showLink = true; } if ($showLink) { - $s= $sk->makeMediaLink( $this->img->getName(), '', '', true ); - $info= wfMsg( 'fileinfo', ceil($this->img->getSize()/1024.0), $this->img->getMimeType() ); - + $filename = wfEscapeWikiText( $this->img->getName() ); + $info = wfMsg( 'fileinfo', + ceil($this->img->getSize()/1024.0), + $this->img->getMimeType() ); + if (!$this->img->isSafeFile()) { - $wgOut->addHTML("<div class=\"fullMedia\">"); - $wgOut->addHTML("<span class=\"dangerousLink\">"); - $wgOut->addHTML($s); - $wgOut->addHTML("</span>"); - - $wgOut->addHTML("<span class=\"fileInfo\"> ("); - $wgOut->addWikiText( $info, false ); - $wgOut->addHTML(")</span>"); - $wgOut->addHTML("</div>"); - - #this should be formated a little nicer. Is CSS sufficient? - $wgOut->addHTML("<div class=\"mediaWarning\">"); - $wgOut->addWikiText( wfMsg( 'mediawarning' ) ); - $wgOut->addHTML('</div>'); - + $warning = wfMsg( 'mediawarning' ); + $wgOut->addWikiText( <<<END +<div class="fullMedia"> +<span class="dangerousLink">[[Media:$filename|$filename]]</span> +<span class="fileInfo"> ($info)</span> +</div> + +<div class="mediaWarning">$warning</div> +END + ); } else { - $wgOut->addHTML("<div class=\"fullMedia\">"); - $wgOut->addHTML($s); - - $wgOut->addHTML("<span class=\"fileInfo\"> ("); - $wgOut->addWikiText( $info, false ); - $wgOut->addHTML(")</span>"); - - $wgOut->addHTML("</div>"); + $wgOut->addWikiText( <<<END +<div class="fullMedia"> +[[Media:$filename|$filename]] <span class="fileInfo"> ($info)</span> +</div> +END + ); } } @@ -367,7 +366,7 @@ class ImagePage extends Article { { global $wgUser, $wgOut, $wgRequest; - $confirm = $wgRequest->getBool( 'wpConfirmB' ); + $confirm = $wgRequest->wasPosted(); $image = $wgRequest->getVal( 'image' ); $oldimage = $wgRequest->getVal( 'oldimage' ); diff --git a/includes/Linker.php b/includes/Linker.php index 81e31e56ce0c..d37e8148e7b4 100644 --- a/includes/Linker.php +++ b/includes/Linker.php @@ -373,6 +373,10 @@ class Linker { global $wgContLang, $wgUser, $wgThumbLimits; $img = new Image( $nt ); + if ( !$img->allowInlineDisplay() ) { + return $this->makeKnownLinkObj( $nt ); + } + $url = $img->getViewURL(); $prefix = $postfix = ''; @@ -406,7 +410,7 @@ class Linker { $wopt = User::getDefaultOption( 'thumbsize' ); } - $width = $wgThumbLimits[$wopt]; + $width = min( $img->getWidth(), $wgThumbLimits[$wopt] ); } return $prefix.$this->makeThumbLinkObj( $img, $label, $alt, $align, $width, $height, $framed, $manual_thumb ).$postfix; @@ -591,7 +595,7 @@ class Linker { * @access public * @todo Handle invalid or missing images better. */ - function makeMediaLinkObj( $title, $text = '', $nourl=false ) { + function makeMediaLinkObj( $title, $text = '' ) { if( is_null( $title ) ) { ### HOTFIX. Instead of breaking, return empty string. return $text; @@ -600,9 +604,6 @@ class Linker { $img = new Image( $title ); if( $img->exists() ) { $url = $img->getURL(); - if( $nourl ) { - $url = str_replace( "http://", "http-noparse://", $url ); - } $class = 'internal'; } else { $upload = Title::makeTitle( NS_SPECIAL, 'Upload' ); diff --git a/includes/LoadBalancer.php b/includes/LoadBalancer.php index ee402d68e587..c4c780576b4e 100644 --- a/includes/LoadBalancer.php +++ b/includes/LoadBalancer.php @@ -27,7 +27,7 @@ define( 'DB_WRITE', -2 ); */ class LoadBalancer { /* private */ var $mServers, $mConnections, $mLoads, $mGroupLoads; - /* private */ var $mFailFunction; + /* private */ var $mFailFunction, $mErrorConnection; /* private */ var $mForce, $mReadIndex, $mLastIndex; /* private */ var $mWaitForFile, $mWaitForPos, $mWaitTimeout; /* private */ var $mLaggedSlaveMode; @@ -40,6 +40,7 @@ class LoadBalancer { $this->mReadIndex = -1; $this->mForce = -1; $this->mLastIndex = -1; + $this->mErrorConnection = false; } function newFromParams( $servers, $failFunction = false, $waitTimeout = 10 ) @@ -139,7 +140,7 @@ class LoadBalancer { return false; } - #wfDebug( var_export( $loads, true ) ); + #wfDebugLog( 'connect', var_export( $loads, true ) ); # Return a random representative of the remainder return $this->pickRandom( $loads ); @@ -182,8 +183,9 @@ class LoadBalancer { $i = $this->pickRandom( $loads ); } } + $serverIndex = $i; if ( $i !== false ) { - wfDebug( "Using reader #$i: {$this->mServers[$i]['host']}...\n" ); + wfDebugLog( 'connect', "Using reader #$i: {$this->mServers[$i]['host']}...\n" ); $this->openConnection( $i ); if ( !$this->isOpen( $i ) ) { @@ -210,7 +212,10 @@ class LoadBalancer { } if ( $sleepTime ) { $totalElapsed += $sleepTime; + $x = "{$this->mServers[$serverIndex]['host']} $sleepTime [$serverIndex]"; + wfProfileIn( "$fname-sleep $x" ); usleep( $sleepTime ); + wfProfileOut( "$fname-sleep $x" ); } } while ( count( $loads ) && !$done && $totalElapsed / 1e6 < $wgDBClusterTimeout ); @@ -348,6 +353,10 @@ class LoadBalancer { $i = $this->getWriterIndex(); } } + # Couldn't find a working server in getReaderIndex()? + if ( $i === false ) { + $this->reportConnectionError( $this->mErrorConnection ); + } # Now we have an explicit index into the servers array $this->openConnection( $i, $fail ); @@ -374,6 +383,7 @@ class LoadBalancer { if ( $fail ) { $this->reportConnectionError( $this->mConnections[$i] ); } + $this->mErrorConnection = $this->mConnections[$i]; $this->mConnections[$i] = false; $success = false; } @@ -434,7 +444,7 @@ class LoadBalancer { if ( $this->mFailFunction ) { $conn->failFunction( $this->mFailFunction ); } else { - $conn->failFunction( 'wfEmergencyAbort' ); + $conn->failFunction( false ); } $conn->reportConnectionError(); $reporting = false; diff --git a/includes/MimeMagic.php b/includes/MimeMagic.php index 5be0ee602954..31f57d0e7d11 100644 --- a/includes/MimeMagic.php +++ b/includes/MimeMagic.php @@ -304,6 +304,27 @@ class MimeMagic { return in_array( $mime, $types ); } + /** + * Returns true if the extension represents a type which can + * be reliably detected from its content. Use this to determine + * whether strict content checks should be applied to reject + * invalid uploads; if we can't identify the type we won't + * be able to say if it's invalid. + * + * @todo Be more accurate when using fancy mime detector plugins; + * right now this is the bare minimum getimagesize() list. + * @return bool + */ + function isRecognizableExtension( $extension ) { + static $types = array( + 'gif', 'jpeg', 'jpg', 'png', 'swf', 'psd', + 'bmp', 'tiff', 'tif', 'jpc', 'jp2', + 'jpx', 'jb2', 'swc', 'iff', 'wbmp', + 'xbm' + ); + return in_array( strtolower( $extension ), $types ); + } + /** mime type detection. This uses detectMimeType to detect the mim type of the file, * but applies additional checks to determine some well known file formats that may be missed @@ -318,14 +339,21 @@ class MimeMagic { $fname = 'MimeMagic::guessMimeType'; $mime= $this->detectMimeType($file,$useExt); - if (strpos($mime,"text/")===0 || - $mime==="application/xml") { + // Read a chunk of the file + $f = fopen( $file, "rt" ); + if( !$f ) return "unknown/unknown"; + $head = fread( $f, 1024 ); + fclose( $f ); + + $sub4 = substr( $head, 0, 4 ); + if ( $sub4 == "\x01\x00\x09\x00" || $sub4 == "\xd7\xcd\xc6\x9a" ) { + // WMF kill kill kill + // Note that WMF may have a bare header, no magic number. + // The former of the above two checks is theoretically prone to false positives + $mime = "application/x-msmetafile"; + } - // Read a chunk of the file - $f = fopen( $file, "rt" ); - if( !$f ) return "unknown/unknown"; - $head = fread( $f, 1024 ); - fclose( $f ); + if (strpos($mime,"text/")===0 || $mime==="application/xml") { $xml_type= NULL; $script_type= NULL; diff --git a/includes/OutputPage.php b/includes/OutputPage.php index ae4694bc4812..e2e63fc4e826 100644 --- a/includes/OutputPage.php +++ b/includes/OutputPage.php @@ -174,7 +174,8 @@ class OutputPage { $name .= ' - '.$taction; } } - $this->setHTMLTitle( $name . ' - ' . wfMsg( 'wikititlesuffix' ) ); + + $this->setHTMLTitle( wfMsg( 'pagetitle', $name ) ); } function getHTMLTitle() { return $this->mHTMLtitle; } function getPageTitle() { return $this->mPagetitle; } @@ -809,13 +810,13 @@ class OutputPage { $ret .= "<!DOCTYPE html PUBLIC \"$wgDocType\"\n \"$wgDTD\">\n"; - if ( "" == $this->mHTMLtitle ) { - $this->mHTMLtitle = wfMsg( "pagetitle", $this->mPagetitle ); + if ( '' == $this->getHTMLTitle() ) { + $this->setHTMLTitle( wfMsg( 'pagetitle', $this->getPageTitle() )); } $rtl = $wgContLang->isRTL() ? " dir='RTL'" : ''; $ret .= "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"$wgContLanguageCode\" lang=\"$wgContLanguageCode\" $rtl>\n"; - $ret .= "<head>\n<title>" . htmlspecialchars( $this->mHTMLtitle ) . "</title>\n"; + $ret .= "<head>\n<title>" . htmlspecialchars( $this->getHTMLTitle() ) . "</title>\n"; array_push( $this->mMetatags, array( "http:Content-type", "$wgMimeType; charset={$wgOutputEncoding}" ) ); $ret .= $this->getHeadLinks(); @@ -876,7 +877,7 @@ class OutputPage { $link = $wgRequest->escapeAppendQuery( 'feed=rss' ); $ret .= "<link rel='alternate' type='application/rss+xml' title='RSS 2.0' href='$link' />\n"; $link = $wgRequest->escapeAppendQuery( 'feed=atom' ); - $ret .= "<link rel='alternate' type='application/rss+atom' title='Atom 0.3' href='$link' />\n"; + $ret .= "<link rel='alternate' type='application/atom+xml' title='Atom 0.3' href='$link' />\n"; } return $ret; diff --git a/includes/Parser.php b/includes/Parser.php index f7d32f5590a3..53f11493580c 100644 --- a/includes/Parser.php +++ b/includes/Parser.php @@ -43,9 +43,6 @@ define( 'OT_MSG' , 3 ); # may want to use in wikisyntax define( 'STRIP_COMMENTS', 'HTMLCommentStrip' ); -# prefix for escaping, used in two functions at least -define( 'UNIQ_PREFIX', 'NaodW29'); - # Constants needed for external link processing define( 'HTTP_PROTOCOLS', 'http:\/\/|https:\/\/' ); # Everything except bracket, space, or control characters @@ -101,7 +98,7 @@ class Parser # Cleared with clearState(): var $mOutput, $mAutonumber, $mDTopen, $mStripState = array(); var $mVariables, $mIncludeCount, $mArgStack, $mLastSection, $mInPre; - var $mInterwikiLinkHolders, $mLinkHolders; + var $mInterwikiLinkHolders, $mLinkHolders, $mUniqPrefix; # Temporary: var $mOptions, $mTitle, $mOutputType, @@ -153,6 +150,7 @@ class Parser 'texts' => array(), 'titles' => array() ); + $this->mUniqPrefix = 'UNIQ' . Parser::getRandomString(); } /** @@ -349,7 +347,7 @@ class Parser $gallery_content = array(); # Replace any instances of the placeholders - $uniq_prefix = UNIQ_PREFIX; + $uniq_prefix = $this->mUniqPrefix; #$text = str_replace( $uniq_prefix, wfHtmlEscapeFirst( $uniq_prefix ), $text ); # html @@ -377,16 +375,14 @@ class Parser } # math - $text = Parser::extractTags('math', $text, $math_content, $uniq_prefix); - foreach( $math_content as $marker => $content ){ - if( $render ) { - if( $this->mOptions->getUseTeX() ) { + if( $this->mOptions->getUseTeX() ) { + $text = Parser::extractTags('math', $text, $math_content, $uniq_prefix); + foreach( $math_content as $marker => $content ){ + if( $render ) { $math_content[$marker] = renderMath( $content ); } else { - $math_content[$marker] = '<math>'.$content.'<math>'; + $math_content[$marker] = '<math>'.$content.'</math>'; } - } else { - $math_content[$marker] = '<math>'.$content.'</math>'; } } @@ -470,11 +466,10 @@ class Parser */ function unstrip( $text, &$state ) { # Must expand in reverse order, otherwise nested tags will be corrupted - $contentDict = end( $state ); - for ( $contentDict = end( $state ); $contentDict !== false; $contentDict = prev( $state ) ) { - if( key($state) != 'nowiki' && key($state) != 'html') { - for ( $content = end( $contentDict ); $content !== false; $content = prev( $contentDict ) ) { - $text = str_replace( key( $contentDict ), $content, $text ); + foreach( array_reverse( $state, true ) as $tag => $contentDict ) { + if( $tag != 'nowiki' && $tag != 'html' ) { + foreach( array_reverse( $contentDict, true ) as $uniq => $content ) { + $text = str_replace( $uniq, $content, $text ); } } } @@ -511,7 +506,7 @@ class Parser * @access private */ function insertStripItem( $text, &$state ) { - $rnd = UNIQ_PREFIX . '-item' . Parser::getRandomString(); + $rnd = $this->mUniqPrefix . '-item' . Parser::getRandomString(); if ( !$state ) { $state = array( 'html' => array(), @@ -650,8 +645,11 @@ class Parser $fc = substr ( $x , 0 , 1 ) ; if ( preg_match( '/^(:*)\{\|(.*)$/', $x, $matches ) ) { $indent_level = strlen( $matches[1] ); + + $attributes = $this->unstripForHTML( $matches[2] ); + $t[$k] = str_repeat( '<dl><dd>', $indent_level ) . - '<table' . Sanitizer::fixTagAttributes ( $matches[2], 'table' ) . '>' ; + '<table' . Sanitizer::fixTagAttributes ( $attributes, 'table' ) . '>' ; array_push ( $td , false ) ; array_push ( $ltd , '' ) ; array_push ( $tr , false ) ; @@ -678,7 +676,8 @@ class Parser array_push ( $tr , false ) ; array_push ( $td , false ) ; array_push ( $ltd , '' ) ; - array_push ( $ltr , Sanitizer::fixTagAttributes ( $x, 'tr' ) ) ; + $attributes = $this->unstripForHTML( $x ); + array_push ( $ltr , Sanitizer::fixTagAttributes ( $attributes, 'tr' ) ) ; } else if ( '|' == $fc || '!' == $fc || '|+' == substr ( $x , 0 , 2 ) ) { # Caption # $x is a table row @@ -720,7 +719,10 @@ class Parser } if ( count ( $y ) == 1 ) $y = "{$z}<{$l}>{$y[0]}" ; - else $y = $y = "{$z}<{$l}".Sanitizer::fixTagAttributes($y[0], $l).">{$y[1]}" ; + else { + $attributes = $this->unstripForHTML( $y[0] ); + $y = "{$z}<{$l}".Sanitizer::fixTagAttributes($attributes, $l).">{$y[1]}" ; + } $t[$k] .= $y ; array_push ( $td , true ) ; } @@ -753,7 +755,11 @@ class Parser $fname = 'Parser::internalParse'; wfProfileIn( $fname ); - $text = Sanitizer::removeHTMLtags( $text, array( &$this, 'replaceVariables' ) ); + # Remove <noinclude> tags and <includeonly> sections + $text = strtr( $text, array( '<noinclude>' => '', '</noinclude>' => '') ); + $text = preg_replace( '/<includeonly>.*?<\/includeonly>/s', '', $text ); + + $text = Sanitizer::removeHTMLtags( $text, array( &$this, 'attributeStripCallback' ) ); $text = $this->replaceVariables( $text, $args ); $text = preg_replace( '/(^|\n)-----*/', '\\1<hr />', $text ); @@ -769,7 +775,7 @@ class Parser # replaceInternalLinks may sometimes leave behind # absolute URLs, which have to be masked to hide them from replaceExternalLinks - $text = str_replace("http-noparse://","http://",$text); + $text = str_replace($this->mUniqPrefix."NOPARSE", "", $text); $text = $this->doMagicLinks( $text ); $text = $this->doTableStuff( $text ); @@ -1225,7 +1231,6 @@ class Parser if ( $useLinkPrefixExtension ) { if ( preg_match( $e2, $s, $m ) ) { $first_prefix = $m[2]; - $s = $m[1]; } else { $first_prefix = false; } @@ -1308,7 +1313,7 @@ class Parser $link = substr($link, 1); } - $nt =& Title::newFromText( $this->unstripNoWiki($link, $this->mStripState) ); + $nt = Title::newFromText( $this->unstripNoWiki($link, $this->mStripState) ); if( !$nt ) { $s .= $prefix . '[[' . $line; continue; @@ -1329,7 +1334,8 @@ class Parser $found = false; while (isset ($a[$k+1]) ) { #look at the next 'line' to see if we can close it there - $next_line = array_shift(array_splice( $a, $k + 1, 1) ); + $spliced = array_splice( $a, $k + 1, 1 ); + $next_line = array_shift( $spliced ); if( preg_match("/^(.*?]].*?)]](.*)$/sD", $next_line, $m) ) { # the first ]] closes the inner link, the second the image $found = true; @@ -1385,7 +1391,7 @@ class Parser $text = $this->replaceInternalLinks($text); # cloak any absolute URLs inside the image markup, so replaceExternalLinks() won't touch them - $s .= $prefix . str_replace('http://', 'http-noparse://', $this->makeImage( $nt, $text ) ) . $trail; + $s .= $prefix . preg_replace( "/\b($wgUrlProtocols)/", "{$this->mUniqPrefix}NOPARSE$1", $this->makeImage( $nt, $text) ) . $trail; $wgLinkCache->addImageLinkObj( $nt ); wfProfileOut( "$fname-image" ); @@ -1437,7 +1443,9 @@ class Parser # Special and Media are pseudo-namespaces; no pages actually exist in them if( $ns == NS_MEDIA ) { - $s .= $prefix . $sk->makeMediaLinkObj( $nt, $text, true ) . $trail; + $link = $sk->makeMediaLinkObj( $nt, $text ); + # Cloak with NOPARSE to avoid replacement in replaceExternalLinks + $s .= $prefix . str_replace( 'http://', "http{$this->mUniqPrefix}NOPARSE://", $link ) . $trail; $wgLinkCache->addImageLinkObj( $nt ); continue; } elseif( $ns == NS_SPECIAL ) { @@ -1743,12 +1751,11 @@ class Parser if( 0 == $prefixLength ) { wfProfileIn( "$fname-paragraph" ); # No prefix (not in list)--go to paragraph mode - $uniq_prefix = UNIQ_PREFIX; // XXX: use a stack for nestable elements like span, table and div $openmatch = preg_match('/(<table|<blockquote|<h1|<h2|<h3|<h4|<h5|<h6|<pre|<tr|<p|<ul|<li|<\\/tr|<\\/td|<\\/th)/iS', $t ); $closematch = preg_match( '/(<\\/table|<\\/blockquote|<\\/h1|<\\/h2|<\\/h3|<\\/h4|<\\/h5|<\\/h6|'. - '<td|<th|<div|<\\/div|<hr|<\\/pre|<\\/p|'.$uniq_prefix.'-pre|<\\/li|<\\/ul)/iS', $t ); + '<td|<th|<div|<\\/div|<hr|<\\/pre|<\\/p|'.$this->mUniqPrefix.'-pre|<\\/li|<\\/ul)/iS', $t ); if ( $openmatch or $closematch ) { $paragraphStack = false; $output .= $this->closeParagraph(); @@ -2279,6 +2286,10 @@ class Parser $this->mTemplatePath[$part1] = 1; if( $this->mOutputType == OT_HTML ) { + # Remove <noinclude> sections and <includeonly> tags + $text = preg_replace( '/<noinclude>.*?<\/noinclude>/s', '', $text ); + $text = strtr( $text, array( '<includeonly>' => '' , '</includeonly>' => '' ) ); + # Strip <nowiki>, <pre>, etc. $text = $this->strip( $text, $this->mStripState ); $text = Sanitizer::removeHTMLtags( $text, array( &$this, 'replaceVariables' ), $assocArgs ); } @@ -3289,6 +3300,34 @@ class Parser $sk =& $this->mOptions->getSkin(); return $sk->makeImageLinkObj( $nt, $caption, $alt, $align, $width, $height, $framed, $thumb, $manual_thumb ); } + + /** + * Set a flag in the output object indicating that the content is dynamic and + * shouldn't be cached. + */ + function disableCache() { + $this->mOutput->mCacheTime = -1; + } + + /** + * Callback from the Sanitizer for expanding items found in HTML attribute + * values, so they can be safely tested and escaped. + * @param string $text + * @param array $args + * @return string + * @access private + */ + function attributeStripCallback( &$text, $args ) { + $text = $this->replaceVariables( $text, $args ); + $text = $this->unstripForHTML( $text ); + return $text; + } + + function unstripForHTML( $text ) { + $text = $this->unstrip( $text, $this->mStripState ); + $text = $this->unstripNoWiki( $text, $this->mStripState ); + return $text; + } } /** diff --git a/includes/Profiling.php b/includes/Profiling.php index 770f39fedd3b..2fe5adab3b75 100755 --- a/includes/Profiling.php +++ b/includes/Profiling.php @@ -245,10 +245,12 @@ class Profiler { $subcalls = $this->calltreeCount($this->mStack, $index); if (!preg_match('/^-overhead/', $fname)) { - # Adjust for profiling overhead - $elapsed -= $overheadInternal; - $elapsed -= ($subcalls * $overheadTotal); - $memory -= ($subcalls * $overheadMemory); + # Adjust for profiling overhead (except special values with elapsed=0 + if ( $elapsed ) { + $elapsed -= $overheadInternal; + $elapsed -= ($subcalls * $overheadTotal); + $memory -= ($subcalls * $overheadMemory); + } } if (!array_key_exists($fname, $this->mCollated)) { diff --git a/includes/QueryPage.php b/includes/QueryPage.php index 8c6e110364dd..6ed0de439dcf 100644 --- a/includes/QueryPage.php +++ b/includes/QueryPage.php @@ -29,6 +29,7 @@ $wgQueryPages = array( array( 'UncategorizedPagesPage', 'Uncategorizedpages'), array( 'UnusedimagesPage', 'Unusedimages' ), array( 'WantedPagesPage', 'Wantedpages' ), + array( 'MostlinkedPage', 'Mostlinked' ), ); global $wgDisableCounters; diff --git a/includes/Sanitizer.php b/includes/Sanitizer.php index b9b3b50212ac..3816b9de03c7 100644 --- a/includes/Sanitizer.php +++ b/includes/Sanitizer.php @@ -554,11 +554,22 @@ class Sanitizer { # Strip javascript "expression" from stylesheets. # http://msdn.microsoft.com/workshop/author/dhtml/overview/recalc.asp - if( $attribute == 'style' && preg_match( - '/(expression|tps*:\/\/|url\\s*\().*/is', - Sanitizer::decodeCharReferences( $value ) ) ) { - # haxx0r - continue; + if( $attribute == 'style' ) { + $stripped = Sanitizer::decodeCharReferences( $value ); + + // Remove any comments; IE gets token splitting wrong + $stripped = preg_replace( '!/\\*.*?\\*/!S', ' ', $stripped ); + $value = htmlspecialchars( $stripped ); + + // ... and continue checks + $stripped = preg_replace( '!\\\\([0-9A-Fa-f]{1,6})[ \\n\\r\\t\\f]?!e', + 'codepointToUtf8(hexdec("$1"))', $stripped ); + $stripped = str_replace( '\\', '', $stripped ); + if( preg_match( '/(expression|tps*:\/\/|url\\s*\().*/is', + $stripped ) ) { + # haxx0r + continue; + } } # Templates and links may be expanded in later parsing, @@ -571,9 +582,12 @@ class Sanitizer { 'RFC' => 'RFC', 'PMID' => 'PMID', ) ); - $value = preg_replace( - '/(' . $wgUrlProtocols . '):/', - '\\1:', $value ); + + # Stupid hack + $value = preg_replace_callback( + '/(' . $wgUrlProtocols . ')/', + array( 'Sanitizer', 'armorLinksCallback' ), + $value ); // If this attribute was previously set, override it. // Output should only have one attribute of each name. @@ -587,6 +601,16 @@ class Sanitizer { } /** + * Regex replace callback for armoring links against further processing. + * @param array $matches + * @return string + * @access private + */ + function armorLinksCallback( $matches ) { + return str_replace( ':', ':', $matches[1] ); + } + + /** * Return an associative array of attribute names and values from * a partial tag string. Attribute names are forces to lowercase, * character references are decoded to UTF-8 text. diff --git a/includes/SearchMySQL.php b/includes/SearchMySQL.php index 304c714050e3..bf90fdb721e0 100644 --- a/includes/SearchMySQL.php +++ b/includes/SearchMySQL.php @@ -61,9 +61,9 @@ class SearchMySQL extends SearchEngine { */ function queryRedirect() { if( $this->showRedirects ) { - return 'AND cur_is_redirect=0'; - } else { return ''; + } else { + return 'AND page_is_redirect=0'; } } diff --git a/includes/SearchMySQL4.php b/includes/SearchMySQL4.php index 76025c4c6a6b..256579f1c283 100644 --- a/includes/SearchMySQL4.php +++ b/includes/SearchMySQL4.php @@ -64,7 +64,7 @@ class SearchMySQL4 extends SearchMySQL { wfDebug( "Would search with '$searchon'\n" ); wfDebug( "Match with /\b" . implode( '\b|\b', $this->searchTerms ) . "\b/\n" ); } else { - wfDebug( "Can't understand search query '{$this->filteredText}'\n" ); + wfDebug( "Can't understand search query '{$filteredText}'\n" ); } $searchon = $this->db->strencode( $searchon ); diff --git a/includes/Setup.php b/includes/Setup.php index 1a580c163d8b..1904c3af3055 100644 --- a/includes/Setup.php +++ b/includes/Setup.php @@ -152,7 +152,7 @@ wfProfileIn( $fname.'-language1' ); require_once( "$IP/languages/Language.php" ); -function setupLangObj(&$langclass) { +function setupLangObj($langclass) { global $IP; if( ! class_exists( $langclass ) ) { @@ -206,7 +206,11 @@ if( $wgCommandLineMode ) { # Prevent loading User settings from the DB. $wgUser->setLoaded( true ); } else { - $wgUser = User::loadFromSession(); + $wgUser = null; + wfRunHooks('AutoAuthenticate',array(&$wgUser)); + if ($wgUser === null) { + $wgUser = User::loadFromSession(); + } } wfProfileOut( $fname.'-User' ); @@ -217,7 +221,7 @@ $wgLanguageCode = $wgRequest->getText('uselang', ''); if ($wgLanguageCode == '') $wgLanguageCode = $wgUser->getOption('language'); # Validate $wgLanguageCode, which will soon be sent to an eval() -if( empty( $wgLanguageCode ) || preg_match( '/^[^a-z-]*$/', $wgLanguageCode ) ) { +if( empty( $wgLanguageCode ) || !preg_match( '/^[a-z]+(-[a-z]+)?$/', $wgLanguageCode ) ) { $wgLanguageCode = $wgContLanguageCode; } diff --git a/includes/Skin.php b/includes/Skin.php index dc1fa4a6bcc4..a84b3f3c6962 100644 --- a/includes/Skin.php +++ b/includes/Skin.php @@ -436,22 +436,30 @@ END; # get a big array of the parents tree $parenttree = $wgTitle->getParentCategoryTree(); - # Skin object passed by reference cause it can not be - # accessed under the method subfunction walkThrough. - $s .= Skin::drawCategoryBrowser($parenttree, $this); + # accessed under the method subfunction drawCategoryBrowser + $tempout = explode("\n", Skin::drawCategoryBrowser($parenttree, $this) ); + # Clean out bogus first entry and sort them + unset($tempout[0]); + asort($tempout); + # Output one per line + $s .= implode("<br />\n", $tempout); } return $s; } - # Render the array as a serie of links - function drawCategoryBrowser ($tree, &$skin) { + /** Render the array as a serie of links. + * @param array $tree Categories tree returned by Title::getParentCategoryTree + * @param object &skin Skin passed by reference + * @return string separated by >, terminate with "\n" + */ + function drawCategoryBrowser($tree, &$skin) { $return = ''; foreach ($tree as $element => $parent) { if (empty($parent)) { # element start a new list - $return .= '<br />'; + $return .= "\n"; } else { # grab the others elements $return .= Skin::drawCategoryBrowser($parent, $skin) . ' > '; @@ -555,7 +563,7 @@ END; return wfMsg( 'thisisdeleted', $this->makeKnownLink( $wgContLang->SpecialPage( 'Undelete/' . $wgTitle->getPrefixedDBkey() ), - wfMsg( 'restorelink', $n ) ) ); + wfMsg( 'restorelink' . ($n == 1 ? '1' : ''), $n ) ) ); } return ''; } @@ -1316,8 +1324,12 @@ END; $link = wfMsgForContent( $line[0] ); if ($link == '-') continue; + if (wfNoMsg($line[1], $text = wfMsg($line[1]))) + $text = $line[1]; + if (wfNoMsg($line[0], $link)) + $link = $line[0]; $bar[$heading][] = array( - 'text' => wfMsg( $line[1] ), + 'text' => $text, 'href' => $this->makeInternalOrExternalUrl( $link ), 'id' => 'n-' . strtr($line[1], ' ', '-'), ); diff --git a/includes/SkinTemplate.php b/includes/SkinTemplate.php index 0cd2963658cf..752ea26d4b0c 100644 --- a/includes/SkinTemplate.php +++ b/includes/SkinTemplate.php @@ -225,6 +225,7 @@ class SkinTemplate extends Skin { $tpl->setRef( 'jsmimetype', $wgJsMimeType ); $tpl->setRef( 'charset', $wgOutputEncoding ); $tpl->set( 'headlinks', $out->getHeadLinks() ); + $tpl->set('headscripts', $out->getScript() ); $tpl->setRef( 'wgScript', $wgScript ); $tpl->setRef( 'skinname', $this->skinname ); $tpl->setRef( 'stylename', $this->stylename ); @@ -362,10 +363,15 @@ class SkinTemplate extends Skin { if ( !$wgHideInterlanguageLinks ) { foreach( $wgOut->getLanguageLinks() as $l ) { + $tmp = explode( ':', $l, 2 ); + $class = 'interwiki-' . $tmp[0]; + unset($tmp); $nt = Title::newFromText( $l ); - $language_urls[] = array('href' => $nt->getFullURL(), - 'text' => ($wgContLang->getLanguageName( $nt->getInterwiki()) != ''?$wgContLang->getLanguageName( $nt->getInterwiki()) : $l), - 'class' => $wgContLang->isRTL() ? 'rtl' : 'ltr'); + $language_urls[] = array( + 'href' => $nt->getFullURL(), + 'text' => ($wgContLang->getLanguageName( $nt->getInterwiki()) != ''?$wgContLang->getLanguageName( $nt->getInterwiki()) : $l), + 'class' => $class + ); } } if(count($language_urls)) { @@ -615,14 +621,12 @@ class SkinTemplate extends Skin { 'href' => $this->mTitle->getLocalUrl( 'action=delete' ) ); } - if ( $wgUser->isLoggedIn() ) { - if ( $this->mTitle->userCanMove()) { - $content_actions['move'] = array( - 'class' => ($this->mTitle->getDbKey() == 'Movepage' and $this->mTitle->getNamespace == NS_SPECIAL) ? 'selected' : false, - 'text' => wfMsg('move'), - 'href' => $this->makeSpecialUrl("Movepage/$this->thispage" ) - ); - } + if ( $this->mTitle->userCanMove()) { + $content_actions['move'] = array( + 'class' => ($this->mTitle->getDbKey() == 'Movepage' and $this->mTitle->getNamespace == NS_SPECIAL) ? 'selected' : false, + 'text' => wfMsg('move'), + 'href' => $this->makeSpecialUrl("Movepage/$this->thispage" ) + ); } } else { //article doesn't exist or is deleted @@ -638,7 +642,7 @@ class SkinTemplate extends Skin { } wfProfileOut( "$fname-live" ); - if( $wgUser->isLoggedIn() and $action != 'submit' ) { + if( $this->loggedin ) { if( !$this->mTitle->userIsWatching()) { $content_actions['watch'] = array( 'class' => ($action == 'watch' or $action == 'unwatch') ? 'selected' : false, @@ -711,6 +715,8 @@ class SkinTemplate extends Skin { } } + wfRunHooks( 'SkinTemplateContentActions', array(&$content_actions) ); + wfProfileOut( $fname ); return $content_actions; } diff --git a/includes/SpecialAllmessages.php b/includes/SpecialAllmessages.php index 56e128f37a87..91975a8c891b 100644 --- a/includes/SpecialAllmessages.php +++ b/includes/SpecialAllmessages.php @@ -96,12 +96,13 @@ function makeHTMLText( $messages ) { $mwnspace = $wgLang->getNsText( NS_MEDIAWIKI ); $mwtalk = $wgLang->getNsText( NS_MEDIAWIKI_TALK ); $txt = " - - <table border='1' cellspacing='0' width='100%'> - <tr bgcolor='#b2b2ff'> - <th>" . wfMsg('allmessagesname') . "</th> - <th>" . wfMsg('allmessagesdefault') . "</th> - <th>" . wfMsg('allmessagescurrent') . "</th> +<table border='1' cellspacing='0' width='100%' id='allmessagestable'> + <tr > + <th rowspan='2'>" . wfMsgHtml('allmessagesname') . "</th> + <th>" . wfMsgHtml('allmessagesdefault') . "</th> + </tr> + <tr> + <th>" . wfMsgHtml('allmessagescurrent') . "</th> </tr>"; wfProfileIn( "$fname-check" ); @@ -132,7 +133,7 @@ function makeHTMLText( $messages ) { $titleObj =& Title::makeTitle( NS_MEDIAWIKI, $title ); $talkPage =& Title::makeTitle( NS_MEDIAWIKI_TALK, $title ); - $colorIt = ($m['statmsg'] == $m['msg']) ? " bgcolor=\"#f0f0ff\"" : " bgcolor=\"#ffe2e2\""; + $changed = ($m['statmsg'] != $m['msg']); $message = htmlspecialchars( $m['statmsg'] ); $mw = htmlspecialchars( $m['msg'] ); @@ -149,15 +150,32 @@ function makeHTMLText( $messages ) { $talkLink = $sk->makeBrokenLinkObj( $talkPage, htmlspecialchars( $talk ) ); } - $txt .= - "<tr$colorIt><td> - $pageLink<br /> - $talkLink + if($changed) { + + $txt .= + "<tr class='orig'> + <td rowspan='2'> + $pageLink<br />$talkLink </td><td> - $message +$message + </td> + </tr><tr class='new'> + <td> +$mw + </td> + </tr>"; + } else { + + $txt .= + "<tr class='def'> + <td> + $pageLink<br />$talkLink </td><td> - $mw - </td></tr>"; +$mw + </td> + </tr>"; + + } } $txt .= "</table>"; wfProfileOut( "$fname-output" ); diff --git a/includes/SpecialAllpages.php b/includes/SpecialAllpages.php index 26e7434abaed..5ba83c7e90b7 100644 --- a/includes/SpecialAllpages.php +++ b/includes/SpecialAllpages.php @@ -45,31 +45,22 @@ function indexNamespaceForm ( $namespace = NS_MAIN, $from = '' ) { global $wgContLang, $wgScript; $t = Title::makeTitle( NS_SPECIAL, "Allpages" ); - $namespaceselect = "<select name='namespace' id='nsselectbox'>"; - $arr = $wgContLang->getFormattedNamespaces(); - foreach ( $arr as $ns => $name ) { - if ($ns < NS_MAIN) - continue; - $n = $ns == 0 ? wfMsg ( 'blanknamespace' ) : $name; - $sel = $ns == $namespace ? ' selected="selected"' : ''; - $namespaceselect .= "<option value='$ns'$sel>$n</option>"; - } - $namespaceselect .= '</select>'; + $namespaceselect = HTMLnamespaceselector($namespace, null); $frombox = "<input type='text' size='20' name='from' id='nsfrom' value=\"" . htmlspecialchars ( $from ) . '"/>'; - $submitbutton = '<input type="submit" value="' . wfMsg( 'allpagessubmit' ) . '" />'; + $submitbutton = '<input type="submit" value="' . wfMsgHtml( 'allpagessubmit' ) . '" />'; - $out = "<div class='namespaceselector'><form method='get' action='{$wgScript}'>"; + $out = "<div class='namespaceoptions'><form method='get' action='{$wgScript}'>"; $out .= '<input type="hidden" name="title" value="'.$t->getPrefixedText().'" />'; $out .= " <table id='nsselect' class='allpages'> <tr> - <td align='right'>" . wfMsg('allpagesfrom') . "</td> + <td align='right'>" . wfMsgHtml('allpagesfrom') . "</td> <td align='left'><label for='nsfrom'>$frombox</label></td> </tr> <tr> - <td align='right'><label for='nsselectbox'>" . wfMsg('namespace') . "</label></td> + <td align='right'><label for='namespace'>" . wfMsgHtml('namespace') . "</label></td> <td align='left'> $namespaceselect $submitbutton </td> diff --git a/includes/SpecialBlockip.php b/includes/SpecialBlockip.php index 8d7c3bb61713..28ef283d581a 100644 --- a/includes/SpecialBlockip.php +++ b/includes/SpecialBlockip.php @@ -67,7 +67,7 @@ class IPBlockForm { $action = $titleObj->escapeLocalURL( "action=submit" ); if ( "" != $err ) { - $wgOut->setSubtitle( wfMsg( 'formerror' ) ); + $wgOut->setSubtitle( wfMsgHtml( 'formerror' ) ); $wgOut->addHTML( "<p class='error'>{$err}</p>\n" ); } diff --git a/includes/SpecialBrokenRedirects.php b/includes/SpecialBrokenRedirects.php index 41011209dab1..4c3a8ed0c12f 100644 --- a/includes/SpecialBrokenRedirects.php +++ b/includes/SpecialBrokenRedirects.php @@ -38,7 +38,7 @@ class BrokenRedirectsPage extends PageQueryPage { p1.page_title AS title, pl_namespace, pl_title - FROM $pagelinks, $page AS p1 + FROM ($pagelinks, $page AS p1) LEFT JOIN $page AS p2 ON pl_namespace=p2.page_namespace AND pl_title=p2.page_title WHERE p1.page_is_redirect=1 diff --git a/includes/SpecialContributions.php b/includes/SpecialContributions.php index 35afe173eca5..ffbfe91c733c 100644 --- a/includes/SpecialContributions.php +++ b/includes/SpecialContributions.php @@ -169,7 +169,7 @@ function wfSpecialContributions( $par = null ) { } $nt =& Title::makeTitle(NS_USER, $nt->getDBkey()); - $limit = min($wgRequest->getInt('limit', 50), 500); + list( $limit, $offset) = wfCheckLimits(); $offset = $wgRequest->getVal('offset'); /* Offset must be an integral. */ if (!strlen($offset) || !preg_match("/^[0-9]+$/", $offset)) @@ -226,6 +226,8 @@ function wfSpecialContributions( $par = null ) { $wgOut->setSubtitle( wfMsgHtml( 'contribsub', $ul ) ); + wfRunHooks('SpecialContributionsBeforeMainOutput', $id ); + $arr = $wgContLang->getFormattedNamespaces(); $nsform = "<form method='get' action=\"$wgScript\">\n"; $nsform .= wfElement("input", array( @@ -245,19 +247,10 @@ function wfSpecialContributions( $par = null ) { "type" => "hidden", "value" => $target)); $nsform .= "<p>"; - $nsform .= htmlspecialchars(wfMsg('namespace')) . " <select name='namespace'>\n"; - foreach (array("" => wfMsg('contributionsall')) + $arr as $nsn => $name) { - if ($nsn < 0) - continue; - $name = $nsn!==0 ? $name : wfMsg('blanknamespace'); - $nsform .= ("$nsn" == "$ns") ? - wfElement("option", - array("value" => $nsn, "selected" => "selected"), - $name) - : - wfElement("option", array("value" => $nsn), $name); - } - $nsform .= "</select>\n"; + $nsform .= wfMsgHtml('namespace'); + + $nsform .= HTMLnamespaceselector($ns, ''); + $nsform .= wfElement("input", array( "type" => "submit", "value" => wfMsg('allpagessubmit'))); @@ -355,7 +348,7 @@ function ucListEdit( $sk, $row ) { if( $row->rev_id == $row->page_latest ) { $topmarktext .= '<strong>' . $messages['uctop'] . '</strong>'; if( !$row->page_is_new ) { - $difftext .= $sk->makeKnownLinkObj( $page, '(' . $messages['diff'] . ')', 'diff=0' ); + $difftext .= '(' . $sk->makeKnownLinkObj( $page, $messages['diff'], 'diff=0' ) . ')'; } else { $difftext .= $messages['newarticle']; } @@ -373,7 +366,7 @@ function ucListEdit( $sk, $row ) { if( $row->rev_deleted && !$wgUser->isAllowed( 'undelete' ) ) { $difftext = '(' . $messages['diff'] . ')'; } else { - $difftext = $sk->makeKnownLinkObj( $page, '(' . $messages['diff'].')', 'diff=prev&oldid='.$row->rev_id ); + $difftext = '(' . $sk->makeKnownLinkObj( $page, $messages['diff'], 'diff=prev&oldid='.$row->rev_id ) . ')'; } $histlink='('.$sk->makeKnownLinkObj( $page, $messages['hist'], 'action=history' ) . ')'; diff --git a/includes/SpecialImport.php b/includes/SpecialImport.php index 07b2df54afa6..728f80c16774 100644 --- a/includes/SpecialImport.php +++ b/includes/SpecialImport.php @@ -128,6 +128,7 @@ class WikiRevision { var $user_text = ""; var $text = ""; var $comment = ""; + var $minor = false; function setTitle( $text ) { $this->title = Title::newFromText( $text ); @@ -154,6 +155,10 @@ class WikiRevision { $this->comment = $text; } + function setMinor( $minor ) { + $this->minor = (bool)$minor; + } + function getTitle() { return $this->title; } @@ -173,6 +178,10 @@ class WikiRevision { function getComment() { return $this->comment; } + + function getMinor() { + return $this->minor; + } function importOldRevision() { $fname = "WikiImporter::importOldRevision"; @@ -188,6 +197,10 @@ class WikiRevision { $userText = $this->getUser(); } + // avoid memory leak...? + global $wgLinkCache; + $wgLinkCache->clear(); + $article = new Article( $this->title ); $pageId = $article->getId(); if( $pageId == 0 ) { @@ -211,7 +224,7 @@ class WikiRevision { 'user' => $userId, 'user_text' => $userText, 'timestamp' => $this->timestamp, - 'minor_edit' => 0 + 'minor_edit' => $this->minor, ) ); $revId = $revision->insertOn( $dbw ); $article->updateIfNewerOn( $dbw, $revision ); @@ -239,6 +252,7 @@ class WikiImporter { function throwXmlError( $err ) { $this->debug( "FAILURE: $err" ); + wfDebug( "WikiImporter XML error: $err\n" ); } # -------------- @@ -253,14 +267,17 @@ class WikiImporter { # case folding violates XML standard, turn it off xml_parser_set_option( $parser, XML_OPTION_CASE_FOLDING, false ); - xml_set_object( $parser, &$this ); + xml_set_object( $parser, $this ); xml_set_element_handler( $parser, "in_start", "" ); + $offset = 0; // for context extraction on error reporting do { $chunk = $this->mSource->readChunk(); if( !xml_parse( $parser, $chunk, $this->mSource->atEnd() ) ) { - return new WikiXmlError( $parser ); + wfDebug( "WikiImporter::doImport encountered XML parsing error\n" ); + return new WikiXmlError( $parser, 'XML import parse failure', $chunk, $offset ); } + $offset += strlen( $chunk ); } while( $chunk !== false && !$this->mSource->atEnd() ); xml_parser_free( $parser ); @@ -380,6 +397,7 @@ class WikiImporter { $this->debug( "in_siteinfo $name" ); switch( $name ) { case "sitename": + case "base": case "generator": case "case": case "namespaces": @@ -466,6 +484,9 @@ class WikiImporter { case "comment": $this->workRevision->setComment( $this->appenddata ); break; + case "minor": + $this->workRevision->setMinor( true ); + break; default: $this->debug( "Bad append: {$this->appendfield}" ); } @@ -479,6 +500,7 @@ class WikiImporter { case "id": case "timestamp": case "comment": + case "minor": case "text": $this->parenttag = "revision"; $this->appendfield = $name; @@ -500,9 +522,8 @@ class WikiImporter { } xml_set_element_handler( $parser, "in_page", "out_page" ); - $out = call_user_func( $this->mRevisionCallback, - &$this->workRevision, - &$this ); + $out = call_user_func_array( $this->mRevisionCallback, + array( &$this->workRevision, &$this ) ); if( !empty( $out ) ) { global $wgOut; $wgOut->addHTML( "<li>" . $out . "</li>\n" ); @@ -514,6 +535,7 @@ class WikiImporter { switch( $name ) { case "username": case "ip": + case "id": $this->parenttag = "contributor"; $this->appendfield = $name; xml_set_element_handler( $parser, "in_nothing", "out_append" ); diff --git a/includes/SpecialIpblocklist.php b/includes/SpecialIpblocklist.php index 492f78db26c9..609bda25631e 100644 --- a/includes/SpecialIpblocklist.php +++ b/includes/SpecialIpblocklist.php @@ -49,20 +49,20 @@ class IPUnblockForm { function showForm( $err ) { - global $wgOut, $wgUser, $wgLang; + global $wgOut, $wgUser, $wgLang, $wgSysopUserBans; $wgOut->setPagetitle( wfMsg( "unblockip" ) ); $wgOut->addWikiText( wfMsg( "unblockiptext" ) ); - $ipa = wfMsg( "ipaddress" ); - $ipr = wfMsg( "ipbreason" ); - $ipus = htmlspecialchars( wfMsg( "ipusubmit" ) ); + $ipa = wfMsgHtml( $wgSysopUserBans ? 'ipadressorusername' : 'ipaddress' ); + $ipr = wfMsgHtml( 'ipbreason' ); + $ipus = wfMsgHtml( 'ipusubmit' ); $titleObj = Title::makeTitle( NS_SPECIAL, "Ipblocklist" ); $action = $titleObj->escapeLocalURL( "action=submit" ); if ( "" != $err ) { $wgOut->setSubtitle( wfMsg( "formerror" ) ); - $wgOut->addHTML( "<p class='error'>{$err}</p>\n" ); + $wgOut->addWikitext( "<span class='error'>{$err}</span>\n" ); } $token = htmlspecialchars( $wgUser->editToken() ); @@ -104,7 +104,7 @@ class IPUnblockForm { } else { $block->mAddress = $this->ip; } - + # Delete block (if it exists) # We should probably check for errors rather than just declaring success $block->delete(); @@ -127,7 +127,9 @@ class IPUnblockForm { $wgOut->setSubtitle( $msg ); } $wgOut->addHTML( "<ul>" ); - Block::enumBlocks( "wfAddRow", 0 ); + // FIXME hack to solve #bug 1487 + if(!Block::enumBlocks( "wfAddRow", 0 )) + $wgOut->addHTML( '<li>'.wfMsg( 'ipblocklistempty' ).'</li>' ); $wgOut->addHTML( "</ul>\n" ); } } @@ -148,9 +150,9 @@ function wfAddRow( $block, $tag ) { $formattedTime = $wgLang->timeanddate( $block->mTimestamp, true ); if ( $block->mExpiry === "" ) { - $formattedExpiry = "indefinite"; + $formattedExpiry = wfMsgHtml('infiniteblock'); } else { - $formattedExpiry = $wgLang->timeanddate( $block->mExpiry, true ); + $formattedExpiry = wfMsgHtml('expiringblock', $wgLang->timeanddate( $block->mExpiry, true ) ); } $line = wfMsg( "blocklistline", $formattedTime, $ulink, $addr, $formattedExpiry ); @@ -159,17 +161,12 @@ function wfAddRow( $block, $tag ) { if ( !$block->mAuto ) { $titleObj = Title::makeTitle( NS_SPECIAL, "Contributions" ); - $clink = "<a href=\"" . $titleObj->escapeLocalURL( "target={$block->mAddress}" ) . "\">" . - wfMsg( "contribslink" ) . "</a>"; - $wgOut->addHTML( " ({$clink})" ); + $wgOut->addHTML( ' (' . $sk->makeKnownLinkObj($titleObj, wfMsgHtml( 'contribslink' ), "target={$block->mAddress}") . ')' ); } if ( $wgUser->isAllowed('block') ) { $titleObj = Title::makeTitle( NS_SPECIAL, "Ipblocklist" ); - $ublink = "<a href=\"" . - $titleObj->escapeLocalURL( "action=unblock&ip=" . urlencode( $addr ) ) . "\">" . - wfMsg( "unblocklink" ) . "</a>"; - $wgOut->addHTML( " ({$ublink})" ); + $wgOut->addHTML( ' (' . $sk->makeKnownLinkObj($titleObj, wfMsgHtml( 'unblocklink' ), 'action=unblock&ip=' . urlencode( $addr ) ) . ')' ); } $wgOut->addHTML( $sk->commentBlock( $block->mReason ) ); $wgOut->addHTML( "</li>\n" ); diff --git a/includes/SpecialMostlinked.php b/includes/SpecialMostlinked.php new file mode 100644 index 000000000000..008a2d4123be --- /dev/null +++ b/includes/SpecialMostlinked.php @@ -0,0 +1,69 @@ +<?php +/** + * + * @package MediaWiki + * @subpackage SpecialPage + */ + +require_once ( 'QueryPage.php' ) ; + +/** + * + * @package MediaWiki + * @subpackage SpecialPage + */ +class MostlinkedPage extends QueryPage { + + function getName() { + return 'Mostlinked'; + } + + function isExpensive() { + return true; + } + function isSyndicated() { return false; } + + function getSQL() { + $dbr =& wfGetDB( DB_SLAVE ); + extract( $dbr->tableNames( 'pagelinks', 'page' ) ); + return + "SELECT 'Mostlinked' AS type, + pl_namespace AS namespace, + pl_title AS title, + COUNT(*) AS value, + page_namespace + FROM $pagelinks + LEFT JOIN $page ON pl_namespace=page_namespace AND pl_title=page_title + GROUP BY pl_namespace,pl_title + HAVING COUNT(*) > 1"; + } + + function formatResult( $skin, $result ) { + global $wgContLang; + + $nt = Title::makeTitle( $result->namespace, $result->title ); + $text = $wgContLang->convert( $nt->getPrefixedText() ); + if ( is_null( $result->page_namespace ) ) + $plink = $skin->makeBrokenLink( $nt->getPrefixedText(), $text ); + else + $plink = $skin->makeKnownLink( $nt->getPrefixedText(), $text ); + + $nl = wfMsg( "nlinks", $result->value ); + $nlink = $skin->makeKnownLink( $wgContLang->specialPage( "Whatlinkshere" ), $nl, "target=" . $nt->getPrefixedURL() ); + + return "{$plink} ({$nlink})"; + } +} + +/** + * constructor + */ +function wfSpecialMostlinked() { + list( $limit, $offset ) = wfCheckLimits(); + + $wpp = new MostlinkedPage(); + + $wpp->doQuery( $offset, $limit ); +} + +?> diff --git a/includes/SpecialMovepage.php b/includes/SpecialMovepage.php index 70c8a4feda13..b07274079b48 100644 --- a/includes/SpecialMovepage.php +++ b/includes/SpecialMovepage.php @@ -17,7 +17,7 @@ function wfSpecialMovepage( $par = null ) { global $wgUser, $wgOut, $wgRequest, $action, $wgOnlySysopMayMove; # check rights. We don't want newbies to move pages to prevents possible attack - if ( $wgUser->isAnon() or $wgUser->isBlocked() or ($wgOnlySysopMayMove and $wgUser->isNewbie())) { + if ( !$wgUser->isAllowed( 'move' ) or $wgUser->isBlocked() or ($wgOnlySysopMayMove and $wgUser->isNewbie())) { $wgOut->errorpage( "movenologin", "movenologintext" ); return; } @@ -138,9 +138,9 @@ class MovePageForm { </td> </tr> <tr> - <td align='right'>{$movereason}:</td> - <td align='left'> - <input type='text' size='40' name=\"wpReason\" value=\"{$encReason}\" /> + <td align='right' valign='top'><br />{$movereason}:</td> + <td align='left' valign='top'><br /> + <textarea cols='60' rows='2' name='wpReason' id='wpReason'>{$encReason}</textarea> </td> </tr>" ); @@ -148,9 +148,9 @@ class MovePageForm { $wgOut->addHTML( " <tr> <td align='right'> - <input type='checkbox' name=\"wpMovetalk\"{$moveTalkChecked} value=\"1\" /> + <input type='checkbox' id=\"wpMovetalk\" name=\"wpMovetalk\"{$moveTalkChecked} value=\"1\" /> </td> - <td>{$movetalk}</td> + <td><label for=\"wpMovetalk\">{$movetalk}</label></td> </tr>" ); } $wgOut->addHTML( " diff --git a/includes/SpecialNewpages.php b/includes/SpecialNewpages.php index feb4c54105e1..3b24cc2a1df2 100644 --- a/includes/SpecialNewpages.php +++ b/includes/SpecialNewpages.php @@ -62,11 +62,13 @@ class NewPagesPage extends QueryPage { $length = wfMsg( 'nbytes', $wgLang->formatNum( $result->length ) ); if ( $u == 0 ) { # not by a logged-in user - $ul = $ut; - } - else { - $ul = $skin->makeLink( $wgContLang->getNsText(NS_USER) . ":{$ut}", $ut ); + $userPage = Title::makeTitle( NS_SPECIAL, 'Contributions' ); + $linkParams = 'target=' . urlencode( $ut ); + } else { + $userPage = Title::makeTitle( NS_USER, $ut ); + $linkParams = ''; } + $ul = $skin->makeLinkObj( $userPage, htmlspecialchars( $ut ), $linkParams ); $d = $wgLang->timeanddate( $result->timestamp, true ); diff --git a/includes/SpecialPage.php b/includes/SpecialPage.php index 869ccafe1af2..4fbd7b9f5b1d 100644 --- a/includes/SpecialPage.php +++ b/includes/SpecialPage.php @@ -17,11 +17,6 @@ /** - * - */ -global $wgSpecialPages, $wgUser; - -/** * @access private */ $wgSpecialPages = array( @@ -47,6 +42,7 @@ $wgSpecialPages = array( 'Unusedcategories' => new SpecialPage( 'Unusedcategories' ), 'Unusedimages' => new SpecialPage( 'Unusedimages' ), 'Wantedpages' => new SpecialPage( 'Wantedpages' ), + 'Mostlinked' => new SpecialPage( 'Mostlinked' ), 'Shortpages' => new SpecialPage( 'Shortpages' ), 'Longpages' => new SpecialPage( 'Longpages' ), 'Newpages' => new IncludableSpecialPage( 'Newpages' ), @@ -75,21 +71,17 @@ $wgSpecialPages = array( 'Userrights' => new SpecialPage( 'Userrights', 'userrights' ), ); -global $wgUseValidation ; if ( $wgUseValidation ) $wgSpecialPages['Validate'] = new SpecialPage( 'Validate' ); -global $wgDisableCounters; if( !$wgDisableCounters ) { $wgSpecialPages['Popularpages'] = new SpecialPage( 'Popularpages' ); } -global $wgDisableInternalSearch; if( !$wgDisableInternalSearch ) { $wgSpecialPages['Search'] = new UnlistedSpecialPage( 'Search' ); } -global $wgEmailAuthentication; if( $wgEmailAuthentication ) { $wgSpecialPages['Confirmemail'] = new UnlistedSpecialPage( 'Confirmemail' ); } @@ -165,7 +157,7 @@ class SpecialPage * @static * @param string $name */ - function &getPage( $name ) { + function getPage( $name ) { global $wgSpecialPages; if ( array_key_exists( $name, $wgSpecialPages ) ) { return $wgSpecialPages[$name]; @@ -179,7 +171,7 @@ class SpecialPage * @param string $name * @return mixed Title object if the redirect exists, otherwise NULL */ - function &getRedirect( $name ) { + function getRedirect( $name ) { global $wgUser; switch ( $name ) { case 'Mypage': @@ -240,12 +232,12 @@ class SpecialPage $par = $bits[1]; } - $page =& SpecialPage::getPage( $name ); + $page = SpecialPage::getPage( $name ); if ( is_null( $page ) ) { if ( $including ) { return false; } else { - $redir =& SpecialPage::getRedirect( $name ); + $redir = SpecialPage::getRedirect( $name ); if ( isset( $redir ) ) { if ( isset( $par ) ) $wgOut->redirect( $redir->getFullURL() . '/' . $par ); diff --git a/includes/SpecialPreferences.php b/includes/SpecialPreferences.php index 6e99b3f03fe6..bdf9e1e45c98 100644 --- a/includes/SpecialPreferences.php +++ b/includes/SpecialPreferences.php @@ -66,6 +66,7 @@ class PreferencesForm { $this->mAction = $request->getVal( 'action' ); $this->mReset = $request->getCheck( 'wpReset' ); $this->mPosted = $request->wasPosted(); + $this->mSuccess = $request->getCheck( 'success' ); $this->mSaveprefs = $request->getCheck( 'wpSaveprefs' ) && $this->mPosted && @@ -113,7 +114,7 @@ class PreferencesForm { } if ( $this->mReset ) { $this->resetPrefs(); - $this->mainPrefsForm( wfMsg( 'prefsreset' ) ); + $this->mainPrefsForm( 'reset', wfMsg( 'prefsreset' ) ); } else if ( $this->mSaveprefs ) { $this->savePreferences(); } else { @@ -197,26 +198,33 @@ class PreferencesForm { if ( '' != $this->mNewpass ) { if ( $this->mNewpass != $this->mRetypePass ) { - $this->mainPrefsForm( wfMsg( 'badretype' ) ); + $this->mainPrefsForm( 'error', wfMsg( 'badretype' ) ); return; } if ( strlen( $this->mNewpass ) < $wgMinimalPasswordLength ) { - $this->mainPrefsForm( wfMsg( 'passwordtooshort', $wgMinimalPasswordLength ) ); + $this->mainPrefsForm( 'error', wfMsg( 'passwordtooshort', $wgMinimalPasswordLength ) ); return; } if (!$wgUser->checkPassword( $this->mOldpass )) { - $this->mainPrefsForm( wfMsg( 'wrongpassword' ) ); + $this->mainPrefsForm( 'error', wfMsg( 'wrongpassword' ) ); return; } if (!$wgAuth->setPassword( $wgUser, $this->mNewpass )) { - $this->mainPrefsForm( wfMsg( 'externaldberror' ) ); + $this->mainPrefsForm( 'error', wfMsg( 'externaldberror' ) ); return; } $wgUser->setPassword( $this->mNewpass ); } $wgUser->setRealName( $this->mRealName ); + + if( $wgUser->getOption( 'language' ) !== $this->mUserLanguage ) { + $needRedirect = true; + } else { + $needRedirect = false; + } + $wgUser->setOption( 'language', $this->mUserLanguage ); $wgUser->setOption( 'variant', $this->mUserVariant ); $wgUser->setOption( 'nickname', $this->mNick ); @@ -226,7 +234,7 @@ class PreferencesForm { if( $wgUseTeX ) { $wgUser->setOption( 'math', $this->mMath ); } - $wgUser->setOption( 'date', $this->validateDate( $this->mDate, 0, 10 ) ); + $wgUser->setOption( 'date', $this->validateDate( $this->mDate, 0, 20 ) ); $wgUser->setOption( 'searchlimit', $this->validateIntOrNull( $this->mSearch ) ); $wgUser->setOption( 'contextlines', $this->validateIntOrNull( $this->mSearchLines ) ); $wgUser->setOption( 'contextchars', $this->validateIntOrNull( $this->mSearchChars ) ); @@ -259,7 +267,7 @@ class PreferencesForm { $wgUser->setCookies(); $wgUser->saveSettings(); - $error = wfMsg( 'savedprefs' ); + $error = false; if( $wgEnableEmail ) { $newadr = $this->mUserEmail; $oldadr = $wgUser->getEmail(); @@ -274,7 +282,7 @@ class PreferencesForm { # User can come back through the confirmation URL to re-enable email. $result = $wgUser->sendConfirmationMail(); if( WikiError::isError( $result ) ) { - $error = wfMsg( 'mailerror', $result->getMessage() ); + $error = wfMsg( 'mailerror', htmlspecialchars( $result->getMessage() ) ); } else { $error = wfMsg( 'eauthentsent', $wgUser->getName() ); } @@ -289,9 +297,15 @@ class PreferencesForm { } } + if( $needRedirect && $error === false ) { + $title =& Title::makeTitle( NS_SPECIAL, "Preferences" ); + $wgOut->redirect($title->getFullURL('success')); + return; + } + $wgOut->setParserOptions( ParserOptions::newFromUser( $wgUser ) ); $po = ParserOptions::newFromUser( $wgUser ); - $this->mainPrefsForm( $error ); + $this->mainPrefsForm( $error === false ? 'success' : 'error', $error); } /** @@ -378,7 +392,7 @@ class PreferencesForm { $checked = $wgUser->getOption( $tname ) == 1 ? ' checked="checked"' : ''; $trailer = $trailer ? $trailer : ''; return "<div class='toggle'><input type='checkbox' value='1' id=\"$tname\" name=\"wpOp$tname\"$checked />" . - " <span class='toggletext'><label for=\"$tname\">$ttext</label>$trailer</span></div>"; + " <span class='toggletext'><label for=\"$tname\">$ttext</label>$trailer</span></div>\n"; } function getToggles( $items ) { @@ -404,7 +418,7 @@ class PreferencesForm { /** * @access private */ - function mainPrefsForm( $err ) { + function mainPrefsForm( $status , $message = '' ) { global $wgUser, $wgOut, $wgLang, $wgContLang, $wgValidSkinNames; global $wgAllowRealName, $wgImageLimits, $wgThumbLimits; global $wgDisableLangConversion; @@ -417,8 +431,12 @@ class PreferencesForm { $wgOut->setArticleRelated( false ); $wgOut->setRobotpolicy( 'noindex,nofollow' ); - if ( '' != $err ) { - $wgOut->addHTML( "<p class='error'>" . htmlspecialchars( $err ) . "</p>\n" ); + if ( $this->mSuccess || 'success' == $status ) { + $wgOut->addWikitext( '<span class="preferences-save-success">'. wfMsg( 'savedprefs' ) . "</span>\n----" ); + } else if ( 'error' == $status ) { + $wgOut->addWikitext( "<span class='error'>" . $message . "</span>\n----" ); + } else if ( '' != $status ) { + $wgOut->addWikitext( $message . "\n----" ); } $uname = $wgUser->getName(); $uid = $wgUser->getID(); @@ -661,7 +679,7 @@ class PreferencesForm { $imageLimitOptions = null; foreach ( $wgImageLimits as $index => $limits ) { $selected = ($index == $this->mImageSize) ? 'selected="selected"' : ''; - $imageLimitOptions .= "<option value=\"{$index}\" {$selected}>{$limits[0]}x{$limits[1]}</option>\n"; + $imageLimitOptions .= "<option value=\"{$index}\" {$selected}>{$limits[0]}×{$limits[1]}". wfMsgHtml('unit-pixel') ."</option>\n"; } $imageThumbOptions = null; @@ -669,7 +687,7 @@ class PreferencesForm { <div><label>" . wfMsg('thumbsize') . "<select name=\"wpThumbSize\">"); foreach ( $wgThumbLimits as $index => $size ) { $selected = ($index == $this->mThumbSize) ? 'selected="selected"' : ''; - $imageThumbOptions .= "<option value=\"{$index}\" {$selected}>{$size}px</option>\n"; + $imageThumbOptions .= "<option value=\"{$index}\" {$selected}>{$size}". wfMsgHtml('unit-pixel') ."</option>\n"; } $wgOut->addHTML( "{$imageThumbOptions}</select></label></div></fieldset>\n\n"); @@ -754,14 +772,14 @@ class PreferencesForm { # $wgOut->addHTML('<fieldset><legend>' . wfMsg('prefs-misc') . '</legend>'); - $msgUnderline = htmlspecialchars(wfMsg("tog-underline")); - $msgUnderlinenever = htmlspecialchars(wfMsg("underline-never")); - $msgUnderlinealways = htmlspecialchars(wfMsg("underline-always")); - $msgUnderlinedefault = htmlspecialchars(wfMsg("underline-default")); - $uopt = $wgUser->getOption("underline"); - $s0 = $uopt == 0 ? " selected=\"selected\"" : ""; - $s1 = $uopt == 1 ? " selected=\"selected\"" : ""; - $s2 = $uopt == 2 ? " selected=\"selected\"" : ""; + $msgUnderline = htmlspecialchars(wfMsg('tog-underline')); + $msgUnderlinenever = htmlspecialchars(wfMsg('underline-never')); + $msgUnderlinealways = htmlspecialchars(wfMsg('underline-always')); + $msgUnderlinedefault = htmlspecialchars(wfMsg('underline-default')); + $uopt = $wgUser->getOption('underline'); + $s0 = $uopt == 0 ? ' selected="selected"' : ''; + $s1 = $uopt == 1 ? ' selected="selected"' : ''; + $s2 = $uopt == 2 ? ' selected="selected"' : ''; $wgOut->addHTML(" <div class='toggle'><label>$msgUnderline <select name=\"wpOpunderline\"> @@ -769,7 +787,8 @@ class PreferencesForm { <option value=\"1\"$s1>$msgUnderlinealways</option> <option value=\"2\"$s2>$msgUnderlinedefault</option> </select> -</label></div> +</label> +</div> "); foreach ( $togs as $tname ) { if( !array_key_exists( $tname, $this->mUsedToggles ) ) { diff --git a/includes/SpecialRandompage.php b/includes/SpecialRandompage.php index 59836c9fedfe..82d580cb6a6b 100644 --- a/includes/SpecialRandompage.php +++ b/includes/SpecialRandompage.php @@ -51,7 +51,7 @@ function wfSpecialRandompage( $par = NS_MAIN ) { } if( is_null( $title ) ) { # That's not supposed to happen :) - $title =& Title::newFromText( wfMsg( 'mainpage' ) ); + $title = Title::newFromText( wfMsg( 'mainpage' ) ); } $wgOut->reportTime(); # for logfile $wgOut->redirect( $title->getFullUrl() ); diff --git a/includes/SpecialRecentchanges.php b/includes/SpecialRecentchanges.php index 8ad4899d7e4c..75bcda521b46 100644 --- a/includes/SpecialRecentchanges.php +++ b/includes/SpecialRecentchanges.php @@ -131,14 +131,15 @@ function wfSpecialRecentchanges( $par, $specialPage ) { $hidem .= $hidebots ? ' AND rc_bot=0' : ''; $hidem .= $hideliu ? ' AND rc_user=0' : ''; $hidem .= $hidepatrolled ? ' AND rc_patrolled=0' : ''; - $hidem .= is_null( $namespace ) ? '' : ' AND rc_namespace' . ($invert ? '!=' : '=') . $namespace; + $hidem .= is_null( $namespace ) ? '' : ' AND rc_namespace' . ($invert ? '!=' : '=') . $namespace; // This is the big thing! $uid = $wgUser->getID(); + $notifts = ($wgShowUpdatedMarker?",wl_notificationtimestamp":""); // Perform query - $sql2 = "SELECT *" . ($uid ? ",wl_user,wl_notificationtimestamp" : "") . " FROM $recentchanges " . + $sql2 = "SELECT $recentchanges.*" . ($uid ? ",wl_user".$notifts : "") . " FROM $recentchanges " . ($uid ? "LEFT OUTER JOIN $watchlist ON wl_user={$uid} AND wl_title=rc_title AND wl_namespace=rc_namespace " : "") . "WHERE rc_timestamp > '{$cutoff}' {$hidem} " . "ORDER BY rc_timestamp DESC LIMIT {$limit}"; @@ -176,15 +177,15 @@ function wfSpecialRecentchanges( $par, $specialPage ) { // Dump everything here $nondefaults = array(); - appendToArrayIfNotDefault( 'days', $days, $defaults, $nondefaults); - appendToArrayIfNotDefault( 'limit', $limit , $defaults, $nondefaults); - appendToArrayIfNotDefault( 'hideminor', $hideminor, $defaults, $nondefaults); - appendToArrayIfNotDefault( 'hidebots', $hidebots, $defaults, $nondefaults); - appendToArrayIfNotDefault( 'hideliu', $hideliu, $defaults, $nondefaults); - appendToArrayIfNotDefault( 'hidepatrolled', $hidepatrolled, $defaults, $nondefaults); - appendToArrayIfNotDefault( 'from', $from, $defaults, $nondefaults); - appendToArrayIfNotDefault( 'namespace', $namespace, $defaults, $nondefaults); - appendToArrayIfNotDefault( 'invert', $invert, $defaults, $nondefaults); + wfAppendToArrayIfNotDefault( 'days', $days, $defaults, $nondefaults); + wfAppendToArrayIfNotDefault( 'limit', $limit , $defaults, $nondefaults); + wfAppendToArrayIfNotDefault( 'hideminor', $hideminor, $defaults, $nondefaults); + wfAppendToArrayIfNotDefault( 'hidebots', $hidebots, $defaults, $nondefaults); + wfAppendToArrayIfNotDefault( 'hideliu', $hideliu, $defaults, $nondefaults); + wfAppendToArrayIfNotDefault( 'hidepatrolled', $hidepatrolled, $defaults, $nondefaults); + wfAppendToArrayIfNotDefault( 'from', $from, $defaults, $nondefaults); + wfAppendToArrayIfNotDefault( 'namespace', $namespace, $defaults, $nondefaults); + wfAppendToArrayIfNotDefault( 'invert', $invert, $defaults, $nondefaults); // Add end of the texts $wgOut->addHTML( '<div class="rcoptions">' . rcOptionsPanel( $defaults, $nondefaults ) ); @@ -458,34 +459,22 @@ function rcNamespaceForm ( $namespace, $invert, $nondefaults ) { global $wgContLang, $wgScript; $t = Title::makeTitle( NS_SPECIAL, 'Recentchanges' ); - $namespaceselect = "<select name='namespace' id='nsselectbox'>"; - $namespaceselect .= '<option value="" ' . ($namespace === '' ? ' selected="selected"' : '') . '>' . wfMsg( 'contributionsall' ) . '</option>'; - $arr = $wgContLang->getFormattedNamespaces(); - foreach ( $arr as $ns => $name ) { - if( $ns < NS_MAIN ) - continue; - $n = $ns === NS_MAIN ? wfMsg ( 'blanknamespace' ) : $name; - $sel = $namespace === (string) $ns ? ' selected="selected"' : ''; - $namespaceselect .= "<option value='$ns'$sel>$n</option>"; - } - $namespaceselect .= '</select>'; + $namespaceselect = HTMLnamespaceselector($namespace, ''); + $submitbutton = '<input type="submit" value="' . wfMsgHtml( 'allpagessubmit' ) . '" />'; + $invertbox = "<input type='checkbox' name='invert' value='1' id='nsinvert'" . ( $invert ? ' checked="checked"' : '' ) . ' />'; + + $out = "<div class='namespacesettings'><form method='get' action='{$wgScript}'>\n"; - $out = ''; - $out .= "<div class='namespaceselector'><form method='get' action='{$wgScript}'>\n"; foreach ( $nondefaults as $key => $value ) { if ($key != 'namespace' && $key != 'invert') - $out .= "<input type='hidden' name='$key' value='$value' />"; + $out .= wfElement('input', array( 'type' => 'hidden', 'name' => $key, 'value' => $value)); } - $submitbutton = '<input type="submit" value="' . wfMsg( 'allpagessubmit' ) . '" />'; - $invertbox = "<input type='checkbox' name='invert' value='1' id='nsinvert'" . ( $invert ? ' checked="checked"' : '' ) . ' />'; - - $out .= '<input type="hidden" name="title" value="'.$t->getPrefixedText().'" />'; $out .= " <div id='nsselect' class='recentchanges'> - <label for='nsselectbox'>" . wfMsg('namespace') . "</label> - $namespaceselect $submitbutton $invertbox <label for='nsinvert'>" . wfMsg('invert') . "</label> + <label for='namespace'>" . wfMsgHtml('namespace') . "</label> + $namespaceselect $submitbutton $invertbox <label for='nsinvert'>" . wfMsgHtml('invert') . "</label> </div>"; $out .= '</form></div>'; return $out; @@ -551,7 +540,13 @@ function rcFormatDiff( $row ) { } wfProfileOut( "$fname-dodiff" ); } else { - $diffText = '<p><b>' . wfMsg( 'newpage' ) . '</b></p>' . + $rev = Revision::newFromId( $row->rc_this_oldid ); + if( is_null( $rev ) ) { + $newtext = ''; + } else { + $newtext = $rev->getText(); + } + $diffText = '<p><b>' . wfMsg( 'newpage' ) . '</b></p>' . '<div>' . nl2br( htmlspecialchars( $newtext ) ) . '</div>'; } @@ -563,18 +558,4 @@ function rcFormatDiff( $row ) { return $comment; } - -/** - * Appends to second array if $value differs from that in $default - */ -function appendToArrayIfNotDefault( $key, $value, $default, &$changed ) -{ - if ( is_null( $changed ) ) { - die(); - } - if ( $default[$key] !== $value ) { - $changed[$key] = $value; - } -} - ?> diff --git a/includes/SpecialRecentchangeslinked.php b/includes/SpecialRecentchangeslinked.php index 938a1609024e..5aaa49032947 100644 --- a/includes/SpecialRecentchangeslinked.php +++ b/includes/SpecialRecentchangeslinked.php @@ -36,7 +36,7 @@ function wfSpecialRecentchangeslinked( $par = NULL ) { } $id = $nt->getArticleId(); - $wgOut->setSubtitle( wfMsg( 'rclsub', $nt->getPrefixedText() ) ); + $wgOut->setSubtitle( htmlspecialchars( wfMsg( 'rclsub', $nt->getPrefixedText() ) ) ); if ( ! $days ) { $days = $wgUser->getOption( 'rcdays' ); diff --git a/includes/SpecialSearch.php b/includes/SpecialSearch.php index 20948b96aed3..fe883874d2b4 100644 --- a/includes/SpecialSearch.php +++ b/includes/SpecialSearch.php @@ -70,7 +70,7 @@ class SpecialSearch { $this->namespaces = $this->userNamespaces( $user ); } - $this->searchRedirects = false; + $this->searchRedirects = $request->getcheck( 'redirs' ) ? true : false; } /** @@ -161,9 +161,10 @@ class SpecialSearch { return; } - $search =& SearchEngine::create(); + $search = SearchEngine::create(); $search->setLimitOffset( $this->limit, $this->offset ); $search->setNamespaces( $this->namespaces ); + $search->showRedirects = $this->searchRedirects; $titleMatches = $search->searchTitle( $term ); $textMatches = $search->searchText( $term ); @@ -223,7 +224,7 @@ class SpecialSearch { function setupPage( $term ) { global $wgOut; $wgOut->setPageTitle( wfMsg( 'searchresults' ) ); - $wgOut->setSubtitle( wfMsg( 'searchquery', $term ) ); + $wgOut->setSubtitle( htmlspecialchars( wfMsg( 'searchquery', $term ) ) ); $wgOut->setArticleRelated( false ); $wgOut->setRobotpolicy( 'noindex,nofollow' ); } diff --git a/includes/SpecialUndelete.php b/includes/SpecialUndelete.php index 51b8083f7a4a..5613b283f8b7 100644 --- a/includes/SpecialUndelete.php +++ b/includes/SpecialUndelete.php @@ -40,7 +40,7 @@ class PageArchive { * * @return ResultWrapper */ - /* static */ function &listAllPages() { + /* static */ function listAllPages() { $dbr =& wfGetDB( DB_SLAVE ); $archive = $dbr->tableName( 'archive' ); @@ -56,7 +56,7 @@ class PageArchive { * * @return ResultWrapper */ - function &listRevisions() { + function listRevisions() { $dbr =& wfGetDB( DB_SLAVE ); return $dbr->resultObject( $dbr->select( 'archive', array( 'ar_minor_edit', 'ar_timestamp', 'ar_user', 'ar_user_text', 'ar_comment' ), @@ -214,10 +214,22 @@ class PageArchive { ); $revision = null; while( $row = $dbw->fetchObject( $result ) ) { + if( $row->ar_text_id ) { + // Revision was deleted in 1.5+; text is in + // the regular text table, use the reference. + // Specify null here so the so the text is + // dereferenced for page length info if needed. + $revText = null; + } else { + // Revision was deleted in 1.4 or earlier. + // Text is squashed into the archive row, and + // a new text table entry will be created for it. + $revText = Revision::getRevisionText( $row, 'ar_' ); + } $revision = new Revision( array( 'page' => $pageId, 'id' => $row->ar_rev_id, - 'text' => Revision::getRevisionText( $row, 'ar_' ), + 'text' => $revText, 'comment' => $row->ar_comment, 'user' => $row->ar_user, 'user_text' => $row->ar_user_text, diff --git a/includes/SpecialUpload.php b/includes/SpecialUpload.php index a10b2aa85ef8..52d145b88e2e 100644 --- a/includes/SpecialUpload.php +++ b/includes/SpecialUpload.php @@ -53,8 +53,10 @@ class UploadForm { $this->mUploadDescription = $request->getText( 'wpUploadDescription' ); $this->mUploadCopyStatus = $request->getText( 'wpUploadCopyStatus' ); - $this->mUploadSource = $request->getText( 'wpUploadSource'); - + $this->mUploadSource = $request->getText( 'wpUploadSource' ); + $this->mWatchthis = $request->getBool( 'wpWatchthis' ); + wfDebug( "UploadForm: watchthis is: '$this->mWatchthis'\n" ); + $this->mAction = $request->getVal( 'action' ); $this->mSessionKey = $request->getInt( 'wpSessionKey' ); @@ -213,7 +215,12 @@ class UploadForm { */ if ( ! $this->mIgnoreWarning ) { $warning = ''; - if( $this->mUploadSaveName != ucfirst( $filtered ) ) { + + global $wgCapitalLinks; + if( $wgCapitalLinks ) { + $filtered = ucfirst( $filtered ); + } + if( $this->mUploadSaveName != $filtered ) { $warning .= '<li>'.wfMsg( 'badfilename', htmlspecialchars( $this->mUploadSaveName ) ).'</li>'; } @@ -265,7 +272,8 @@ class UploadForm { $success = $img->recordUpload( $this->mUploadOldVersion, $this->mUploadDescription, $this->mUploadCopyStatus, - $this->mUploadSource ); + $this->mUploadSource, + $this->mWatchthis ); if ( $success ) { $this->showSuccess(); @@ -482,6 +490,7 @@ class UploadForm { <input type='hidden' name='wpSessionKey' value=\"" . htmlspecialchars( $this->mSessionKey ) . "\" /> <input type='hidden' name='wpUploadDescription' value=\"" . htmlspecialchars( $this->mUploadDescription ) . "\" /> <input type='hidden' name='wpDestFile' value=\"" . htmlspecialchars( $this->mDestFile ) . "\" /> + <input type='hidden' name='wpWatchthis' value=\"" . htmlspecialchars( intval( $this->mWatchthis ) ) . "\" /> {$copyright} <table border='0'> <tr> @@ -553,12 +562,16 @@ class UploadForm { " ; } + $watchChecked = $wgUser->getOption( 'watchdefault' ) + ? 'checked="checked"' + : ''; + $wgOut->addHTML( " <form id='upload' method='post' enctype='multipart/form-data' action=\"$action\"> <table border='0'><tr> <td align='right'>{$sourcefilename}:</td><td align='left'> - <input tabindex='1' type='file' name='wpUploadFile' id='wpUploadFile' onchange='fillDestFilename()' size='40' /> + <input tabindex='1' type='file' name='wpUploadFile' id='wpUploadFile' " . ($this->mDestFile?"":"onchange='fillDestFilename()' ") . "size='40' /> </td></tr><tr> <td align='right'>{$destfilename}:</td><td align='left'> @@ -570,6 +583,11 @@ class UploadForm { . htmlspecialchars( $this->mUploadDescription ) . "</textarea> </td></tr><tr> + + <td></td><td align='left'> + <input type='checkbox' name='wpWatchthis' id='wpWatchthis' $watchChecked value='true' /> + <label for='wpWatchthis'>" . wfMsgHtml( 'watchthis' ) . "</label> + </td></tr><tr> {$source} </tr> <tr><td></td><td align='left'> @@ -679,14 +697,18 @@ class UploadForm { */ function verifyExtension( $mime, $extension ) { $fname = 'SpecialUpload::verifyExtension'; - - if (!$mime || $mime=="unknown" || $mime=="unknown/unknown") { - wfDebug( "$fname: passing file with unknown mime type\n" ); - return true; - } - - $magic=& wfGetMimeMagic(); - + + $magic =& wfGetMimeMagic(); + + if ( ! $mime || $mime == 'unknown' || $mime == 'unknown/unknown' ) + if ( ! $magic->isRecognizableExtension( $extension ) ) { + wfDebug( "$fname: passing file with unknown detected mime type; unrecognized extension '$extension', can't verify\n" ); + return true; + } else { + wfDebug( "$fname: rejecting file with unknown detected mime type; recognized extension '$extension', so probably invalid file\n" ); + return false; + } + $match= $magic->isMatchingExtension($extension,$mime); if ($match===NULL) { diff --git a/includes/SpecialUserlogin.php b/includes/SpecialUserlogin.php index 84b36ccdb15a..57859a4ca34d 100644 --- a/includes/SpecialUserlogin.php +++ b/includes/SpecialUserlogin.php @@ -147,6 +147,8 @@ class LoginForm { $wgUser->sendConfirmationMail(); } + wfRunHooks( 'AddNewAccount' ); + if( $this->hasSessionCookie() ) { return $this->successfulLogin( wfMsg( 'welcomecreation', $wgUser->getName() ) ); } else { diff --git a/includes/SpecialVersion.php b/includes/SpecialVersion.php index a525d35ef0c9..8e8112907dae 100644 --- a/includes/SpecialVersion.php +++ b/includes/SpecialVersion.php @@ -1,77 +1,147 @@ <?php /** - * Give information about the version MediaWiki, PHP, and the database + * Give information about the version of MediaWiki, PHP, the DB and extensions * * @package MediaWiki * @subpackage SpecialPage + * + * @author Ævar Arnfjörð Bjarmason <avarab@gmail.com> + * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later */ /** * constructor */ function wfSpecialVersion() { - global $wgOut, $wgVersion, $wgExtensionCredits; + $version = new SpecialVersion; + $version->execute(); +} + +class SpecialVersion { + /** + * @var object + */ + var $langObj; - $dbr =& wfGetDB( DB_SLAVE ); + /** + * Constructor + */ + function SpecialVersion() { + // English motherfucker, do you speak it? + $this->langObj = setupLangObj( 'LanguageEn' ); + $this->langObj->initEncoding(); + } - $out = " -<div dir='ltr'> -This wiki is powered by '''[http://www.mediawiki.org/ MediaWiki]''', -copyright (C) 2001-2005 Magnus Manske, Brion Vibber, Lee Daniel Crocker, -Tim Starling, Erik Möller, and others. - -MediaWiki is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -MediaWiki is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. + function execute() { + global $wgOut; + + $wgOut->addWikiText( $this->MediaWikiCredits() . $this->extensionCredits() ); + $wgOut->addHTML( $this->IPInfo() ); + } -You should have received [{{SERVER}}{{SCRIPTPATH}}/COPYING a copy of the GNU General Public License] -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -or [http://www.gnu.org/copyleft/gpl.html read it online] + function MediaWikiCredits() { + global $wgVersion; + + $dbr =& wfGetDB( DB_SLAVE ); + + $ret = + "__NOTOC__ + <div dir='ltr'> + This wiki is powered by '''[http://www.mediawiki.org/ MediaWiki]''', + copyright (C) 2001-2005 Magnus Manske, Brion Vibber, Lee Daniel Crocker, + Tim Starling, Erik Möller, and others. + + MediaWiki is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + MediaWiki is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received [{{SERVER}}{{SCRIPTPATH}}/COPYING a copy of the GNU General Public License] + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + or [http://www.gnu.org/copyleft/gpl.html read it online] + + * [http://www.mediawiki.org/ MediaWiki]: $wgVersion + * [http://www.php.net/ PHP]: " . phpversion() . " (" . php_sapi_name() . ") + * " . $dbr->getSoftwareLink() . ": " . $dbr->getServerVersion() . " + </div>"; + + return str_replace( "\t\t", '', $ret ); + } + + function extensionCredits() { + global $wgExtensionCredits, $wgExtensionFunctions, $wgSkinExtensionFunction; + + if ( ! count( $wgExtensionCredits ) && ! count( $wgExtensionFunctions ) && ! count( $wgSkinExtensionFunction ) ) + return ''; -* [http://www.mediawiki.org/ MediaWiki]: $wgVersion -* [http://www.php.net/ PHP]: " . phpversion() . " (" . php_sapi_name() . ") -* " . $dbr->getSoftwareLink() . ": " . $dbr->getServerVersion() . " -</div> -"; - if ( count( $wgExtensionCredits ) > 0 ) { $extensionTypes = array( 'specialpage' => 'Special pages', 'parserhook' => 'Parser hooks', - 'other' => 'Other' + 'other' => 'Other', ); - $out .= "== Extensions ==\n"; - + $out = "\n* Extensions:\n"; foreach ( $extensionTypes as $type => $text ) { - if ( count( @$wgExtensionCredits[$type] ) > 0 ) { - $out .= "=== $text ===\n"; + if ( count( @$wgExtensionCredits[$type] ) ) { + $out .= "** $text:\n"; foreach ( $wgExtensionCredits[$type] as $extension ) { - $out .= formatExtensionCredits( $extension['name'], $extension['author'], @$extension['url'], @$extension['version'] ); + wfSuppressWarnings(); + $out .= $this->formatCredits( + $extension['name'], + $extension['version'], + $extension['author'], + $extension['url'], + $extension['description'] + ); + wfRestoreWarnings(); } } + } + if ( count( $wgExtensionFunctions ) ) { + $out .= "** Extension functions:\n"; + $out .= '***' . $this->langObj->listToText( $wgExtensionFunctions ) . "\n"; } + + if ( count( $wgSkinExtensionFunction ) ) { + $out .= "** Skin extension functions:\n"; + $out .= '***' . $this->langObj->listToText( $wgSkinExtensionFunction ) . "\n"; + } + + return $out; + } + + function formatCredits( $name, $version = null, $author = null, $url = null, $description = null) { + $ret = '*** '; + if ( isset( $url ) ) + $ret .= "[$url "; + $ret .= "''$name"; + if ( isset( $version ) ) + $ret .= " (version $version)"; + $ret .= "''"; + if ( isset( $url ) ) + $ret .= ']'; + if ( isset( $description ) ) + $ret .= ', ' . $description; + if ( isset( $description ) && isset( $author ) ) + $ret .= ', '; + if ( isset( $author ) ) + $ret .= ' by ' . $this->langObj->listToText( (array)$author ); + + return "$ret\n"; } - $wgOut->addWikiText( $out ); -} -function formatExtensionCredits( $name, $author, $url = null, $version = null ) { - $ret = '* '; - if ( isset( $url ) ) - $ret .= "[$url "; - $ret .= $name; - if ( isset( $url ) ) - $ret .= ']'; - if ( isset( $version ) ) - $ret .= " $version"; - $ret .= " by $author\n"; - return $ret; + function IPInfo() { + global $wgIP; + + $ip = str_replace( '--', ' - - ', htmlspecialchars( $wgIP ) ); + return "<!-- visited from $ip -->\n"; + } } ?> diff --git a/includes/SpecialWatchlist.php b/includes/SpecialWatchlist.php index e6fabf80fa26..c0accdaaae37 100644 --- a/includes/SpecialWatchlist.php +++ b/includes/SpecialWatchlist.php @@ -22,7 +22,7 @@ function wfSpecialWatchlist( $par ) { $fname = 'wfSpecialWatchlist'; $wgOut->setPagetitle( wfMsg( 'watchlist' ) ); - $sub = wfMsg( 'watchlistsub', $wgUser->getName() ); + $sub = htmlspecialchars( wfMsg( 'watchlistsub', $wgUser->getName() ) ); $wgOut->setSubtitle( $sub ); $wgOut->setRobotpolicy( 'noindex,nofollow' ); @@ -33,11 +33,20 @@ function wfSpecialWatchlist( $par ) { return; } + $defaults = array( + /* float */ 'days' => 3.0, /* or 0.5, watch further below */ + /* bool */ 'hideOwn' => false, + ); + + extract($defaults); + # Get query variables $days = $wgRequest->getVal( 'days' ); + $hideOwn = $wgRequest->getBool( 'hideOwn' ); + + # Watchlist editing $action = $wgRequest->getVal( 'action' ); $remove = $wgRequest->getVal( 'remove' ); - $hideOwn = $wgRequest->getBool( 'hideOwn' ); $id = $wgRequest->getArray( 'id' ); $uid = $wgUser->getID(); @@ -45,7 +54,7 @@ function wfSpecialWatchlist( $par ) { $wgUser->clearAllNotifications( $uid ); } - + # Deleting items from watchlist if(($action == 'submit') && isset($remove) && is_array($id)) { $wgOut->addWikiText( wfMsg( 'removingchecked' ) ); $wgOut->addHTML( '<p>' ); @@ -78,32 +87,38 @@ function wfSpecialWatchlist( $par ) { $dbr =& wfGetDB( DB_SLAVE ); extract( $dbr->tableNames( 'page', 'revision', 'watchlist', 'recentchanges' ) ); - $sql = "SELECT COUNT(*) AS n FROM $watchlist WHERE wl_user=$uid"; - $res = $dbr->query( $sql, $fname ); - $s = $dbr->fetchObject( $res ); + $sql = "SELECT COUNT(*) AS n FROM $watchlist WHERE wl_user=$uid"; + $res = $dbr->query( $sql, $fname ); + $s = $dbr->fetchObject( $res ); -# Patch *** A1 *** (see A2 below) + # Patch *** A1 *** (see A2 below) # adjust for page X, talk:page X, which are both stored separately, but treated together -# $nitems = $s->n / 2; - $nitems = $s->n; + $nitems = floor($s->n / 2); +# $nitems = $s->n; if($nitems == 0) { $wgOut->addWikiText( wfMsg( 'nowatchlist' ) ); return; } - if ( is_null( $days ) ) { - $big = 1000; + if( is_null($days) || !is_numeric($days) ) { + $big = 1000; /* The magical big */ if($nitems > $big) { # Set default cutoff shorter - $days = (12.0 / 24.0); # 12 hours... + $days = $defaults['days'] = (12.0 / 24.0); # 12 hours... } else { - $days = 3; # longer cutoff for shortlisters + $days = $defaults['days']; # default cutoff for shortlisters } } else { $days = floatval($days); } + // Dump everything here + $nondefaults = array(); + + wfAppendToArrayIfNotDefault( 'days', $days, $defaults, $nondefaults); + wfAppendToArrayIfNotDefault( 'hideOwn', $hideOwn, $defaults, $nondefaults); + if ( $days <= 0 ) { $docutoff = ''; $cutoff = false; @@ -112,13 +127,17 @@ function wfSpecialWatchlist( $par ) { $docutoff = "AND rev_timestamp > '" . ( $cutoff = $dbr->timestamp( time() - intval( $days * 86400 ) ) ) . "'"; - $sql = "SELECT COUNT(*) AS n FROM $page, $revision WHERE rev_timestamp>'$cutoff' AND page_id=rev_page"; - $res = $dbr->query( $sql, $fname ); - $s = $dbr->fetchObject( $res ); - $npages = $s->n; + /* + $sql = "SELECT COUNT(*) AS n FROM $page, $revision WHERE rev_timestamp>'$cutoff' AND page_id=rev_page"; + $res = $dbr->query( $sql, $fname ); + $s = $dbr->fetchObject( $res ); + $npages = $s->n; + */ + $npages = 40000 * $days; } + /* Edit watchlist form */ if($wgRequest->getBool('edit') || $par == 'edit' ) { $wgOut->addWikiText( wfMsg( 'watchlistcontains', $wgLang->formatNum( $nitems ) ) . "\n\n" . wfMsg( 'watcheditlist' ) ); @@ -139,12 +158,12 @@ function wfSpecialWatchlist( $par ) { while( $s = $dbr->fetchObject( $res ) ) { $list[$s->wl_namespace][] = $s->wl_title; } - + // TODO: Display a TOC foreach($list as $ns => $titles) { if (Namespace::isTalk($ns)) continue; - if ($ns != NS_MAIN) + if ($ns != NS_MAIN) $wgOut->addHTML( '<h2>' . $wgContLang->getFormattedNsText( $ns ) . '</h2>' ); $wgOut->addHTML( '<ul>' ); foreach($titles as $title) { @@ -181,23 +200,8 @@ function wfSpecialWatchlist( $par ) { # through the time-sorted page list checking for watched items. # Up estimate of watched items by 15% to compensate for talk pages... - if( $cutoff && ( $nitems*1.15 > $npages ) ) { - $x = 'rev_timestamp'; - $y = wfMsg( 'watchmethod-recent' ); - # TG patch: here we do not consider pages and their talk pages equivalent - why should we ? - # The change results in talk-pages not automatically included in watchlists, when their parent page is included - # $z = "wl_namespace=cur_namespace & ~1"; - $z = 'wl_namespace=page_namespace'; - } else { - $x = 'page_timestamp'; - $y = wfMsg( 'watchmethod-list' ); - # TG patch: here we do not consider pages and their talk pages equivalent - why should we ? - # The change results in talk-pages not automatically included in watchlists, when their parent page is included - # $z = "(wl_namespace=cur_namespace OR wl_namespace+1=cur_namespace)"; - $z = 'wl_namespace=page_namespace'; - } - $andHideOwn = $hideOwn ? "AND (rev_user <> $uid)" : ''; + $andHideOwn = $hideOwn ? "AND (rc_user <> $uid)" : ''; # Show watchlist header $header = ''; @@ -208,8 +212,9 @@ function wfSpecialWatchlist( $par ) { $header .= wfMsg( 'wlheader-showupdated' ) . "\n"; } - $header .= wfMsg( 'watchdetails', $wgLang->formatNum( $nitems / 2 ), - $wgLang->formatNum( $npages ), $y, + # TODO: Consider removing the third parameter + $header .= wfMsg( 'watchdetails', $wgLang->formatNum( $nitems ), + $wgLang->formatNum( $npages ), '', $specialTitle->getFullUrl( 'edit=yes' ) ); $wgOut->addWikiText( $header ); @@ -222,53 +227,63 @@ function wfSpecialWatchlist( $par ) { "\n\n" ); } - $use_index = $dbr->useIndexClause( $x ); - $sql = "SELECT - page_namespace,page_title,rev_comment, page_id, - rev_user,rev_user_text,rev_timestamp,rev_minor_edit,rev_id,page_is_new,wl_notificationtimestamp - FROM $watchlist,$page,$revision $use_index - WHERE wl_user=$uid - $andHideOwn - AND $z - AND wl_title=page_title - AND page_latest=rev_id - $docutoff - ORDER BY rev_timestamp DESC"; - + $sql = "SELECT + rc_namespace page_namespace,rc_title page_title, + rc_comment rev_comment, rc_cur_id page_id, + rc_user rev_user,rc_user_text rev_user_text, + rc_timestamp rev_timestamp,rc_minor rev_minor_edit, + rc_this_oldid rev_id, + rc_last_oldid, + rc_new page_is_new,wl_notificationtimestamp + FROM $watchlist,$recentchanges,$page + WHERE wl_user=$uid + AND wl_namespace=rc_namespace + AND wl_title=rc_title + AND rc_timestamp > '$cutoff' + AND rc_cur_id=page_id + AND rc_this_oldid=page_latest + $andHideOwn + ORDER BY rc_timestamp DESC"; $res = $dbr->query( $sql, $fname ); $numRows = $dbr->numRows( $res ); + + /* Start bottom header */ + $wgOut->addHTML( "<hr />\n<p>" ); + if($days >= 1) - $note = wfMsg( 'rcnote', $wgLang->formatNum( $numRows ), $wgLang->formatNum( $days ) ); + $wgOut->addWikiText( wfMsg( 'rcnote', $wgLang->formatNum( $numRows ), + $wgLang->formatNum( $days ) ) . '<br />' , false ); elseif($days > 0) - $note = wfMsg( 'wlnote', $wgLang->formatNum( $numRows ), $wgLang->formatNum( round($days*24) ) ); - else - $note = ''; - $wgOut->addHTML( "\n<hr />\n{$note}\n<br />" ); - $note = wlCutoffLinks( $days ); - $wgOut->addHTML( "{$note}\n" ); + $wgOut->addWikiText( wfMsg( 'wlnote', $wgLang->formatNum( $numRows ), + $wgLang->formatNum( round($days*24) ) ) . '<br />' , false ); + + $wgOut->addHTML( "\n" . wlCutoffLinks( $days, 'Watchlist', $nondefaults ) . "<br />\n" ); $sk = $wgUser->getSkin(); $s = $sk->makeKnownLink( $wgContLang->specialPage( 'Watchlist' ), - (0 == $hideOwn) ? wfMsg( 'wlhide' ) : wfMsg( 'wlshow' ), - 'hideOwn=' . $wgLang->formatNum( 1-$hideOwn ) ); - - $note = wfMsg( "wlhideshowown", $s ); - $wgOut->addHTML( "\n<br />{$note}\n<br />" ); + (0 == $hideOwn) ? wfMsgHtml( 'wlhide' ) : wfMsgHtml( 'wlshow' ), + wfArrayToCGI( array('hideOwn' => 1-$hideOwn ), $nondefaults ) ); + + $wgOut->addHTML( wfMsgHtml( "wlhideshowown", $s ) ); if ( $numRows == 0 ) { - $wgOut->addHTML( '<p><i>' . wfMsg( 'watchnochange' ) . '</i></p>' ); + $wgOut->addWikitext( "<br />" . wfMsg( 'watchnochange' ), false ); + $wgOut->addHTML( "</p>\n" ); return; } + $wgOut->addHTML( "</p>\n" ); + /* End bottom header */ + $sk = $wgUser->getSkin(); $list =& new ChangesList( $sk ); $s = $list->beginRecentChangesList(); $counter = 1; while ( $obj = $dbr->fetchObject( $res ) ) { # Make fake RC entry - $rc = RecentChange::newFromCurRow( $obj ); + $rc = RecentChange::newFromCurRow( $obj, $obj->rc_last_oldid ); $rc->counter = $counter++; if ( $wgShowUpdatedMarker ) { @@ -300,44 +315,42 @@ function wfSpecialWatchlist( $par ) { } - -function wlHoursLink( $h, $page ) { +function wlHoursLink( $h, $page, $options = array() ) { global $wgUser, $wgLang, $wgContLang; $sk = $wgUser->getSkin(); $s = $sk->makeKnownLink( $wgContLang->specialPage( $page ), $wgLang->formatNum( $h ), - 'days=' . ($h / 24.0) ); + wfArrayToCGI( array('days' => ($h / 24.0)), $options ) ); return $s; } - -function wlDaysLink( $d, $page ) { +function wlDaysLink( $d, $page, $options = array() ) { global $wgUser, $wgLang, $wgContLang; $sk = $wgUser->getSkin(); $s = $sk->makeKnownLink( $wgContLang->specialPage( $page ), - ($d ? $wgLang->formatNum( $d ) : wfMsg( 'watchlistall2' ) ), "days=$d" ); + ($d ? $wgLang->formatNum( $d ) : wfMsgHtml( 'watchlistall2' ) ), + wfArrayToCGI( array('days' => $d), $options ) ); return $s; } -function wlCutoffLinks( $days, $page = 'Watchlist' ) -{ +function wlCutoffLinks( $days, $page = 'Watchlist', $options = array() ) { $hours = array( 1, 2, 6, 12 ); $days = array( 1, 3, 7 ); $cl = ''; $i = 0; foreach( $hours as $h ) { - $hours[$i++] = wlHoursLink( $h, $page ); + $hours[$i++] = wlHoursLink( $h, $page, $options ); } $i = 0; foreach( $days as $d ) { - $days[$i++] = wlDaysLink( $d, $page ); + $days[$i++] = wlDaysLink( $d, $page, $options ); } return wfMsg ('wlshowlast', implode(' | ', $hours), implode(' | ', $days), - wlDaysLink( 0, $page ) ); + wlDaysLink( 0, $page, $options ) ); } ?> diff --git a/includes/SpecialWhatlinkshere.php b/includes/SpecialWhatlinkshere.php index 4d5bfa0564d7..087b517cebb8 100644 --- a/includes/SpecialWhatlinkshere.php +++ b/includes/SpecialWhatlinkshere.php @@ -32,7 +32,7 @@ function wfSpecialWhatlinkshere($par = NULL) { $sk = $wgUser->getSkin(); $isredir = ' (' . wfMsg( 'isredirect' ) . ")\n"; - $wgOut->addHTML('< '.$sk->makeKnownLinkObj($nt, '', 'redirect=no' )."<br />\n"); + $wgOut->addHTML('< '.$sk->makeLinkObj($nt, '', 'redirect=no' )."<br />\n"); wfShowIndirectLinks( 0, $nt, $limit, $offset ); } diff --git a/includes/StreamFile.php b/includes/StreamFile.php index 3098ea876cdd..0c44bb9228a0 100644 --- a/includes/StreamFile.php +++ b/includes/StreamFile.php @@ -4,7 +4,7 @@ /** */ function wfStreamFile( $fname ) { global $wgSquidMaxage; - $stat = stat( $fname ); + $stat = @stat( $fname ); if ( !$stat ) { header( 'HTTP/1.0 404 Not Found' ); echo "<html><body> @@ -15,7 +15,6 @@ does not.</p> return; } - header( "Cache-Control: s-maxage=$wgSquidMaxage, must-revalidate, max-age=0" ); header( 'Last-Modified: ' . gmdate( 'D, d M Y H:i:s', $stat['mtime'] ) . ' GMT' ); if ( !empty( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) ) { diff --git a/includes/Title.php b/includes/Title.php index 33f728b780aa..e147b28e1299 100644 --- a/includes/Title.php +++ b/includes/Title.php @@ -102,7 +102,7 @@ class Title { * @static * @access public */ - function &newFromText( $text, $defaultNamespace = NS_MAIN ) { + function newFromText( $text, $defaultNamespace = NS_MAIN ) { $fname = 'Title::newFromText'; wfProfileIn( $fname ); @@ -667,7 +667,7 @@ class Title { $namespace = $wgContLang->getNsText( $this->mNamespace ); if ( '' != $namespace ) { # Can this actually happen? Interwikis shouldn't be parsed. - $namepace .= ':'; + $namespace .= ':'; } $url = str_replace( '$1', $namespace . $this->mUrlform, $baseUrl ); if( $query != '' ) { @@ -950,7 +950,8 @@ class Title { } } - if( $action == 'move' && !$this->isMovable() ) { + if( $action == 'move' && + !( $this->isMovable() && $wgUser->isAllowed( 'move' ) ) ) { wfProfileOut( $fname ); return false; } @@ -1280,57 +1281,58 @@ class Title { $this->mDbkeyform = $t; - # Initial colon indicating main namespace + # Initial colon indicates main namespace rather than specified default + # but should not create invalid {ns,title} pairs such as {0,Project:Foo} if ( ':' == $t{0} ) { - $r = substr( $t, 1 ); $this->mNamespace = NS_MAIN; - } else { - # Namespace or interwiki prefix - $firstPass = true; - do { - if ( preg_match( "/^(.+?)_*:_*(.*)$/S", $t, $m ) ) { - $p = $m[1]; - $lowerNs = strtolower( $p ); - if ( $ns = Namespace::getCanonicalIndex( $lowerNs ) ) { - # Canonical namespace - $t = $m[2]; - $this->mNamespace = $ns; - } elseif ( $ns = $wgContLang->getNsIndex( $lowerNs )) { - # Ordinary namespace - $t = $m[2]; - $this->mNamespace = $ns; - } elseif( $this->getInterwikiLink( $p ) ) { - if( !$firstPass ) { - # Can't make a local interwiki link to an interwiki link. - # That's just crazy! + $t = substr( $t, 1 ); # remove the colon but continue processing + } + + # Namespace or interwiki prefix + $firstPass = true; + do { + if ( preg_match( "/^(.+?)_*:_*(.*)$/S", $t, $m ) ) { + $p = $m[1]; + $lowerNs = strtolower( $p ); + if ( $ns = Namespace::getCanonicalIndex( $lowerNs ) ) { + # Canonical namespace + $t = $m[2]; + $this->mNamespace = $ns; + } elseif ( $ns = $wgContLang->getNsIndex( $lowerNs )) { + # Ordinary namespace + $t = $m[2]; + $this->mNamespace = $ns; + } elseif( $this->getInterwikiLink( $p ) ) { + if( !$firstPass ) { + # Can't make a local interwiki link to an interwiki link. + # That's just crazy! + wfProfileOut( $fname ); + return false; + } + + # Interwiki link + $t = $m[2]; + $this->mInterwiki = $p; + + # Redundant interwiki prefix to the local wiki + if ( 0 == strcasecmp( $this->mInterwiki, $wgLocalInterwiki ) ) { + if( $t == '' ) { + # Can't have an empty self-link wfProfileOut( $fname ); return false; } - - # Interwiki link - $t = $m[2]; - $this->mInterwiki = $p; - - # Redundant interwiki prefix to the local wiki - if ( 0 == strcasecmp( $this->mInterwiki, $wgLocalInterwiki ) ) { - if( $t == '' ) { - # Can't have an empty self-link - wfProfileOut( $fname ); - return false; - } - $this->mInterwiki = ''; - $firstPass = false; - # Do another namespace split... - continue; - } + $this->mInterwiki = ''; + $firstPass = false; + # Do another namespace split... + continue; } - # If there's no recognized interwiki or namespace, - # then let the colon expression be part of the title. } - break; - } while( true ); - $r = $t; - } + # If there's no recognized interwiki or namespace, + # then let the colon expression be part of the title. + } + break; + } while( true ); + $r = $t; # We already know that some pages won't be in the database! # @@ -1657,6 +1659,7 @@ class Title { $u->doUpdate(); } + global $wgUser; wfRunHooks( 'TitleMoveComplete', array( &$this, &$nt, &$wgUser, $pageid, $redirid ) ); return true; } @@ -1845,7 +1848,7 @@ class Title { # Is it a redirect? $id = $nt->getArticleID(); $obj = $dbw->selectRow( array( 'page', 'revision', 'text'), - array( 'page_is_redirect','old_text' ), + array( 'page_is_redirect','old_text','old_flags' ), array( 'page_id' => $id, 'page_latest=rev_id', 'rev_text_id=old_id' ), $fname, 'FOR UPDATE' ); @@ -1853,14 +1856,18 @@ class Title { # Not a redirect return false; } + $text = Revision::getRevisionText( $obj ); # Does the redirect point to the source? - if ( preg_match( "/\\[\\[\\s*([^\\]\\|]*)]]/", $obj->old_text, $m ) ) { + if ( preg_match( "/\\[\\[\\s*([^\\]\\|]*)]]/", $text, $m ) ) { $redirTitle = Title::newFromText( $m[1] ); if( !is_object( $redirTitle ) || $redirTitle->getPrefixedDBkey() != $this->getPrefixedDBkey() ) { return false; } + } else { + # Fail safe + return false; } # Does the article have a history? @@ -2036,7 +2043,7 @@ class Title { * @param Title $title * @return bool */ - function equals( &$title ) { + function equals( $title ) { return $this->getInterwiki() == $title->getInterwiki() && $this->getNamespace() == $title->getNamespace() && $this->getDbkey() == $title->getDbkey(); diff --git a/includes/User.php b/includes/User.php index 902283182495..8b0f577a8dbb 100644 --- a/includes/User.php +++ b/includes/User.php @@ -50,8 +50,6 @@ class User { * @static */ function newFromName( $name ) { - $u = new User(); - # Force usernames to capital global $wgContLang; $name = $wgContLang->ucfirst( $name ); @@ -71,6 +69,7 @@ class User { return null; } + $u = new User(); $u->setName( $canonicalName ); $u->setId( $u->idFromName( $canonicalName ) ); return $u; @@ -252,13 +251,16 @@ class User { * @todo Check what is doing really [AV] */ function randomPassword() { + global $wgMinimalPasswordLength; $pwchars = 'ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz'; $l = strlen( $pwchars ) - 1; - $np = $pwchars{mt_rand( 0, $l )} . $pwchars{mt_rand( 0, $l )} . - $pwchars{mt_rand( 0, $l )} . chr( mt_rand(48, 57) ) . - $pwchars{mt_rand( 0, $l )} . $pwchars{mt_rand( 0, $l )} . - $pwchars{mt_rand( 0, $l )}; + $pwlength = max( 7, $wgMinimalPasswordLength ); + $digit = mt_rand(0, $pwlength - 1); + $np = ''; + for ( $i = 0; $i < $pwlength; $i++ ) { + $np .= $i == $digit ? chr( mt_rand(48, 57) ) : $pwchars{ mt_rand(0, $l)}; + } return $np; } diff --git a/includes/UserTalkUpdate.php b/includes/UserTalkUpdate.php index 7c61406026e6..10bf9158f891 100644 --- a/includes/UserTalkUpdate.php +++ b/includes/UserTalkUpdate.php @@ -50,7 +50,7 @@ class UserTalkUpdate { $this->mAction = $action; $this->mNamespace = $ns; - $this->mTitle = $title; # str_replace( '_', ' ', $title ); # I do not know, why this was needed . T. Gries 23.11.2004 + $this->mTitle = $title; $this->mSummary = $summary; $this->mMinorEdit = $minoredit; $this->mTimestamp = $timestamp; @@ -62,7 +62,7 @@ class UserTalkUpdate { # If the user talk page is our own, clear the flag # when we are reading it or writing it. - if ( 0 == strcmp( $this->mTitle, $wgUser->getName() ) ) { + if ( 0 == strcmp( str_replace( '_', ' ', $this->mTitle ), $wgUser->getName() ) ) { $wgUser->setNewtalk( 0 ); $wgUser->saveSettings(); } else { diff --git a/includes/WatchedItem.php b/includes/WatchedItem.php index b27c53faf95c..1912f5400b6c 100644 --- a/includes/WatchedItem.php +++ b/includes/WatchedItem.php @@ -133,13 +133,27 @@ class WatchedItem { } /** + * Check if the given title already is watched by the user, and if so + * add watches on a new title. To be used for page renames and such. + * + * @param Title $ot Page title to duplicate entries from, if present + * @param Title $nt Page title to add watches on * @static */ function duplicateEntries( $ot, $nt ) { + WatchedItem::doDuplicateEntries( $ot->getSubjectPage(), $nt->getSubjectPage() ); + WatchedItem::doDuplicateEntries( $ot->getTalkPage(), $nt->getTalkPage() ); + } + + /** + * @static + * @access private + */ + function doDuplicateEntries( $ot, $nt ) { $fname = "WatchedItem::duplicateEntries"; global $wgMemc, $wgDBname; - $oldnamespace = $ot->getNamespace() & ~1; - $newnamespace = $nt->getNamespace() & ~1; + $oldnamespace = $ot->getNamespace(); + $newnamespace = $nt->getNamespace(); $oldtitle = $ot->getDBkey(); $newtitle = $nt->getDBkey(); @@ -160,6 +174,11 @@ class WatchedItem { ); } $dbw->freeResult( $res ); + + if( empty( $values ) ) { + // Nothing to do + return true; + } # Perform replace # Note that multi-row replace is very efficient for MySQL but may be inefficient for diff --git a/includes/WikiError.php b/includes/WikiError.php index f693003a5ffa..46738fccf5b9 100644 --- a/includes/WikiError.php +++ b/includes/WikiError.php @@ -89,15 +89,36 @@ class WikiXmlError extends WikiError { * @param resource $parser * @param string $message */ - function WikiXmlError( $parser, $message = '' ) { + function WikiXmlError( $parser, $message = 'XML parsing error', $context = null, $offset = 0 ) { $this->mXmlError = xml_get_error_code( $parser ); + $this->mColumn = xml_get_current_column_number( $parser ); + $this->mLine = xml_get_current_line_number( $parser ); + $this->mByte = xml_get_current_byte_index( $parser ); + $this->mContext = $this->_extractContext( $context, $offset ); $this->mMessage = $message; xml_parser_free( $parser ); + wfDebug( "WikiXmlError: " . $this->getMessage() . "\n" ); } /** @return string */ function getMessage() { - return $this->mMessage . ': ' . xml_error_string( $this->mXmlError ); + return sprintf( '%s at line %d, col %d (byte %d%s): %s', + $this->mMessage, + $this->mLine, + $this->mColumn, + $this->mByte, + $this->mContext, + xml_error_string( $this->mXmlError ) ); + } + + function _extractContext( $context, $offset ) { + if( is_null( $context ) ) { + return null; + } else { + // Hopefully integer overflow will be handled transparently here + $inlineOffset = $this->mByte - $offset; + return '; "' . substr( $context, $inlineOffset, 16 ) . '"'; + } } } |