Index: branches/iwtransclusion/phase3v2/maintenance/archives/patch-globaltemplatelinks.sql |
— | — | @@ -17,15 +17,19 @@ |
18 | 18 | -- Needed for display purposes |
19 | 19 | gtl_from_title varchar(255) binary NOT NULL, |
20 | 20 | |
21 | | - -- The wiki ID of the wiki that hosts the transcluded page |
22 | | - gtl_to_wiki varchar(64) NOT NULL, |
| 21 | + -- The interwiki prefix of the wiki that hosts the transcluded page |
| 22 | + gtl_to_prefix varchar(32) NOT NULL, |
23 | 23 | |
24 | 24 | -- The namespace of the transcluded page on that wiki |
25 | 25 | gtl_to_namespace int NOT NULL, |
26 | 26 | |
| 27 | + -- The namespace name of transcluded page |
| 28 | + -- Needed for display purposes, since the local namespace ID doesn't necessarily match a distant one |
| 29 | + gtl_to_namespacetext varchar(255) NOT NULL, |
| 30 | + |
27 | 31 | -- The title of the transcluded page on that wiki |
28 | 32 | gtl_to_title varchar(255) binary NOT NULL |
29 | 33 | ) /*$wgDBTableOptions*/; |
30 | 34 | |
31 | | -CREATE UNIQUE INDEX /*i*/gtl_to_from ON /*_*/globaltemplatelinks (gtl_to_wiki, gtl_to_namespace, gtl_to_title, gtl_from_wiki, gtl_from_page); |
32 | | -CREATE UNIQUE INDEX /*i*/gtl_from_to ON /*_*/globaltemplatelinks (gtl_from_wiki, gtl_from_page, gtl_to_wiki, gtl_to_namespace, gtl_to_title); |
| 35 | +CREATE UNIQUE INDEX /*i*/gtl_to_from ON /*_*/globaltemplatelinks (gtl_to_prefix, gtl_to_namespace, gtl_to_title, gtl_from_wiki, gtl_from_page); |
| 36 | +CREATE UNIQUE INDEX /*i*/gtl_from_to ON /*_*/globaltemplatelinks (gtl_from_wiki, gtl_from_page, gtl_to_prefix, gtl_to_namespace, gtl_to_title); |
Index: branches/iwtransclusion/phase3v2/maintenance/language/messages.inc |
— | — | @@ -627,6 +627,9 @@ |
628 | 628 | 'templatesused', |
629 | 629 | 'templatesusedpreview', |
630 | 630 | 'templatesusedsection', |
| 631 | + 'distanttemplatesused', |
| 632 | + 'distanttemplatesusedpreview', |
| 633 | + 'distanttemplatesusedsection', |
631 | 634 | 'template-protected', |
632 | 635 | 'template-semiprotected', |
633 | 636 | 'hiddencategories', |
Index: branches/iwtransclusion/phase3v2/includes/Article.php |
— | — | @@ -3063,9 +3063,8 @@ |
3064 | 3064 | * @param $commit boolean defaults to true, triggers transaction end |
3065 | 3065 | * @return boolean true if successful |
3066 | 3066 | */ |
3067 | | - public function doDeleteArticle( $reason, $suppress = false, $id = 0, $commit = true, &$error = '' ) { |
3068 | | - global $wgDeferredUpdateList, $wgUseTrackbacks; |
3069 | | - global $wgUser; |
| 3067 | + public function doDeleteArticle( $reason, $suppress = false, $id = 0, $commit = true ) { |
| 3068 | + global $wgDeferredUpdateList, $wgUseTrackbacks, $wgEnableInterwikiTemplatesTracking, $wgGlobalDatabase; |
3070 | 3069 | |
3071 | 3070 | wfDebug( __METHOD__ . "\n" ); |
3072 | 3071 | |
— | — | @@ -3166,6 +3165,14 @@ |
3167 | 3166 | $dbw->delete( 'langlinks', array( 'll_from' => $id ) ); |
3168 | 3167 | $dbw->delete( 'iwlinks', array( 'iwl_from' => $id ) ); |
3169 | 3168 | $dbw->delete( 'redirect', array( 'rd_from' => $id ) ); |
| 3169 | + |
| 3170 | + if ( $wgEnableInterwikiTemplatesTracking && $wgGlobalDatabase ) { |
| 3171 | + $dbw2 = wfGetDB( DB_MASTER, array(), $wgGlobalDatabase ); |
| 3172 | + $dbw2->delete( 'globaltemplatelinks', |
| 3173 | + array( 'gtl_from_wiki' => wfWikiID( ), |
| 3174 | + 'gtl_from_page' => $id ) |
| 3175 | + ); |
| 3176 | + } |
3170 | 3177 | } |
3171 | 3178 | |
3172 | 3179 | # If using cleanup triggers, we can skip some manual deletes |
— | — | @@ -4149,6 +4156,42 @@ |
4150 | 4157 | } |
4151 | 4158 | |
4152 | 4159 | /** |
| 4160 | + * Return a list of distant templates used by this article. |
| 4161 | + * Uses the globaltemplatelinks table |
| 4162 | + * |
| 4163 | + * @return Array of Title objects |
| 4164 | + */ |
| 4165 | + public function getUsedDistantTemplates() { |
| 4166 | + global $wgGlobalDatabase; |
| 4167 | + |
| 4168 | + $result = array(); |
| 4169 | + |
| 4170 | + if ( $wgGlobalDatabase ) { |
| 4171 | + $id = $this->mTitle->getArticleID(); |
| 4172 | + |
| 4173 | + if ( $id == 0 ) { |
| 4174 | + return array(); |
| 4175 | + } |
| 4176 | + |
| 4177 | + $dbr = wfGetDB( DB_SLAVE, array(), $wgGlobalDatabase ); |
| 4178 | + $res = $dbr->select( array( 'globaltemplatelinks' ), |
| 4179 | + array( 'gtl_to_prefix', 'gtl_to_namespace', 'gtl_to_title' ), |
| 4180 | + array( 'gtl_from_wiki' => wfWikiID( ), 'gtl_from_page' => $id ), |
| 4181 | + __METHOD__ ); |
| 4182 | + |
| 4183 | + if ( $res !== false ) { |
| 4184 | + foreach ( $res as $row ) { |
| 4185 | + $result[] = Title::makeTitle( $row->gtl_to_namespace, $row->gtl_to_title, null, $row->gtl_to_prefix ); |
| 4186 | + } |
| 4187 | + } |
| 4188 | + |
| 4189 | + $dbr->freeResult( $res ); |
| 4190 | + } |
| 4191 | + |
| 4192 | + return $result; |
| 4193 | + } |
| 4194 | + |
| 4195 | + /** |
4153 | 4196 | * Returns a list of hidden categories this page is a member of. |
4154 | 4197 | * Uses the page_props and categorylinks tables. |
4155 | 4198 | * |
Index: branches/iwtransclusion/phase3v2/includes/parser/Parser.php |
— | — | @@ -3055,7 +3055,7 @@ |
3056 | 3056 | * @private |
3057 | 3057 | */ |
3058 | 3058 | function braceSubstitution( $piece, $frame ) { |
3059 | | - global $wgContLang, $wgNonincludableNamespaces; |
| 3059 | + global $wgContLang, $wgNonincludableNamespaces, $wgEnableInterwikiTranscluding, $wgEnableInterwikiTemplatesTracking; |
3060 | 3060 | wfProfileIn( __METHOD__ ); |
3061 | 3061 | wfProfileIn( __METHOD__.'-setup' ); |
3062 | 3062 | |
— | — | @@ -3269,11 +3269,15 @@ |
3270 | 3270 | $text = "[[:$titleText]]"; |
3271 | 3271 | $found = true; |
3272 | 3272 | } |
3273 | | - } elseif ( $title->isTrans() ) { |
| 3273 | + } elseif ( $wgEnableInterwikiTranscluding && $title->isTrans() ) { |
3274 | 3274 | // TODO: Work by Peter17 in progress |
3275 | 3275 | |
3276 | 3276 | $text = Interwiki::interwikiTransclude( $title ); |
3277 | 3277 | |
| 3278 | + if ( $wgEnableInterwikiTemplatesTracking ) { |
| 3279 | + $this->registerDistantTemplate( $title ); |
| 3280 | + } |
| 3281 | + |
3278 | 3282 | if ( $text !== false ) { |
3279 | 3283 | # Preprocess it like a template |
3280 | 3284 | $text = $this->preprocessToDom( $text, self::PTD_FOR_INCLUSION ); |
— | — | @@ -3428,6 +3432,17 @@ |
3429 | 3433 | * @param Title $title |
3430 | 3434 | * @return mixed string or false |
3431 | 3435 | */ |
| 3436 | + function registerDistantTemplate( $title ) { |
| 3437 | + $stuff = Parser::distantTemplateCallback( $title, $this ); |
| 3438 | + $text = $stuff['text']; |
| 3439 | + $finalTitle = isset( $stuff['finalTitle'] ) ? $stuff['finalTitle'] : $title; |
| 3440 | + if ( isset( $stuff['deps'] ) ) { |
| 3441 | + foreach ( $stuff['deps'] as $dep ) { |
| 3442 | + $this->mOutput->addDistantTemplate( $dep['title'], $dep['page_id'], $dep['rev_id'] ); |
| 3443 | + } |
| 3444 | + } |
| 3445 | + } |
| 3446 | + |
3432 | 3447 | function fetchTemplate( $title ) { |
3433 | 3448 | $rv = $this->fetchTemplateAndTitle( $title ); |
3434 | 3449 | return $rv[0]; |
Index: branches/iwtransclusion/phase3v2/includes/parser/ParserOutput.php |
— | — | @@ -293,6 +293,31 @@ |
294 | 294 | $this->mTemplateIds[$ns][$dbk] = $rev_id; // For versioning |
295 | 295 | } |
296 | 296 | |
| 297 | + function addDistantTemplate( $title, $page_id, $rev_id ) { |
| 298 | + $prefix = $title->getInterwiki(); |
| 299 | + if ( $prefix !=='' ) { |
| 300 | + $ns = $title->getNamespace(); |
| 301 | + $dbk = $title->getDBkey(); |
| 302 | + |
| 303 | + if ( !isset( $this->mDistantTemplates[$prefix] ) ) { |
| 304 | + $this->mDistantTemplates[$prefix] = array(); |
| 305 | + } |
| 306 | + if ( !isset( $this->mDistantTemplates[$prefix][$ns] ) ) { |
| 307 | + $this->mDistantTemplates[$prefix][$ns] = array(); |
| 308 | + } |
| 309 | + $this->mDistantTemplates[$prefix][$ns][$dbk] = $page_id; |
| 310 | + |
| 311 | + // For versioning |
| 312 | + if ( !isset( $this->mDistantTemplateIds[$prefix] ) ) { |
| 313 | + $this->mDistantTemplateIds[$prefix] = array(); |
| 314 | + } |
| 315 | + if ( !isset( $this->mDistantTemplateIds[$prefix][$ns] ) ) { |
| 316 | + $this->mDistantTemplateIds[$prefix][$ns] = array(); |
| 317 | + } |
| 318 | + $this->mDistantTemplateIds[$prefix][$ns][$dbk] = $rev_id; |
| 319 | + } |
| 320 | + } |
| 321 | + |
297 | 322 | /** |
298 | 323 | * @param $title Title object, must be an interwiki link |
299 | 324 | * @throws MWException if given invalid input |
Index: branches/iwtransclusion/phase3v2/includes/Linker.php |
— | — | @@ -1581,6 +1581,48 @@ |
1582 | 1582 | } |
1583 | 1583 | |
1584 | 1584 | /** |
| 1585 | + * Returns HTML for the "templates used on this page" list. |
| 1586 | + * |
| 1587 | + * @param $templates Array of templates from Article::getUsedTemplate |
| 1588 | + * or similar |
| 1589 | + * @param $preview Boolean: whether this is for a preview |
| 1590 | + * @param $section Boolean: whether this is for a section edit |
| 1591 | + * @return String: HTML output |
| 1592 | + */ |
| 1593 | + public function formatDistantTemplates( $templates, $preview = false, $section = false ) { |
| 1594 | + wfProfileIn( __METHOD__ ); |
| 1595 | + |
| 1596 | + $outText = ''; |
| 1597 | + if ( count( $templates ) > 0 ) { |
| 1598 | + # Do a batch existence check |
| 1599 | + $batch = new LinkBatch; |
| 1600 | + foreach( $templates as $title ) { |
| 1601 | + $batch->addObj( $title ); |
| 1602 | + } |
| 1603 | + $batch->execute(); |
| 1604 | + |
| 1605 | + # Construct the HTML |
| 1606 | + $outText = '<div class="mw-templatesUsedExplanation">'; |
| 1607 | + if ( $preview ) { |
| 1608 | + $outText .= wfMsgExt( 'distanttemplatesusedpreview', array( 'parse' ), count( $templates ) ); |
| 1609 | + } elseif ( $section ) { |
| 1610 | + $outText .= wfMsgExt( 'distanttemplatesusedsection', array( 'parse' ), count( $templates ) ); |
| 1611 | + } else { |
| 1612 | + $outText .= wfMsgExt( 'distanttemplatesused', array( 'parse' ), count( $templates ) ); |
| 1613 | + } |
| 1614 | + $outText .= "</div><ul>\n"; |
| 1615 | + |
| 1616 | + usort( $templates, array( 'Title', 'compare' ) ); |
| 1617 | + foreach ( $templates as $titleObj ) { |
| 1618 | + $outText .= '<li>' . $this->link( $titleObj ) . '</li>'; |
| 1619 | + } |
| 1620 | + $outText .= '</ul>'; |
| 1621 | + } |
| 1622 | + wfProfileOut( __METHOD__ ); |
| 1623 | + return $outText; |
| 1624 | + } |
| 1625 | + |
| 1626 | + /** |
1585 | 1627 | * Returns HTML for the "hidden categories on this page" list. |
1586 | 1628 | * |
1587 | 1629 | * @param $hiddencats Array of hidden categories from Article::getHiddenCategories |
Index: branches/iwtransclusion/phase3v2/includes/db/Database.php |
— | — | @@ -1456,6 +1456,31 @@ |
1457 | 1457 | return false; |
1458 | 1458 | } |
1459 | 1459 | } |
| 1460 | + |
| 1461 | + /** |
| 1462 | + * Build a partial where clause from a 3-d array |
| 1463 | + * The keys on each level may be either integers or strings. |
| 1464 | + * |
| 1465 | + * @param $data Array: organized as 3-d array(baseKeyVal => array(middleKeyVal => array(subKeyVal => <ignored>, ...), ...), ...) |
| 1466 | + * @param $baseKey String: field name to match the base-level keys to (eg 'gtl_to_prefix') |
| 1467 | + * @param $middleKey String: field name to match the middle-level keys to (eg 'gtl_to_namespace') |
| 1468 | + * @param $subKey String: field name to match the sub-level keys to (eg 'gtl_to_title') |
| 1469 | + * @return Mixed: string SQL fragment, or false if no items in array. |
| 1470 | + */ |
| 1471 | + function makeWhereFrom3d( $data, $baseKey, $middleKey, $subKey ) { |
| 1472 | + $conds = array(); |
| 1473 | + foreach ( $data as $base => $subdata ) { |
| 1474 | + foreach ( $subdata as $middle => $sub ) { |
| 1475 | + if ( count( $sub ) ) { |
| 1476 | + $conds[] = $this->makeList( |
| 1477 | + array( $baseKey => $base, |
| 1478 | + $middleKey => $middle, |
| 1479 | + $subKey => array_keys( $sub ) ), |
| 1480 | + LIST_AND |
| 1481 | + ); |
| 1482 | + } |
| 1483 | + } |
| 1484 | + } |
1460 | 1485 | |
1461 | 1486 | /** |
1462 | 1487 | * Bitwise operations |
Property changes on: branches/iwtransclusion/phase3v2/includes/db/Database.php |
___________________________________________________________________ |
Modified: svn:mergeinfo |
1463 | 1488 | Merged /branches/iwtransclusion/phase3/includes/db/Database.php:r70764 |
Index: branches/iwtransclusion/phase3v2/includes/EditPage.php |
— | — | @@ -1273,8 +1273,8 @@ |
1274 | 1274 | * @param $formCallback Callback that takes an OutputPage parameter; will be called |
1275 | 1275 | * during form output near the top, for captchas and the like. |
1276 | 1276 | */ |
1277 | | - function showEditForm( $formCallback = null ) { |
1278 | | - global $wgOut, $wgUser; |
| 1277 | + function showEditForm( $formCallback=null ) { |
| 1278 | + global $wgOut, $wgUser, $wgTitle, $wgEnableInterwikiTranscluding, $wgEnableInterwikiTemplatesTracking; |
1279 | 1279 | |
1280 | 1280 | wfProfileIn( __METHOD__ ); |
1281 | 1281 | |
— | — | @@ -1321,6 +1321,9 @@ |
1322 | 1322 | |
1323 | 1323 | $templates = $this->getTemplates(); |
1324 | 1324 | $formattedtemplates = $sk->formatTemplates( $templates, $this->preview, $this->section != ''); |
| 1325 | + |
| 1326 | + $distantTemplates = $this->getDistantTemplates(); |
| 1327 | + $formattedDistantTemplates = $sk->formatDistantTemplates( $distantTemplates, $this->preview, $this->section != '' ); |
1325 | 1328 | |
1326 | 1329 | $hiddencats = $this->mArticle->getHiddenCategories(); |
1327 | 1330 | $formattedhiddencats = $sk->formatHiddenCategories( $hiddencats ); |
— | — | @@ -1420,6 +1423,21 @@ |
1421 | 1424 | <div class='templatesUsed'> |
1422 | 1425 | {$formattedtemplates} |
1423 | 1426 | </div> |
| 1427 | +HTML |
| 1428 | +); |
| 1429 | + |
| 1430 | + if ( $wgEnableInterwikiTranscluding && $wgEnableInterwikiTemplatesTracking ) { |
| 1431 | + $wgOut->addHTML( <<<HTML |
| 1432 | +{$this->editFormTextAfterTools} |
| 1433 | +<div class='distantTemplatesUsed'> |
| 1434 | +{$formattedDistantTemplates} |
| 1435 | +</div> |
| 1436 | +HTML |
| 1437 | +); |
| 1438 | + } |
| 1439 | + |
| 1440 | + $wgOut->addHTML( <<<HTML |
| 1441 | +{$this->editFormTextAfterTools} |
1424 | 1442 | <div class='hiddencats'> |
1425 | 1443 | {$formattedhiddencats} |
1426 | 1444 | </div> |
— | — | @@ -2056,6 +2074,28 @@ |
2057 | 2075 | return $this->mArticle->getUsedTemplates(); |
2058 | 2076 | } |
2059 | 2077 | } |
| 2078 | + |
| 2079 | + function getDistantTemplates() { |
| 2080 | + global $wgEnableInterwikiTemplatesTracking; |
| 2081 | + if ( !$wgEnableInterwikiTemplatesTracking ) { |
| 2082 | + return array( ); |
| 2083 | + } |
| 2084 | + if ( $this->preview || $this->section != '' ) { |
| 2085 | + $templates = array(); |
| 2086 | + if ( !isset( $this->mParserOutput ) ) return $templates; |
| 2087 | + $templatesList = $this->mParserOutput->getDistantTemplates(); |
| 2088 | + foreach( $templatesList as $prefix => $templatesbyns ) { |
| 2089 | + foreach( $templatesbyns as $ns => $template ) { |
| 2090 | + foreach( array_keys( $template ) as $dbk ) { |
| 2091 | + $templates[] = Title::makeTitle( $ns, $dbk, null, $prefix ); |
| 2092 | + } |
| 2093 | + } |
| 2094 | + } |
| 2095 | + return $templates; |
| 2096 | + } else { |
| 2097 | + return $this->mArticle->getUsedDistantTemplates(); |
| 2098 | + } |
| 2099 | + } |
2060 | 2100 | |
2061 | 2101 | /** |
2062 | 2102 | * Call the stock "user is blocked" page |
Index: branches/iwtransclusion/phase3v2/includes/OutputPage.php |
— | — | @@ -2098,6 +2098,9 @@ |
2099 | 2099 | * @param $action String: action that was denied or null if unknown |
2100 | 2100 | */ |
2101 | 2101 | public function readOnlyPage( $source = null, $protected = false, $reasons = array(), $action = null ) { |
| 2102 | + global $wgUser, $wgEnableInterwikiTranscluding, $wgEnableInterwikiTemplatesTracking; |
| 2103 | + $skin = $wgUser->getSkin(); |
| 2104 | + |
2102 | 2105 | $this->setRobotPolicy( 'noindex,nofollow' ); |
2103 | 2106 | $this->setArticleRelated( false ); |
2104 | 2107 | |
— | — | @@ -2143,6 +2146,12 @@ |
2144 | 2147 | {$skin->formatTemplates( $article->getUsedTemplates() )} |
2145 | 2148 | </div> |
2146 | 2149 | " ); |
| 2150 | + if ( $wgEnableInterwikiTranscluding && $wgEnableInterwikiTemplatesTracking ) { |
| 2151 | + $this->addHTML( "<div class='distantTemplatesUsed'> |
| 2152 | +{$skin->formatDistantTemplates( $article->getUsedDistantTemplates( ) )} |
| 2153 | +</div> |
| 2154 | +" ); |
| 2155 | + } |
2147 | 2156 | } |
2148 | 2157 | |
2149 | 2158 | # If the title doesn't exist, it's fairly pointless to print a return |
Property changes on: branches/iwtransclusion/phase3v2/includes/OutputPage.php |
___________________________________________________________________ |
Modified: svn:mergeinfo |
2150 | 2159 | Merged /branches/iwtransclusion/phase3/includes/OutputPage.php:r70764 |
Index: branches/iwtransclusion/phase3v2/includes/Title.php |
— | — | @@ -5,11 +5,16 @@ |
6 | 6 | */ |
7 | 7 | |
8 | 8 | /** |
9 | | - * @deprecated This used to be a define, but was moved to |
10 | | - * Title::GAID_FOR_UPDATE in 1.17. This will probably be removed in 1.18 |
| 9 | + * @todo: determine if it is really necessary to load this. Appears to be left over from pre-autoloader versions, and |
| 10 | + * is only really needed to provide access to constant UTF8_REPLACEMENT, which actually resides in UtfNormalDefines.php |
| 11 | + * and is loaded by UtfNormalUtil.php, which is loaded by UtfNormal.php. |
11 | 12 | */ |
12 | | -define( 'GAID_FOR_UPDATE', Title::GAID_FOR_UPDATE ); |
| 13 | +if ( !class_exists( 'UtfNormal' ) ) { |
| 14 | + require_once( dirname( __FILE__ ) . '/normal/UtfNormal.php' ); |
| 15 | +} |
13 | 16 | |
| 17 | +define ( 'GAID_FOR_UPDATE', 1 ); |
| 18 | + |
14 | 19 | /** |
15 | 20 | * Represents a title within MediaWiki. |
16 | 21 | * Optionally may contain an interwiki designation or namespace. |
— | — | @@ -31,13 +36,7 @@ |
32 | 37 | */ |
33 | 38 | const CACHE_MAX = 1000; |
34 | 39 | |
35 | | - /** |
36 | | - * Used to be GAID_FOR_UPDATE define. Used with getArticleID() and friends |
37 | | - * to use the master DB |
38 | | - */ |
39 | | - const GAID_FOR_UPDATE = 1; |
40 | 40 | |
41 | | - |
42 | 41 | /** |
43 | 42 | * @name Private member variables |
44 | 43 | * Please use the accessor functions instead. |
— | — | @@ -63,11 +62,11 @@ |
64 | 63 | var $mCascadeSources; ///< Where are the cascading restrictions coming from on this page? |
65 | 64 | var $mRestrictionsLoaded = false; ///< Boolean for initialisation on demand |
66 | 65 | var $mPrefixedText; ///< Text form including namespace/interwiki, initialised on demand |
67 | | - var $mTitleProtection; ///< Cached value for getTitleProtection (create protection) |
| 66 | + var $mTitleProtection; ///< Cached value of getTitleProtection |
68 | 67 | # Don't change the following default, NS_MAIN is hardcoded in several |
69 | 68 | # places. See bug 696. |
70 | 69 | var $mDefaultNamespace = NS_MAIN; // /< Namespace index when there is no namespace |
71 | | - # Zero except in {{transclusion}} tags |
| 70 | + # Zero except in {{transclusion}} tags |
72 | 71 | var $mWatched = null; // /< Is $wgUser watching this page? null if unfilled, accessed through userIsWatching() |
73 | 72 | var $mLength = -1; // /< The page length, 0 for special pages |
74 | 73 | var $mRedirect = null; // /< Is the article at this title a redirect? |
— | — | @@ -78,16 +77,17 @@ |
79 | 78 | |
80 | 79 | /** |
81 | 80 | * Constructor |
| 81 | + * @private |
82 | 82 | */ |
83 | | - /*protected*/ function __construct() { } |
| 83 | + /* private */ function __construct() { } |
84 | 84 | |
85 | 85 | /** |
86 | 86 | * Create a new Title from a prefixed DB key |
87 | 87 | * |
88 | | - * @param $key String the database key, which has underscores |
| 88 | + * @param $key \type{\string} The database key, which has underscores |
89 | 89 | * instead of spaces, possibly including namespace and |
90 | 90 | * interwiki prefixes |
91 | | - * @return Title, or NULL on an error |
| 91 | + * @return \type{Title} the new object, or NULL on an error |
92 | 92 | */ |
93 | 93 | public static function newFromDBkey( $key ) { |
94 | 94 | $t = new Title(); |
— | — | @@ -103,13 +103,13 @@ |
104 | 104 | * Create a new Title from text, such as what one would find in a link. De- |
105 | 105 | * codes any HTML entities in the text. |
106 | 106 | * |
107 | | - * @param $text String the link text; spaces, prefixes, and an |
| 107 | + * @param $text string The link text; spaces, prefixes, and an |
108 | 108 | * initial ':' indicating the main namespace are accepted. |
109 | | - * @param $defaultNamespace Int the namespace to use if none is speci- |
| 109 | + * @param $defaultNamespace int The namespace to use if none is speci- |
110 | 110 | * fied by a prefix. If you want to force a specific namespace even if |
111 | 111 | * $text might begin with a namespace prefix, use makeTitle() or |
112 | 112 | * makeTitleSafe(). |
113 | | - * @return Title, or null on an error. |
| 113 | + * @return Title The new object, or null on an error. |
114 | 114 | */ |
115 | 115 | public static function newFromText( $text, $defaultNamespace = NS_MAIN ) { |
116 | 116 | if ( is_object( $text ) ) { |
— | — | @@ -128,7 +128,9 @@ |
129 | 129 | return Title::$titleCache[$text]; |
130 | 130 | } |
131 | 131 | |
132 | | - # Convert things like é ā or 〗 into normalized (bug 14952) text |
| 132 | + /** |
| 133 | + * Convert things like é ā or 〗 into normalized(bug 14952) text |
| 134 | + */ |
133 | 135 | $filteredText = Sanitizer::decodeCharReferencesAndNormalize( $text ); |
134 | 136 | |
135 | 137 | $t = new Title(); |
— | — | @@ -165,8 +167,8 @@ |
166 | 168 | * Create a new Title from URL-encoded text. Ensures that |
167 | 169 | * the given title's length does not exceed the maximum. |
168 | 170 | * |
169 | | - * @param $url String the title, as might be taken from a URL |
170 | | - * @return Title the new object, or NULL on an error |
| 171 | + * @param $url \type{\string} the title, as might be taken from a URL |
| 172 | + * @return \type{Title} the new object, or NULL on an error |
171 | 173 | */ |
172 | 174 | public static function newFromURL( $url ) { |
173 | 175 | global $wgLegalTitleChars; |
— | — | @@ -190,12 +192,12 @@ |
191 | 193 | /** |
192 | 194 | * Create a new Title from an article ID |
193 | 195 | * |
194 | | - * @param $id Int the page_id corresponding to the Title to create |
195 | | - * @param $flags Int use Title::GAID_FOR_UPDATE to use master |
196 | | - * @return Title the new object, or NULL on an error |
| 196 | + * @param $id \type{\int} the page_id corresponding to the Title to create |
| 197 | + * @param $flags \type{\int} use GAID_FOR_UPDATE to use master |
| 198 | + * @return \type{Title} the new object, or NULL on an error |
197 | 199 | */ |
198 | 200 | public static function newFromID( $id, $flags = 0 ) { |
199 | | - $db = ( $flags & self::GAID_FOR_UPDATE ) ? wfGetDB( DB_MASTER ) : wfGetDB( DB_SLAVE ); |
| 201 | + $db = ( $flags & GAID_FOR_UPDATE ) ? wfGetDB( DB_MASTER ) : wfGetDB( DB_SLAVE ); |
200 | 202 | $row = $db->selectRow( 'page', '*', array( 'page_id' => $id ), __METHOD__ ); |
201 | 203 | if ( $row !== false ) { |
202 | 204 | $title = Title::newFromRow( $row ); |
— | — | @@ -208,15 +210,15 @@ |
209 | 211 | /** |
210 | 212 | * Make an array of titles from an array of IDs |
211 | 213 | * |
212 | | - * @param $ids Array of Int Array of IDs |
213 | | - * @return Array of Titles |
| 214 | + * @param $ids \type{\arrayof{\int}} Array of IDs |
| 215 | + * @return \type{\arrayof{Title}} Array of Titles |
214 | 216 | */ |
215 | 217 | public static function newFromIDs( $ids ) { |
216 | 218 | if ( !count( $ids ) ) { |
217 | 219 | return array(); |
218 | 220 | } |
219 | 221 | $dbr = wfGetDB( DB_SLAVE ); |
220 | | - |
| 222 | + |
221 | 223 | $res = $dbr->select( |
222 | 224 | 'page', |
223 | 225 | array( |
— | — | @@ -237,8 +239,8 @@ |
238 | 240 | /** |
239 | 241 | * Make a Title object from a DB row |
240 | 242 | * |
241 | | - * @param $row Object database row (needs at least page_title,page_namespace) |
242 | | - * @return Title corresponding Title |
| 243 | + * @param $row \type{Row} (needs at least page_title,page_namespace) |
| 244 | + * @return \type{Title} corresponding Title |
243 | 245 | */ |
244 | 246 | public static function newFromRow( $row ) { |
245 | 247 | $t = self::makeTitle( $row->page_namespace, $row->page_title ); |
— | — | @@ -258,11 +260,11 @@ |
259 | 261 | * For convenience, spaces are converted to underscores so that |
260 | 262 | * eg user_text fields can be used directly. |
261 | 263 | * |
262 | | - * @param $ns Int the namespace of the article |
263 | | - * @param $title String the unprefixed database key form |
264 | | - * @param $fragment String the link fragment (after the "#") |
265 | | - * @param $interwiki String the interwiki prefix |
266 | | - * @return Title the new object |
| 264 | + * @param $ns \type{\int} the namespace of the article |
| 265 | + * @param $title \type{\string} the unprefixed database key form |
| 266 | + * @param $fragment \type{\string} The link fragment (after the "#") |
| 267 | + * @param $interwiki \type{\string} The interwiki prefix |
| 268 | + * @return \type{Title} the new object |
267 | 269 | */ |
268 | 270 | public static function &makeTitle( $ns, $title, $fragment = '', $interwiki = '' ) { |
269 | 271 | $t = new Title(); |
— | — | @@ -281,11 +283,11 @@ |
282 | 284 | * The parameters will be checked for validity, which is a bit slower |
283 | 285 | * than makeTitle() but safer for user-provided data. |
284 | 286 | * |
285 | | - * @param $ns Int the namespace of the article |
286 | | - * @param $title String database key form |
287 | | - * @param $fragment String the link fragment (after the "#") |
288 | | - * @param $interwiki String interwiki prefix |
289 | | - * @return Title the new object, or NULL on an error |
| 287 | + * @param $ns \type{\int} the namespace of the article |
| 288 | + * @param $title \type{\string} the database key form |
| 289 | + * @param $fragment \type{\string} The link fragment (after the "#") |
| 290 | + * @param $interwiki \type{\string} The interwiki prefix |
| 291 | + * @return \type{Title} the new object, or NULL on an error |
290 | 292 | */ |
291 | 293 | public static function makeTitleSafe( $ns, $title, $fragment = '', $interwiki = '' ) { |
292 | 294 | $t = new Title(); |
— | — | @@ -300,7 +302,7 @@ |
301 | 303 | /** |
302 | 304 | * Create a new Title for the Main Page |
303 | 305 | * |
304 | | - * @return Title the new object |
| 306 | + * @return \type{Title} the new object |
305 | 307 | */ |
306 | 308 | public static function newMainPage() { |
307 | 309 | $title = Title::newFromText( wfMsgForContent( 'mainpage' ) ); |
— | — | @@ -317,8 +319,8 @@ |
318 | 320 | * This will only return the very next target, useful for |
319 | 321 | * the redirect table and other checks that don't need full recursion |
320 | 322 | * |
321 | | - * @param $text String: Text with possible redirect |
322 | | - * @return Title: The corresponding Title |
| 323 | + * @param $text \type{\string} Text with possible redirect |
| 324 | + * @return \type{Title} The corresponding Title |
323 | 325 | */ |
324 | 326 | public static function newFromRedirect( $text ) { |
325 | 327 | return self::newFromRedirectInternal( $text ); |
— | — | @@ -330,8 +332,8 @@ |
331 | 333 | * This will recurse down $wgMaxRedirects times or until a non-redirect target is hit |
332 | 334 | * in order to provide (hopefully) the Title of the final destination instead of another redirect |
333 | 335 | * |
334 | | - * @param $text String Text with possible redirect |
335 | | - * @return Title |
| 336 | + * @param $text \type{\string} Text with possible redirect |
| 337 | + * @return \type{Title} The corresponding Title |
336 | 338 | */ |
337 | 339 | public static function newFromRedirectRecurse( $text ) { |
338 | 340 | $titles = self::newFromRedirectArray( $text ); |
— | — | @@ -344,11 +346,15 @@ |
345 | 347 | * The last element in the array is the final destination after all redirects |
346 | 348 | * have been resolved (up to $wgMaxRedirects times) |
347 | 349 | * |
348 | | - * @param $text String Text with possible redirect |
349 | | - * @return Array of Titles, with the destination last |
| 350 | + * @param $text \type{\string} Text with possible redirect |
| 351 | + * @return \type{\array} Array of Titles, with the destination last |
350 | 352 | */ |
351 | 353 | public static function newFromRedirectArray( $text ) { |
352 | 354 | global $wgMaxRedirects; |
| 355 | + // are redirects disabled? |
| 356 | + if ( $wgMaxRedirects < 1 ) { |
| 357 | + return null; |
| 358 | + } |
353 | 359 | $title = self::newFromRedirectInternal( $text ); |
354 | 360 | if ( is_null( $title ) ) { |
355 | 361 | return null; |
— | — | @@ -379,15 +385,10 @@ |
380 | 386 | * Really extract the redirect destination |
381 | 387 | * Do not call this function directly, use one of the newFromRedirect* functions above |
382 | 388 | * |
383 | | - * @param $text String Text with possible redirect |
384 | | - * @return Title |
| 389 | + * @param $text \type{\string} Text with possible redirect |
| 390 | + * @return \type{Title} The corresponding Title |
385 | 391 | */ |
386 | 392 | protected static function newFromRedirectInternal( $text ) { |
387 | | - global $wgMaxRedirects; |
388 | | - if ( $wgMaxRedirects < 1 ) { |
389 | | - //redirects are disabled, so quit early |
390 | | - return null; |
391 | | - } |
392 | 393 | $redir = MagicWord::get( 'redirect' ); |
393 | 394 | $text = trim( $text ); |
394 | 395 | if ( $redir->matchStartAndRemove( $text ) ) { |
— | — | @@ -400,7 +401,9 @@ |
401 | 402 | // and URL-decode links |
402 | 403 | if ( strpos( $m[1], '%' ) !== false ) { |
403 | 404 | // Match behavior of inline link parsing here; |
404 | | - $m[1] = rawurldecode( ltrim( $m[1], ':' ) ); |
| 405 | + // don't interpret + as " " most of the time! |
| 406 | + // It might be safe to just use rawurldecode instead, though. |
| 407 | + $m[1] = urldecode( ltrim( $m[1], ':' ) ); |
405 | 408 | } |
406 | 409 | $title = Title::newFromText( $m[1] ); |
407 | 410 | // If the title is a redirect to bad special pages or is invalid, return null |
— | — | @@ -420,8 +423,9 @@ |
421 | 424 | /** |
422 | 425 | * Get the prefixed DB key associated with an ID |
423 | 426 | * |
424 | | - * @param $id Int the page_id of the article |
425 | | - * @return Title an object representing the article, or NULL if no such article was found |
| 427 | + * @param $id \type{\int} the page_id of the article |
| 428 | + * @return \type{Title} an object representing the article, or NULL |
| 429 | + * if no such article was found |
426 | 430 | */ |
427 | 431 | public static function nameOf( $id ) { |
428 | 432 | $dbr = wfGetDB( DB_SLAVE ); |
— | — | @@ -443,7 +447,7 @@ |
444 | 448 | /** |
445 | 449 | * Get a regex character class describing the legal characters in a link |
446 | 450 | * |
447 | | - * @return String the list of characters, not delimited |
| 451 | + * @return \type{\string} the list of characters, not delimited |
448 | 452 | */ |
449 | 453 | public static function legalChars() { |
450 | 454 | global $wgLegalTitleChars; |
— | — | @@ -454,9 +458,10 @@ |
455 | 459 | * Get a string representation of a title suitable for |
456 | 460 | * including in a search index |
457 | 461 | * |
458 | | - * @param $ns Int a namespace index |
459 | | - * @param $title String text-form main part |
460 | | - * @return String a stripped-down title string ready for the search index |
| 462 | + * @param $ns \type{\int} a namespace index |
| 463 | + * @param $title \type{\string} text-form main part |
| 464 | + * @return \type{\string} a stripped-down title string ready for the |
| 465 | + * search index |
461 | 466 | */ |
462 | 467 | public static function indexTitle( $ns, $title ) { |
463 | 468 | global $wgContLang; |
— | — | @@ -481,11 +486,11 @@ |
482 | 487 | /** |
483 | 488 | * Make a prefixed DB key from a DB key and a namespace index |
484 | 489 | * |
485 | | - * @param $ns Int numerical representation of the namespace |
486 | | - * @param $title String the DB key form the title |
487 | | - * @param $fragment String The link fragment (after the "#") |
488 | | - * @param $interwiki String The interwiki prefix |
489 | | - * @return String the prefixed form of the title |
| 490 | + * @param $ns \type{\int} numerical representation of the namespace |
| 491 | + * @param $title \type{\string} the DB key form the title |
| 492 | + * @param $fragment \type{\string} The link fragment (after the "#") |
| 493 | + * @param $interwiki \type{\string} The interwiki prefix |
| 494 | + * @return \type{\string} the prefixed form of the title |
490 | 495 | */ |
491 | 496 | public static function makeName( $ns, $title, $fragment = '', $interwiki = '' ) { |
492 | 497 | global $wgContLang; |
— | — | @@ -505,7 +510,8 @@ |
506 | 511 | * Determine whether the object refers to a page within |
507 | 512 | * this project. |
508 | 513 | * |
509 | | - * @return Bool TRUE if this is an in-project interwiki link or a wikilink, FALSE otherwise |
| 514 | + * @return \type{\bool} TRUE if this is an in-project interwiki link |
| 515 | + * or a wikilink, FALSE otherwise |
510 | 516 | */ |
511 | 517 | public function isLocal() { |
512 | 518 | if ( $this->mInterwiki != '' ) { |
— | — | @@ -519,7 +525,7 @@ |
520 | 526 | * Determine whether the object refers to a page within |
521 | 527 | * this project and is transcludable. |
522 | 528 | * |
523 | | - * @return Bool TRUE if this is transcludable |
| 529 | + * @return \type{\bool} TRUE if this is transcludable |
524 | 530 | */ |
525 | 531 | public function isTrans() { |
526 | 532 | if ( $this->mInterwiki == '' ) { |
— | — | @@ -528,11 +534,25 @@ |
529 | 535 | |
530 | 536 | return Interwiki::fetch( $this->mInterwiki )->isTranscludable(); |
531 | 537 | } |
| 538 | + |
| 539 | + /** |
| 540 | + * Returns the API URL of the distant wiki |
| 541 | + * which owns the object. |
| 542 | + * |
| 543 | + * @return \type{\string} the API URL |
| 544 | + */ |
| 545 | + public function getTransAPI() { |
| 546 | + if ( $this->mInterwiki == '' ) |
| 547 | + return false; |
532 | 548 | |
| 549 | + return Interwiki::fetch( $this->mInterwiki )->getAPI(); |
| 550 | + } |
| 551 | + |
533 | 552 | /** |
534 | | - * Returns the DB name of the distant wiki which owns the object. |
| 553 | + * Returns the DB name of the distant wiki |
| 554 | + * which owns the object. |
535 | 555 | * |
536 | | - * @return String the DB name |
| 556 | + * @return \type{\string} the DB name |
537 | 557 | */ |
538 | 558 | public function getTransWikiID() { |
539 | 559 | if ( $this->mInterwiki == '' ) { |
— | — | @@ -564,35 +584,35 @@ |
565 | 585 | /** |
566 | 586 | * Get the text form (spaces not underscores) of the main part |
567 | 587 | * |
568 | | - * @return String Main part of the title |
| 588 | + * @return \type{\string} Main part of the title |
569 | 589 | */ |
570 | 590 | public function getText() { return $this->mTextform; } |
571 | 591 | |
572 | 592 | /** |
573 | 593 | * Get the URL-encoded form of the main part |
574 | 594 | * |
575 | | - * @return String Main part of the title, URL-encoded |
| 595 | + * @return \type{\string} Main part of the title, URL-encoded |
576 | 596 | */ |
577 | 597 | public function getPartialURL() { return $this->mUrlform; } |
578 | 598 | |
579 | 599 | /** |
580 | 600 | * Get the main part with underscores |
581 | 601 | * |
582 | | - * @return String: Main part of the title, with underscores |
| 602 | + * @return \type{\string} Main part of the title, with underscores |
583 | 603 | */ |
584 | 604 | public function getDBkey() { return $this->mDbkeyform; } |
585 | 605 | |
586 | 606 | /** |
587 | | - * Get the namespace index, i.e. one of the NS_xxxx constants. |
| 607 | + * Get the namespace index, i.e.\ one of the NS_xxxx constants. |
588 | 608 | * |
589 | | - * @return Integer: Namespace index |
| 609 | + * @return \type{\int} Namespace index |
590 | 610 | */ |
591 | 611 | public function getNamespace() { return $this->mNamespace; } |
592 | 612 | |
593 | 613 | /** |
594 | 614 | * Get the namespace text |
595 | 615 | * |
596 | | - * @return String: Namespace text |
| 616 | + * @return \type{\string} Namespace text |
597 | 617 | */ |
598 | 618 | public function getNsText() { |
599 | 619 | global $wgContLang; |
— | — | @@ -608,20 +628,13 @@ |
609 | 629 | return MWNamespace::getCanonicalName( $this->mNamespace ); |
610 | 630 | } |
611 | 631 | } |
612 | | - |
613 | | - if ( $wgContLang->needsGenderDistinction() && |
614 | | - MWNamespace::hasGenderDistinction( $this->mNamespace ) ) { |
615 | | - $gender = GenderCache::singleton()->getGenderOf( $this->getText(), __METHOD__ ); |
616 | | - return $wgContLang->getGenderNsText( $this->mNamespace, $gender ); |
617 | | - } |
618 | | - |
619 | 632 | return $wgContLang->getNsText( $this->mNamespace ); |
620 | 633 | } |
621 | 634 | |
622 | 635 | /** |
623 | 636 | * Get the DB key with the initial letter case as specified by the user |
624 | 637 | * |
625 | | - * @return String DB key |
| 638 | + * @return \type{\string} DB key |
626 | 639 | */ |
627 | 640 | function getUserCaseDBKey() { |
628 | 641 | return $this->mUserCaseDBKey; |
— | — | @@ -630,7 +643,7 @@ |
631 | 644 | /** |
632 | 645 | * Get the namespace text of the subject (rather than talk) page |
633 | 646 | * |
634 | | - * @return String Namespace text |
| 647 | + * @return \type{\string} Namespace text |
635 | 648 | */ |
636 | 649 | public function getSubjectNsText() { |
637 | 650 | global $wgContLang; |
— | — | @@ -640,7 +653,7 @@ |
641 | 654 | /** |
642 | 655 | * Get the namespace text of the talk page |
643 | 656 | * |
644 | | - * @return String Namespace text |
| 657 | + * @return \type{\string} Namespace text |
645 | 658 | */ |
646 | 659 | public function getTalkNsText() { |
647 | 660 | global $wgContLang; |
— | — | @@ -650,7 +663,7 @@ |
651 | 664 | /** |
652 | 665 | * Could this title have a corresponding talk page? |
653 | 666 | * |
654 | | - * @return Bool TRUE or FALSE |
| 667 | + * @return \type{\bool} TRUE or FALSE |
655 | 668 | */ |
656 | 669 | public function canTalk() { |
657 | 670 | return( MWNamespace::canTalk( $this->mNamespace ) ); |
— | — | @@ -659,20 +672,20 @@ |
660 | 673 | /** |
661 | 674 | * Get the interwiki prefix (or null string) |
662 | 675 | * |
663 | | - * @return String Interwiki prefix |
| 676 | + * @return \type{\string} Interwiki prefix |
664 | 677 | */ |
665 | 678 | public function getInterwiki() { return $this->mInterwiki; } |
666 | 679 | |
667 | 680 | /** |
668 | 681 | * Get the Title fragment (i.e.\ the bit after the #) in text form |
669 | 682 | * |
670 | | - * @return String Title fragment |
| 683 | + * @return \type{\string} Title fragment |
671 | 684 | */ |
672 | 685 | public function getFragment() { return $this->mFragment; } |
673 | 686 | |
674 | 687 | /** |
675 | 688 | * Get the fragment in URL form, including the "#" character if there is one |
676 | | - * @return String Fragment in URL form |
| 689 | + * @return \type{\string} Fragment in URL form |
677 | 690 | */ |
678 | 691 | public function getFragmentForURL() { |
679 | 692 | if ( $this->mFragment == '' ) { |
— | — | @@ -685,14 +698,14 @@ |
686 | 699 | /** |
687 | 700 | * Get the default namespace index, for when there is no namespace |
688 | 701 | * |
689 | | - * @return Int Default namespace index |
| 702 | + * @return \type{\int} Default namespace index |
690 | 703 | */ |
691 | 704 | public function getDefaultNamespace() { return $this->mDefaultNamespace; } |
692 | 705 | |
693 | 706 | /** |
694 | 707 | * Get title for search index |
695 | 708 | * |
696 | | - * @return String a stripped-down title string ready for the |
| 709 | + * @return \type{\string} a stripped-down title string ready for the |
697 | 710 | * search index |
698 | 711 | */ |
699 | 712 | public function getIndexTitle() { |
— | — | @@ -702,7 +715,7 @@ |
703 | 716 | /** |
704 | 717 | * Get the prefixed database key form |
705 | 718 | * |
706 | | - * @return String the prefixed title, with underscores and |
| 719 | + * @return \type{\string} the prefixed title, with underscores and |
707 | 720 | * any interwiki and namespace prefixes |
708 | 721 | */ |
709 | 722 | public function getPrefixedDBkey() { |
— | — | @@ -715,7 +728,7 @@ |
716 | 729 | * Get the prefixed title with spaces. |
717 | 730 | * This is the form usually used for display |
718 | 731 | * |
719 | | - * @return String the prefixed title, with spaces |
| 732 | + * @return \type{\string} the prefixed title, with spaces |
720 | 733 | */ |
721 | 734 | public function getPrefixedText() { |
722 | 735 | if ( empty( $this->mPrefixedText ) ) { // FIXME: bad usage of empty() ? |
— | — | @@ -744,7 +757,8 @@ |
745 | 758 | * Get the prefixed title with spaces, plus any fragment |
746 | 759 | * (part beginning with '#') |
747 | 760 | * |
748 | | - * @return String the prefixed title, with spaces and the fragment, including '#' |
| 761 | + * @return \type{\string} the prefixed title, with spaces and |
| 762 | + * the fragment, including '#' |
749 | 763 | */ |
750 | 764 | public function getFullText() { |
751 | 765 | $text = $this->getPrefixedText(); |
— | — | @@ -757,7 +771,7 @@ |
758 | 772 | /** |
759 | 773 | * Get the base name, i.e. the leftmost parts before the / |
760 | 774 | * |
761 | | - * @return String Base name |
| 775 | + * @return \type{\string} Base name |
762 | 776 | */ |
763 | 777 | public function getBaseText() { |
764 | 778 | if ( !MWNamespace::hasSubpages( $this->mNamespace ) ) { |
— | — | @@ -775,7 +789,7 @@ |
776 | 790 | /** |
777 | 791 | * Get the lowest-level subpage name, i.e. the rightmost part after / |
778 | 792 | * |
779 | | - * @return String Subpage name |
| 793 | + * @return \type{\string} Subpage name |
780 | 794 | */ |
781 | 795 | public function getSubpageText() { |
782 | 796 | if ( !MWNamespace::hasSubpages( $this->mNamespace ) ) { |
— | — | @@ -788,7 +802,7 @@ |
789 | 803 | /** |
790 | 804 | * Get a URL-encoded form of the subpage text |
791 | 805 | * |
792 | | - * @return String URL-encoded subpage name |
| 806 | + * @return \type{\string} URL-encoded subpage name |
793 | 807 | */ |
794 | 808 | public function getSubpageUrlForm() { |
795 | 809 | $text = $this->getSubpageText(); |
— | — | @@ -799,7 +813,7 @@ |
800 | 814 | /** |
801 | 815 | * Get a URL-encoded title (not an actual URL) including interwiki |
802 | 816 | * |
803 | | - * @return String the URL-encoded form |
| 817 | + * @return \type{\string} the URL-encoded form |
804 | 818 | */ |
805 | 819 | public function getPrefixedURL() { |
806 | 820 | $s = $this->prefix( $this->mDbkeyform ); |
— | — | @@ -814,8 +828,8 @@ |
815 | 829 | * @param $query \twotypes{\string,\array} an optional query string, not used for interwiki |
816 | 830 | * links. Can be specified as an associative array as well, e.g., |
817 | 831 | * array( 'action' => 'edit' ) (keys and values will be URL-escaped). |
818 | | - * @param $variant String language variant of url (for sr, zh..) |
819 | | - * @return String the URL |
| 832 | + * @param $variant \type{\string} language variant of url (for sr, zh..) |
| 833 | + * @return \type{\string} the URL |
820 | 834 | */ |
821 | 835 | public function getFullURL( $query = '', $variant = false ) { |
822 | 836 | global $wgServer, $wgRequest; |
— | — | @@ -861,17 +875,25 @@ |
862 | 876 | * $wgArticlePath will be used. Can be specified as an associative array |
863 | 877 | * as well, e.g., array( 'action' => 'edit' ) (keys and values will be |
864 | 878 | * URL-escaped). |
865 | | - * @param $variant String language variant of url (for sr, zh..) |
866 | | - * @return String the URL |
| 879 | + * @param $variant \type{\string} language variant of url (for sr, zh..) |
| 880 | + * @return \type{\string} the URL |
867 | 881 | */ |
868 | 882 | public function getLocalURL( $query = '', $variant = false ) { |
869 | 883 | global $wgArticlePath, $wgScript, $wgServer, $wgRequest; |
870 | | - global $wgVariantArticlePath, $wgContLang; |
| 884 | + global $wgVariantArticlePath, $wgContLang, $wgUser; |
871 | 885 | |
872 | 886 | if ( is_array( $query ) ) { |
873 | 887 | $query = wfArrayToCGI( $query ); |
874 | 888 | } |
875 | 889 | |
| 890 | + // internal links should point to same variant as current page (only anonymous users) |
| 891 | + if ( !$variant && $wgContLang->hasVariants() && !$wgUser->isLoggedIn() ) { |
| 892 | + $pref = $wgContLang->getPreferredVariant( false ); |
| 893 | + if ( $pref != $wgContLang->getCode() ) { |
| 894 | + $variant = $pref; |
| 895 | + } |
| 896 | + } |
| 897 | + |
876 | 898 | if ( $this->isExternal() ) { |
877 | 899 | $url = $this->getFullURL(); |
878 | 900 | if ( $query ) { |
— | — | @@ -942,12 +964,12 @@ |
943 | 965 | * The result obviously should not be URL-escaped, but does need to be |
944 | 966 | * HTML-escaped if it's being output in HTML. |
945 | 967 | * |
946 | | - * @param $query Array of Strings An associative array of key => value pairs for the |
| 968 | + * @param $query \type{\arrayof{\string}} An associative array of key => value pairs for the |
947 | 969 | * query string. Keys and values will be escaped. |
948 | | - * @param $variant String language variant of URL (for sr, zh..). Ignored |
| 970 | + * @param $variant \type{\string} Language variant of URL (for sr, zh..). Ignored |
949 | 971 | * for external links. Default is "false" (same variant as current page, |
950 | 972 | * for anonymous users). |
951 | | - * @return String the URL |
| 973 | + * @return \type{\string} the URL |
952 | 974 | */ |
953 | 975 | public function getLinkUrl( $query = array(), $variant = false ) { |
954 | 976 | wfProfileIn( __METHOD__ ); |
— | — | @@ -966,8 +988,8 @@ |
967 | 989 | * Get an HTML-escaped version of the URL form, suitable for |
968 | 990 | * using in a link, without a server name or fragment |
969 | 991 | * |
970 | | - * @param $query String an optional query string |
971 | | - * @return String the URL |
| 992 | + * @param $query \type{\string} an optional query string |
| 993 | + * @return \type{\string} the URL |
972 | 994 | */ |
973 | 995 | public function escapeLocalURL( $query = '' ) { |
974 | 996 | return htmlspecialchars( $this->getLocalURL( $query ) ); |
— | — | @@ -977,8 +999,8 @@ |
978 | 1000 | * Get an HTML-escaped version of the URL form, suitable for |
979 | 1001 | * using in a link, including the server name and fragment |
980 | 1002 | * |
981 | | - * @param $query String an optional query string |
982 | | - * @return String the URL |
| 1003 | + * @param $query \type{\string} an optional query string |
| 1004 | + * @return \type{\string} the URL |
983 | 1005 | */ |
984 | 1006 | public function escapeFullURL( $query = '' ) { |
985 | 1007 | return htmlspecialchars( $this->getFullURL( $query ) ); |
— | — | @@ -989,14 +1011,13 @@ |
990 | 1012 | * - Used in various Squid-related code, in case we have a different |
991 | 1013 | * internal hostname for the server from the exposed one. |
992 | 1014 | * |
993 | | - * @param $query String an optional query string |
994 | | - * @param $variant String language variant of url (for sr, zh..) |
995 | | - * @return String the URL |
| 1015 | + * @param $query \type{\string} an optional query string |
| 1016 | + * @param $variant \type{\string} language variant of url (for sr, zh..) |
| 1017 | + * @return \type{\string} the URL |
996 | 1018 | */ |
997 | 1019 | public function getInternalURL( $query = '', $variant = false ) { |
998 | | - global $wgInternalServer, $wgServer; |
999 | | - $server = $wgInternalServer !== false ? $wgInternalServer : $wgServer; |
1000 | | - $url = $server . $this->getLocalURL( $query, $variant ); |
| 1020 | + global $wgInternalServer; |
| 1021 | + $url = $wgInternalServer . $this->getLocalURL( $query, $variant ); |
1001 | 1022 | wfRunHooks( 'GetInternalURL', array( &$this, &$url, $query ) ); |
1002 | 1023 | return $url; |
1003 | 1024 | } |
— | — | @@ -1004,7 +1025,7 @@ |
1005 | 1026 | /** |
1006 | 1027 | * Get the edit URL for this Title |
1007 | 1028 | * |
1008 | | - * @return String the URL, or a null string if this is an |
| 1029 | + * @return \type{\string} the URL, or a null string if this is an |
1009 | 1030 | * interwiki link |
1010 | 1031 | */ |
1011 | 1032 | public function getEditURL() { |
— | — | @@ -1020,7 +1041,7 @@ |
1021 | 1042 | * Get the HTML-escaped displayable text form. |
1022 | 1043 | * Used for the title field in <a> tags. |
1023 | 1044 | * |
1024 | | - * @return String the text, including any prefixes |
| 1045 | + * @return \type{\string} the text, including any prefixes |
1025 | 1046 | */ |
1026 | 1047 | public function getEscapedText() { |
1027 | 1048 | return htmlspecialchars( $this->getPrefixedText() ); |
— | — | @@ -1029,7 +1050,7 @@ |
1030 | 1051 | /** |
1031 | 1052 | * Is this Title interwiki? |
1032 | 1053 | * |
1033 | | - * @return Bool |
| 1054 | + * @return \type{\bool} |
1034 | 1055 | */ |
1035 | 1056 | public function isExternal() { |
1036 | 1057 | return ( $this->mInterwiki != '' ); |
— | — | @@ -1038,8 +1059,8 @@ |
1039 | 1060 | /** |
1040 | 1061 | * Is this page "semi-protected" - the *only* protection is autoconfirm? |
1041 | 1062 | * |
1042 | | - * @param $action String Action to check (default: edit) |
1043 | | - * @return Bool |
| 1063 | + * @param $action \type{\string} Action to check (default: edit) |
| 1064 | + * @return \type{\bool} |
1044 | 1065 | */ |
1045 | 1066 | public function isSemiProtected( $action = 'edit' ) { |
1046 | 1067 | if ( $this->exists() ) { |
— | — | @@ -1064,9 +1085,9 @@ |
1065 | 1086 | /** |
1066 | 1087 | * Does the title correspond to a protected article? |
1067 | 1088 | * |
1068 | | - * @param $action String the action the page is protected from, |
| 1089 | + * @param $action \type{\string} the action the page is protected from, |
1069 | 1090 | * by default checks all actions. |
1070 | | - * @return Bool |
| 1091 | + * @return \type{\bool} |
1071 | 1092 | */ |
1072 | 1093 | public function isProtected( $action = '' ) { |
1073 | 1094 | global $wgRestrictionLevels; |
— | — | @@ -1096,7 +1117,7 @@ |
1097 | 1118 | /** |
1098 | 1119 | * Is this a conversion table for the LanguageConverter? |
1099 | 1120 | * |
1100 | | - * @return Bool |
| 1121 | + * @return \type{\bool} |
1101 | 1122 | */ |
1102 | 1123 | public function isConversionTable() { |
1103 | 1124 | if( |
— | — | @@ -1113,7 +1134,7 @@ |
1114 | 1135 | /** |
1115 | 1136 | * Is $wgUser watching this page? |
1116 | 1137 | * |
1117 | | - * @return Bool |
| 1138 | + * @return \type{\bool} |
1118 | 1139 | */ |
1119 | 1140 | public function userIsWatching() { |
1120 | 1141 | global $wgUser; |
— | — | @@ -1138,31 +1159,24 @@ |
1139 | 1160 | * |
1140 | 1161 | * May provide false positives, but should never provide a false negative. |
1141 | 1162 | * |
1142 | | - * @param $action String action that permission needs to be checked for |
1143 | | - * @return Bool |
| 1163 | + * @param $action \type{\string} action that permission needs to be checked for |
| 1164 | + * @return \type{\bool} |
1144 | 1165 | */ |
1145 | 1166 | public function quickUserCan( $action ) { |
1146 | 1167 | return $this->userCan( $action, false ); |
1147 | 1168 | } |
1148 | 1169 | |
1149 | 1170 | /** |
1150 | | - * Determines if $user is unable to edit this page because it has been protected |
| 1171 | + * Determines if $wgUser is unable to edit this page because it has been protected |
1151 | 1172 | * by $wgNamespaceProtection. |
1152 | 1173 | * |
1153 | | - * @param $user User object, $wgUser will be used if not passed |
1154 | | - * @return Bool |
| 1174 | + * @return \type{\bool} |
1155 | 1175 | */ |
1156 | | - public function isNamespaceProtected( User $user = null ) { |
1157 | | - global $wgNamespaceProtection; |
1158 | | - |
1159 | | - if ( $user === null ) { |
1160 | | - global $wgUser; |
1161 | | - $user = $wgUser; |
1162 | | - } |
1163 | | - |
| 1176 | + public function isNamespaceProtected() { |
| 1177 | + global $wgNamespaceProtection, $wgUser; |
1164 | 1178 | if ( isset( $wgNamespaceProtection[$this->mNamespace] ) ) { |
1165 | 1179 | foreach ( (array)$wgNamespaceProtection[$this->mNamespace] as $right ) { |
1166 | | - if ( $right != '' && !$user->isAllowed( $right ) ) { |
| 1180 | + if ( $right != '' && !$wgUser->isAllowed( $right ) ) { |
1167 | 1181 | return true; |
1168 | 1182 | } |
1169 | 1183 | } |
— | — | @@ -1173,9 +1187,9 @@ |
1174 | 1188 | /** |
1175 | 1189 | * Can $wgUser perform $action on this page? |
1176 | 1190 | * |
1177 | | - * @param $action String action that permission needs to be checked for |
1178 | | - * @param $doExpensiveQueries Bool Set this to false to avoid doing unnecessary queries. |
1179 | | - * @return Bool |
| 1191 | + * @param $action \type{\string} action that permission needs to be checked for |
| 1192 | + * @param $doExpensiveQueries \type{\bool} Set this to false to avoid doing unnecessary queries. |
| 1193 | + * @return \type{\bool} |
1180 | 1194 | */ |
1181 | 1195 | public function userCan( $action, $doExpensiveQueries = true ) { |
1182 | 1196 | global $wgUser; |
— | — | @@ -1187,13 +1201,21 @@ |
1188 | 1202 | * |
1189 | 1203 | * FIXME: This *does not* check throttles (User::pingLimiter()). |
1190 | 1204 | * |
1191 | | - * @param $action String action that permission needs to be checked for |
1192 | | - * @param $user User to check |
1193 | | - * @param $doExpensiveQueries Bool Set this to false to avoid doing unnecessary queries. |
1194 | | - * @param $ignoreErrors Array of Strings Set this to a list of message keys whose corresponding errors may be ignored. |
1195 | | - * @return Array of arguments to wfMsg to explain permissions problems. |
| 1205 | + * @param $action \type{\string}action that permission needs to be checked for |
| 1206 | + * @param $user \type{User} user to check |
| 1207 | + * @param $doExpensiveQueries \type{\bool} Set this to false to avoid doing unnecessary queries. |
| 1208 | + * @param $ignoreErrors \type{\arrayof{\string}} Set this to a list of message keys whose corresponding errors may be ignored. |
| 1209 | + * @return \type{\array} Array of arrays of the arguments to wfMsg to explain permissions problems. |
1196 | 1210 | */ |
1197 | 1211 | public function getUserPermissionsErrors( $action, $user, $doExpensiveQueries = true, $ignoreErrors = array() ) { |
| 1212 | + if ( !StubObject::isRealObject( $user ) ) { |
| 1213 | + // Since StubObject is always used on globals, we can |
| 1214 | + // unstub $wgUser here and set $user = $wgUser |
| 1215 | + global $wgUser; |
| 1216 | + $wgUser->_unstub( '', 5 ); |
| 1217 | + $user = $wgUser; |
| 1218 | + } |
| 1219 | + |
1198 | 1220 | $errors = $this->getUserPermissionsErrorsInternal( $action, $user, $doExpensiveQueries ); |
1199 | 1221 | |
1200 | 1222 | // Remove the errors being ignored. |
— | — | @@ -1265,6 +1287,8 @@ |
1266 | 1288 | $errors[] = array( 'cant-move-to-user-page' ); |
1267 | 1289 | } |
1268 | 1290 | } elseif ( !$user->isAllowed( $action ) ) { |
| 1291 | + $return = null; |
| 1292 | + |
1269 | 1293 | // We avoid expensive display logic for quickUserCan's and such |
1270 | 1294 | $groups = false; |
1271 | 1295 | if ( !$short ) { |
— | — | @@ -1315,18 +1339,10 @@ |
1316 | 1340 | |
1317 | 1341 | /** |
1318 | 1342 | * Check various permission hooks |
1319 | | - * |
1320 | | - * @param $action String the action to check |
1321 | | - * @param $user User user to check |
1322 | | - * @param $errors Array list of current errors |
1323 | | - * @param $doExpensiveQueries Boolean whether or not to perform expensive queries |
1324 | | - * @param $short Boolean short circuit on first error |
1325 | | - * |
1326 | | - * @return Array list of errors |
| 1343 | + * @see checkQuickPermissions for parameter information |
1327 | 1344 | */ |
1328 | 1345 | private function checkPermissionHooks( $action, $user, $errors, $doExpensiveQueries, $short ) { |
1329 | 1346 | // Use getUserPermissionsErrors instead |
1330 | | - $result = ''; |
1331 | 1347 | if ( !wfRunHooks( 'userCan', array( &$this, &$user, $action, &$result ) ) ) { |
1332 | 1348 | return $result ? array() : array( array( 'badaccess-group0' ) ); |
1333 | 1349 | } |
— | — | @@ -1345,14 +1361,7 @@ |
1346 | 1362 | |
1347 | 1363 | /** |
1348 | 1364 | * Check permissions on special pages & namespaces |
1349 | | - * |
1350 | | - * @param $action String the action to check |
1351 | | - * @param $user User user to check |
1352 | | - * @param $errors Array list of current errors |
1353 | | - * @param $doExpensiveQueries Boolean whether or not to perform expensive queries |
1354 | | - * @param $short Boolean short circuit on first error |
1355 | | - * |
1356 | | - * @return Array list of errors |
| 1365 | + * @see checkQuickPermissions for parameter information |
1357 | 1366 | */ |
1358 | 1367 | private function checkSpecialsAndNSPermissions( $action, $user, $errors, $doExpensiveQueries, $short ) { |
1359 | 1368 | # Only 'createaccount' and 'execute' can be performed on |
— | — | @@ -1363,7 +1372,7 @@ |
1364 | 1373 | } |
1365 | 1374 | |
1366 | 1375 | # Check $wgNamespaceProtection for restricted namespaces |
1367 | | - if ( $this->isNamespaceProtected( $user ) ) { |
| 1376 | + if ( $this->isNamespaceProtected() ) { |
1368 | 1377 | $ns = $this->mNamespace == NS_MAIN ? |
1369 | 1378 | wfMsg( 'nstab-main' ) : $this->getNsText(); |
1370 | 1379 | $errors[] = $this->mNamespace == NS_MEDIAWIKI ? |
— | — | @@ -1375,14 +1384,7 @@ |
1376 | 1385 | |
1377 | 1386 | /** |
1378 | 1387 | * Check CSS/JS sub-page permissions |
1379 | | - * |
1380 | | - * @param $action String the action to check |
1381 | | - * @param $user User user to check |
1382 | | - * @param $errors Array list of current errors |
1383 | | - * @param $doExpensiveQueries Boolean whether or not to perform expensive queries |
1384 | | - * @param $short Boolean short circuit on first error |
1385 | | - * |
1386 | | - * @return Array list of errors |
| 1388 | + * @see checkQuickPermissions for parameter information |
1387 | 1389 | */ |
1388 | 1390 | private function checkCSSandJSPermissions( $action, $user, $errors, $doExpensiveQueries, $short ) { |
1389 | 1391 | # Protect css/js subpages of user pages |
— | — | @@ -1406,14 +1408,7 @@ |
1407 | 1409 | * Check against page_restrictions table requirements on this |
1408 | 1410 | * page. The user must possess all required rights for this |
1409 | 1411 | * action. |
1410 | | - * |
1411 | | - * @param $action String the action to check |
1412 | | - * @param $user User user to check |
1413 | | - * @param $errors Array list of current errors |
1414 | | - * @param $doExpensiveQueries Boolean whether or not to perform expensive queries |
1415 | | - * @param $short Boolean short circuit on first error |
1416 | | - * |
1417 | | - * @return Array list of errors |
| 1412 | + * @see checkQuickPermissions for parameter information |
1418 | 1413 | */ |
1419 | 1414 | private function checkPageRestrictions( $action, $user, $errors, $doExpensiveQueries, $short ) { |
1420 | 1415 | foreach ( $this->getRestrictions( $action ) as $right ) { |
— | — | @@ -1440,14 +1435,7 @@ |
1441 | 1436 | |
1442 | 1437 | /** |
1443 | 1438 | * Check restrictions on cascading pages. |
1444 | | - * |
1445 | | - * @param $action String the action to check |
1446 | | - * @param $user User to check |
1447 | | - * @param $errors Array list of current errors |
1448 | | - * @param $doExpensiveQueries Boolean whether or not to perform expensive queries |
1449 | | - * @param $short Boolean short circuit on first error |
1450 | | - * |
1451 | | - * @return Array list of errors |
| 1439 | + * @see checkQuickPermissions for parameter information |
1452 | 1440 | */ |
1453 | 1441 | private function checkCascadingSourcesRestrictions( $action, $user, $errors, $doExpensiveQueries, $short ) { |
1454 | 1442 | if ( $doExpensiveQueries && !$this->isCssJsSubpage() ) { |
— | — | @@ -1479,14 +1467,7 @@ |
1480 | 1468 | |
1481 | 1469 | /** |
1482 | 1470 | * Check action permissions not already checked in checkQuickPermissions |
1483 | | - * |
1484 | | - * @param $action String the action to check |
1485 | | - * @param $user User to check |
1486 | | - * @param $errors Array list of current errors |
1487 | | - * @param $doExpensiveQueries Boolean whether or not to perform expensive queries |
1488 | | - * @param $short Boolean short circuit on first error |
1489 | | - * |
1490 | | - * @return Array list of errors |
| 1471 | + * @see checkQuickPermissions for parameter information |
1491 | 1472 | */ |
1492 | 1473 | private function checkActionPermissions( $action, $user, $errors, $doExpensiveQueries, $short ) { |
1493 | 1474 | if ( $action == 'protect' ) { |
— | — | @@ -1525,17 +1506,10 @@ |
1526 | 1507 | |
1527 | 1508 | /** |
1528 | 1509 | * Check that the user isn't blocked from editting. |
1529 | | - * |
1530 | | - * @param $action String the action to check |
1531 | | - * @param $user User to check |
1532 | | - * @param $errors Array list of current errors |
1533 | | - * @param $doExpensiveQueries Boolean whether or not to perform expensive queries |
1534 | | - * @param $short Boolean short circuit on first error |
1535 | | - * |
1536 | | - * @return Array list of errors |
| 1510 | + * @see checkQuickPermissions for parameter information |
1537 | 1511 | */ |
1538 | 1512 | private function checkUserBlock( $action, $user, $errors, $doExpensiveQueries, $short ) { |
1539 | | - if( $short && count( $errors ) > 0 ) { |
| 1513 | + if( $short ) { |
1540 | 1514 | return $errors; |
1541 | 1515 | } |
1542 | 1516 | |
— | — | @@ -1545,14 +1519,8 @@ |
1546 | 1520 | $errors[] = array( 'confirmedittext' ); |
1547 | 1521 | } |
1548 | 1522 | |
1549 | | - if ( in_array( $action, array( 'read', 'createaccount', 'unblock' ) ) ){ |
1550 | | - // Edit blocks should not affect reading. |
1551 | | - // Account creation blocks handled at userlogin. |
1552 | | - // Unblocking handled in SpecialUnblock |
1553 | | - } elseif( ( $action == 'edit' || $action == 'create' ) && !$user->isBlockedFrom( $this ) ){ |
1554 | | - // Don't block the user from editing their own talk page unless they've been |
1555 | | - // explicitly blocked from that too. |
1556 | | - } elseif( $user->isBlocked() && $user->mBlock->prevents( $action ) !== false ) { |
| 1523 | + // Edit blocks should not affect reading. Account creation blocks handled at userlogin. |
| 1524 | + if ( $action != 'read' && $action != 'createaccount' && $user->isBlockedFrom( $this ) ) { |
1557 | 1525 | $block = $user->mBlock; |
1558 | 1526 | |
1559 | 1527 | // This is from OutputPage::blockedPage |
— | — | @@ -1572,16 +1540,29 @@ |
1573 | 1541 | } |
1574 | 1542 | |
1575 | 1543 | $link = '[[' . $wgContLang->getNsText( NS_USER ) . ":{$name}|{$name}]]"; |
1576 | | - $blockid = $block->getId(); |
| 1544 | + $blockid = $block->mId; |
1577 | 1545 | $blockExpiry = $user->mBlock->mExpiry; |
1578 | 1546 | $blockTimestamp = $wgLang->timeanddate( wfTimestamp( TS_MW, $user->mBlock->mTimestamp ), true ); |
1579 | 1547 | if ( $blockExpiry == 'infinity' ) { |
1580 | | - $blockExpiry = wfMessage( 'infiniteblock' )->text(); |
| 1548 | + // Entry in database (table ipblocks) is 'infinity' but 'ipboptions' uses 'infinite' or 'indefinite' |
| 1549 | + $scBlockExpiryOptions = wfMsg( 'ipboptions' ); |
| 1550 | + |
| 1551 | + foreach ( explode( ',', $scBlockExpiryOptions ) as $option ) { |
| 1552 | + if ( !strpos( $option, ':' ) ) |
| 1553 | + continue; |
| 1554 | + |
| 1555 | + list( $show, $value ) = explode( ':', $option ); |
| 1556 | + |
| 1557 | + if ( $value == 'infinite' || $value == 'indefinite' ) { |
| 1558 | + $blockExpiry = $show; |
| 1559 | + break; |
| 1560 | + } |
| 1561 | + } |
1581 | 1562 | } else { |
1582 | 1563 | $blockExpiry = $wgLang->timeanddate( wfTimestamp( TS_MW, $blockExpiry ), true ); |
1583 | 1564 | } |
1584 | 1565 | |
1585 | | - $intended = strval( $user->mBlock->getTarget() ); |
| 1566 | + $intended = $user->mBlock->mAddress; |
1586 | 1567 | |
1587 | 1568 | $errors[] = array( ( $block->mAuto ? 'autoblockedtext' : 'blockedtext' ), $link, $reason, $ip, $name, |
1588 | 1569 | $blockid, $blockExpiry, $intended, $blockTimestamp ); |
— | — | @@ -1595,11 +1576,11 @@ |
1596 | 1577 | * which checks ONLY that previously checked by userCan (i.e. it leaves out |
1597 | 1578 | * checks on wfReadOnly() and blocks) |
1598 | 1579 | * |
1599 | | - * @param $action String action that permission needs to be checked for |
1600 | | - * @param $user User to check |
1601 | | - * @param $doExpensiveQueries Bool Set this to false to avoid doing unnecessary queries. |
1602 | | - * @param $short Bool Set this to true to stop after the first permission error. |
1603 | | - * @return Array of arrays of the arguments to wfMsg to explain permissions problems. |
| 1580 | + * @param $action \type{\string} action that permission needs to be checked for |
| 1581 | + * @param $user \type{User} user to check |
| 1582 | + * @param $doExpensiveQueries \type{\bool} Set this to false to avoid doing unnecessary queries. |
| 1583 | + * @param $short \type{\bool} Set this to true to stop after the first permission error. |
| 1584 | + * @return \type{\array} Array of arrays of the arguments to wfMsg to explain permissions problems. |
1604 | 1585 | */ |
1605 | 1586 | protected function getUserPermissionsErrorsInternal( $action, $user, $doExpensiveQueries = true, $short = false ) { |
1606 | 1587 | wfProfileIn( __METHOD__ ); |
— | — | @@ -1628,9 +1609,8 @@ |
1629 | 1610 | |
1630 | 1611 | /** |
1631 | 1612 | * Is this title subject to title protection? |
1632 | | - * Title protection is the one applied against creation of such title. |
1633 | 1613 | * |
1634 | | - * @return Mixed An associative array representing any existent title |
| 1614 | + * @return \type{\mixed} An associative array representing any existent title |
1635 | 1615 | * protection, or false if there's none. |
1636 | 1616 | */ |
1637 | 1617 | private function getTitleProtection() { |
— | — | @@ -1659,9 +1639,9 @@ |
1660 | 1640 | /** |
1661 | 1641 | * Update the title protection status |
1662 | 1642 | * |
1663 | | - * @param $create_perm String Permission required for creation |
1664 | | - * @param $reason String Reason for protection |
1665 | | - * @param $expiry String Expiry timestamp |
| 1643 | + * @param $create_perm \type{\string} Permission required for creation |
| 1644 | + * @param $reason \type{\string} Reason for protection |
| 1645 | + * @param $expiry \type{\string} Expiry timestamp |
1666 | 1646 | * @return boolean true |
1667 | 1647 | */ |
1668 | 1648 | public function updateTitleProtection( $create_perm, $reason, $expiry ) { |
— | — | @@ -1677,10 +1657,10 @@ |
1678 | 1658 | |
1679 | 1659 | $dbw = wfGetDB( DB_MASTER ); |
1680 | 1660 | |
1681 | | - $encodedExpiry = $dbw->encodeExpiry( $expiry ); |
| 1661 | + $encodedExpiry = Block::encodeExpiry( $expiry, $dbw ); |
1682 | 1662 | |
1683 | 1663 | $expiry_description = ''; |
1684 | | - if ( $encodedExpiry != $dbw->getInfinity() ) { |
| 1664 | + if ( $encodedExpiry != 'infinity' ) { |
1685 | 1665 | $expiry_description = ' (' . wfMsgForContent( 'protect-expiring', $wgContLang->timeanddate( $expiry ), |
1686 | 1666 | $wgContLang->date( $expiry ) , $wgContLang->time( $expiry ) ) . ')'; |
1687 | 1667 | } else { |
— | — | @@ -1689,23 +1669,21 @@ |
1690 | 1670 | |
1691 | 1671 | # Update protection table |
1692 | 1672 | if ( $create_perm != '' ) { |
1693 | | - $this->mTitleProtection = array( |
| 1673 | + $dbw->replace( 'protected_titles', array( array( 'pt_namespace', 'pt_title' ) ), |
| 1674 | + array( |
1694 | 1675 | 'pt_namespace' => $namespace, |
1695 | 1676 | 'pt_title' => $title, |
1696 | 1677 | 'pt_create_perm' => $create_perm, |
1697 | | - 'pt_timestamp' => $dbw->encodeExpiry( wfTimestampNow() ), |
| 1678 | + 'pt_timestamp' => Block::encodeExpiry( wfTimestampNow(), $dbw ), |
1698 | 1679 | 'pt_expiry' => $encodedExpiry, |
1699 | 1680 | 'pt_user' => $wgUser->getId(), |
1700 | 1681 | 'pt_reason' => $reason, |
1701 | | - ); |
1702 | | - $dbw->replace( 'protected_titles', array( array( 'pt_namespace', 'pt_title' ) ), |
1703 | | - $this->mTitleProtection, __METHOD__ ); |
| 1682 | + ), __METHOD__ |
| 1683 | + ); |
1704 | 1684 | } else { |
1705 | 1685 | $dbw->delete( 'protected_titles', array( 'pt_namespace' => $namespace, |
1706 | 1686 | 'pt_title' => $title ), __METHOD__ ); |
1707 | | - $this->mTitleProtection = false; |
1708 | 1687 | } |
1709 | | - |
1710 | 1688 | # Update the protection log |
1711 | 1689 | if ( $dbw->affectedRows() ) { |
1712 | 1690 | $log = new LogPage( 'protect' ); |
— | — | @@ -1732,14 +1710,13 @@ |
1733 | 1711 | array( 'pt_namespace' => $this->getNamespace(), 'pt_title' => $this->getDBkey() ), |
1734 | 1712 | __METHOD__ |
1735 | 1713 | ); |
1736 | | - $this->mTitleProtection = false; |
1737 | 1714 | } |
1738 | 1715 | |
1739 | 1716 | /** |
1740 | 1717 | * Would anybody with sufficient privileges be able to move this page? |
1741 | 1718 | * Some pages just aren't movable. |
1742 | 1719 | * |
1743 | | - * @return Bool TRUE or FALSE |
| 1720 | + * @return \type{\bool} TRUE or FALSE |
1744 | 1721 | */ |
1745 | 1722 | public function isMovable() { |
1746 | 1723 | return MWNamespace::isMovable( $this->getNamespace() ) && $this->getInterwiki() == ''; |
— | — | @@ -1748,7 +1725,7 @@ |
1749 | 1726 | /** |
1750 | 1727 | * Can $wgUser read this page? |
1751 | 1728 | * |
1752 | | - * @return Bool |
| 1729 | + * @return \type{\bool} |
1753 | 1730 | * @todo fold these checks into userCan() |
1754 | 1731 | */ |
1755 | 1732 | public function userCanRead() { |
— | — | @@ -1796,36 +1773,47 @@ |
1797 | 1774 | } else { |
1798 | 1775 | global $wgWhitelistRead; |
1799 | 1776 | |
1800 | | - # Always grant access to the login page. |
1801 | | - # Even anons need to be able to log in. |
1802 | | - if ( $this->isSpecial( 'Userlogin' ) || $this->isSpecial( 'ChangePassword' ) ) { |
| 1777 | + /** |
| 1778 | + * Always grant access to the login page. |
| 1779 | + * Even anons need to be able to log in. |
| 1780 | + */ |
| 1781 | + if ( $this->isSpecial( 'Userlogin' ) || $this->isSpecial( 'Resetpass' ) ) { |
1803 | 1782 | return true; |
1804 | 1783 | } |
1805 | 1784 | |
1806 | | - # Bail out if there isn't whitelist |
| 1785 | + /** |
| 1786 | + * Bail out if there isn't whitelist |
| 1787 | + */ |
1807 | 1788 | if ( !is_array( $wgWhitelistRead ) ) { |
1808 | 1789 | return false; |
1809 | 1790 | } |
1810 | 1791 | |
1811 | | - # Check for explicit whitelisting |
| 1792 | + /** |
| 1793 | + * Check for explicit whitelisting |
| 1794 | + */ |
1812 | 1795 | $name = $this->getPrefixedText(); |
1813 | 1796 | $dbName = $this->getPrefixedDBKey(); |
1814 | 1797 | // Check with and without underscores |
1815 | 1798 | if ( in_array( $name, $wgWhitelistRead, true ) || in_array( $dbName, $wgWhitelistRead, true ) ) |
1816 | 1799 | return true; |
1817 | 1800 | |
1818 | | - # Old settings might have the title prefixed with |
1819 | | - # a colon for main-namespace pages |
| 1801 | + /** |
| 1802 | + * Old settings might have the title prefixed with |
| 1803 | + * a colon for main-namespace pages |
| 1804 | + */ |
1820 | 1805 | if ( $this->getNamespace() == NS_MAIN ) { |
1821 | 1806 | if ( in_array( ':' . $name, $wgWhitelistRead ) ) { |
1822 | 1807 | return true; |
1823 | 1808 | } |
1824 | 1809 | } |
1825 | 1810 | |
1826 | | - # If it's a special page, ditch the subpage bit and check again |
| 1811 | + /** |
| 1812 | + * If it's a special page, ditch the subpage bit |
| 1813 | + * and check again |
| 1814 | + */ |
1827 | 1815 | if ( $this->getNamespace() == NS_SPECIAL ) { |
1828 | 1816 | $name = $this->getDBkey(); |
1829 | | - list( $name, /* $subpage */ ) = SpecialPageFactory::resolveAlias( $name ); |
| 1817 | + list( $name, /* $subpage */ ) = SpecialPage::resolveAliasWithSubpage( $name ); |
1830 | 1818 | if ( $name === false ) { |
1831 | 1819 | # Invalid special page, but we show standard login required message |
1832 | 1820 | return false; |
— | — | @@ -1842,22 +1830,9 @@ |
1843 | 1831 | } |
1844 | 1832 | |
1845 | 1833 | /** |
1846 | | - * Is this the mainpage? |
1847 | | - * @note Title::newFromText seams to be sufficiently optimized by the title |
1848 | | - * cache that we don't need to over-optimize by doing direct comparisons and |
1849 | | - * acidentally creating new bugs where $title->equals( Title::newFromText() ) |
1850 | | - * ends up reporting something differently than $title->isMainPage(); |
1851 | | - * |
1852 | | - * @return Bool |
1853 | | - */ |
1854 | | - public function isMainPage() { |
1855 | | - return $this->equals( Title::newMainPage() ); |
1856 | | - } |
1857 | | - |
1858 | | - /** |
1859 | 1834 | * Is this a talk page of some sort? |
1860 | 1835 | * |
1861 | | - * @return Bool |
| 1836 | + * @return \type{\bool} |
1862 | 1837 | */ |
1863 | 1838 | public function isTalkPage() { |
1864 | 1839 | return MWNamespace::isTalk( $this->getNamespace() ); |
— | — | @@ -1866,7 +1841,7 @@ |
1867 | 1842 | /** |
1868 | 1843 | * Is this a subpage? |
1869 | 1844 | * |
1870 | | - * @return Bool |
| 1845 | + * @return \type{\bool} |
1871 | 1846 | */ |
1872 | 1847 | public function isSubpage() { |
1873 | 1848 | return MWNamespace::hasSubpages( $this->mNamespace ) |
— | — | @@ -1877,7 +1852,7 @@ |
1878 | 1853 | /** |
1879 | 1854 | * Does this have subpages? (Warning, usually requires an extra DB query.) |
1880 | 1855 | * |
1881 | | - * @return Bool |
| 1856 | + * @return \type{\bool} |
1882 | 1857 | */ |
1883 | 1858 | public function hasSubpages() { |
1884 | 1859 | if ( !MWNamespace::hasSubpages( $this->mNamespace ) ) { |
— | — | @@ -1903,7 +1878,7 @@ |
1904 | 1879 | /** |
1905 | 1880 | * Get all subpages of this page. |
1906 | 1881 | * |
1907 | | - * @param $limit Int maximum number of subpages to fetch; -1 for no limit |
| 1882 | + * @param $limit Maximum number of subpages to fetch; -1 for no limit |
1908 | 1883 | * @return mixed TitleArray, or empty array if this page's namespace |
1909 | 1884 | * doesn't allow subpages |
1910 | 1885 | */ |
— | — | @@ -1933,7 +1908,7 @@ |
1934 | 1909 | * Could this page contain custom CSS or JavaScript, based |
1935 | 1910 | * on the title? |
1936 | 1911 | * |
1937 | | - * @return Bool |
| 1912 | + * @return \type{\bool} |
1938 | 1913 | */ |
1939 | 1914 | public function isCssOrJsPage() { |
1940 | 1915 | return $this->mNamespace == NS_MEDIAWIKI |
— | — | @@ -1942,7 +1917,7 @@ |
1943 | 1918 | |
1944 | 1919 | /** |
1945 | 1920 | * Is this a .css or .js subpage of a user page? |
1946 | | - * @return Bool |
| 1921 | + * @return \type{\bool} |
1947 | 1922 | */ |
1948 | 1923 | public function isCssJsSubpage() { |
1949 | 1924 | return ( NS_USER == $this->mNamespace and preg_match( "/\\/.*\\.(?:css|js)$/", $this->mTextform ) ); |
— | — | @@ -1950,12 +1925,21 @@ |
1951 | 1926 | |
1952 | 1927 | /** |
1953 | 1928 | * Is this a *valid* .css or .js subpage of a user page? |
| 1929 | + * Check that the corresponding skin exists |
1954 | 1930 | * |
1955 | | - * @return Bool |
1956 | | - * @deprecated since 1.17 |
| 1931 | + * @return \type{\bool} |
1957 | 1932 | */ |
1958 | 1933 | public function isValidCssJsSubpage() { |
1959 | | - return $this->isCssJsSubpage(); |
| 1934 | + if ( $this->isCssJsSubpage() ) { |
| 1935 | + $name = $this->getSkinFromCssJsSubpage(); |
| 1936 | + if ( $name == 'common' ) { |
| 1937 | + return true; |
| 1938 | + } |
| 1939 | + $skinNames = Skin::getSkinNames(); |
| 1940 | + return array_key_exists( $name, $skinNames ); |
| 1941 | + } else { |
| 1942 | + return false; |
| 1943 | + } |
1960 | 1944 | } |
1961 | 1945 | |
1962 | 1946 | /** |
— | — | @@ -1972,7 +1956,7 @@ |
1973 | 1957 | /** |
1974 | 1958 | * Is this a .css subpage of a user page? |
1975 | 1959 | * |
1976 | | - * @return Bool |
| 1960 | + * @return \type{\bool} |
1977 | 1961 | */ |
1978 | 1962 | public function isCssSubpage() { |
1979 | 1963 | return ( NS_USER == $this->mNamespace && preg_match( "/\\/.*\\.css$/", $this->mTextform ) ); |
— | — | @@ -1981,7 +1965,7 @@ |
1982 | 1966 | /** |
1983 | 1967 | * Is this a .js subpage of a user page? |
1984 | 1968 | * |
1985 | | - * @return Bool |
| 1969 | + * @return \type{\bool} |
1986 | 1970 | */ |
1987 | 1971 | public function isJsSubpage() { |
1988 | 1972 | return ( NS_USER == $this->mNamespace && preg_match( "/\\/.*\\.js$/", $this->mTextform ) ); |
— | — | @@ -1991,12 +1975,12 @@ |
1992 | 1976 | * Protect css subpages of user pages: can $wgUser edit |
1993 | 1977 | * this page? |
1994 | 1978 | * |
1995 | | - * @return Bool |
| 1979 | + * @return \type{\bool} |
1996 | 1980 | * @todo XXX: this might be better using restrictions |
1997 | 1981 | */ |
1998 | 1982 | public function userCanEditCssSubpage() { |
1999 | 1983 | global $wgUser; |
2000 | | - return ( ( $wgUser->isAllowedAll( 'editusercssjs', 'editusercss' ) ) |
| 1984 | + return ( ( $wgUser->isAllowed( 'editusercssjs' ) && $wgUser->isAllowed( 'editusercss' ) ) |
2001 | 1985 | || preg_match( '/^' . preg_quote( $wgUser->getName(), '/' ) . '\//', $this->mTextform ) ); |
2002 | 1986 | } |
2003 | 1987 | |
— | — | @@ -2004,19 +1988,19 @@ |
2005 | 1989 | * Protect js subpages of user pages: can $wgUser edit |
2006 | 1990 | * this page? |
2007 | 1991 | * |
2008 | | - * @return Bool |
| 1992 | + * @return \type{\bool} |
2009 | 1993 | * @todo XXX: this might be better using restrictions |
2010 | 1994 | */ |
2011 | 1995 | public function userCanEditJsSubpage() { |
2012 | 1996 | global $wgUser; |
2013 | | - return ( ( $wgUser->isAllowedAll( 'editusercssjs', 'edituserjs' ) ) |
2014 | | - || preg_match( '/^' . preg_quote( $wgUser->getName(), '/' ) . '\//', $this->mTextform ) ); |
| 1997 | + return ( ( $wgUser->isAllowed( 'editusercssjs' ) && $wgUser->isAllowed( 'edituserjs' ) ) |
| 1998 | + || preg_match( '/^' . preg_quote( $wgUser->getName(), '/' ) . '\//', $this->mTextform ) ); |
2015 | 1999 | } |
2016 | 2000 | |
2017 | 2001 | /** |
2018 | 2002 | * Cascading protection: Return true if cascading restrictions apply to this page, false if not. |
2019 | 2003 | * |
2020 | | - * @return Bool If the page is subject to cascading restrictions. |
| 2004 | + * @return \type{\bool} If the page is subject to cascading restrictions. |
2021 | 2005 | */ |
2022 | 2006 | public function isCascadeProtected() { |
2023 | 2007 | list( $sources, /* $restrictions */ ) = $this->getCascadeProtectionSources( false ); |
— | — | @@ -2026,15 +2010,15 @@ |
2027 | 2011 | /** |
2028 | 2012 | * Cascading protection: Get the source of any cascading restrictions on this page. |
2029 | 2013 | * |
2030 | | - * @param $getPages Bool Whether or not to retrieve the actual pages |
| 2014 | + * @param $getPages \type{\bool} Whether or not to retrieve the actual pages |
2031 | 2015 | * that the restrictions have come from. |
2032 | | - * @return Mixed Array of Title objects of the pages from which cascading restrictions |
2033 | | - * have come, false for none, or true if such restrictions exist, but $getPages |
2034 | | - * was not set. The restriction array is an array of each type, each of which |
2035 | | - * contains a array of unique groups. |
| 2016 | + * @return \type{\arrayof{mixed title array, restriction array}} Array of the Title |
| 2017 | + * objects of the pages from which cascading restrictions have come, |
| 2018 | + * false for none, or true if such restrictions exist, but $getPages was not set. |
| 2019 | + * The restriction array is an array of each type, each of which contains a |
| 2020 | + * array of unique groups. |
2036 | 2021 | */ |
2037 | 2022 | public function getCascadeProtectionSources( $getPages = true ) { |
2038 | | - global $wgContLang; |
2039 | 2023 | $pagerestrictions = array(); |
2040 | 2024 | |
2041 | 2025 | if ( isset( $this->mCascadeSources ) && $getPages ) { |
— | — | @@ -2080,7 +2064,7 @@ |
2081 | 2065 | $purgeExpired = false; |
2082 | 2066 | |
2083 | 2067 | foreach ( $res as $row ) { |
2084 | | - $expiry = $wgContLang->formatExpiry( $row->pr_expiry, TS_MW ); |
| 2068 | + $expiry = Block::decodeExpiry( $row->pr_expiry ); |
2085 | 2069 | if ( $expiry > $now ) { |
2086 | 2070 | if ( $getPages ) { |
2087 | 2071 | $page_id = $row->pr_page; |
— | — | @@ -2110,6 +2094,8 @@ |
2111 | 2095 | Title::purgeExpiredRestrictions(); |
2112 | 2096 | } |
2113 | 2097 | |
| 2098 | + wfProfileOut( __METHOD__ ); |
| 2099 | + |
2114 | 2100 | if ( $getPages ) { |
2115 | 2101 | $this->mCascadeSources = $sources; |
2116 | 2102 | $this->mCascadingRestrictions = $pagerestrictions; |
— | — | @@ -2117,7 +2103,6 @@ |
2118 | 2104 | $this->mHasCascadingRestrictions = $sources; |
2119 | 2105 | } |
2120 | 2106 | |
2121 | | - wfProfileOut( __METHOD__ ); |
2122 | 2107 | return array( $sources, $pagerestrictions ); |
2123 | 2108 | } |
2124 | 2109 | |
— | — | @@ -2137,14 +2122,15 @@ |
2138 | 2123 | /** |
2139 | 2124 | * Loads a string into mRestrictions array |
2140 | 2125 | * |
2141 | | - * @param $res Resource restrictions as an SQL result. |
2142 | | - * @param $oldFashionedRestrictions String comma-separated list of page |
| 2126 | + * @param $res \type{Resource} restrictions as an SQL result. |
| 2127 | + * @param $oldFashionedRestrictions string comma-separated list of page |
2143 | 2128 | * restrictions from page table (pre 1.10) |
2144 | 2129 | */ |
2145 | 2130 | private function loadRestrictionsFromResultWrapper( $res, $oldFashionedRestrictions = null ) { |
2146 | 2131 | $rows = array(); |
| 2132 | + $dbr = wfGetDB( DB_SLAVE ); |
2147 | 2133 | |
2148 | | - foreach ( $res as $row ) { |
| 2134 | + while ( $row = $dbr->fetchObject( $res ) ) { |
2149 | 2135 | $rows[] = $row; |
2150 | 2136 | } |
2151 | 2137 | |
— | — | @@ -2153,22 +2139,20 @@ |
2154 | 2140 | |
2155 | 2141 | /** |
2156 | 2142 | * Compiles list of active page restrictions from both page table (pre 1.10) |
2157 | | - * and page_restrictions table for this existing page. |
2158 | | - * Public for usage by LiquidThreads. |
| 2143 | + * and page_restrictions table |
2159 | 2144 | * |
2160 | 2145 | * @param $rows array of db result objects |
2161 | 2146 | * @param $oldFashionedRestrictions string comma-separated list of page |
2162 | 2147 | * restrictions from page table (pre 1.10) |
2163 | 2148 | */ |
2164 | 2149 | public function loadRestrictionsFromRows( $rows, $oldFashionedRestrictions = null ) { |
2165 | | - global $wgContLang; |
2166 | 2150 | $dbr = wfGetDB( DB_SLAVE ); |
2167 | 2151 | |
2168 | 2152 | $restrictionTypes = $this->getRestrictionTypes(); |
2169 | 2153 | |
2170 | 2154 | foreach ( $restrictionTypes as $type ) { |
2171 | 2155 | $this->mRestrictions[$type] = array(); |
2172 | | - $this->mRestrictionsExpiry[$type] = $wgContLang->formatExpiry( '', TS_MW ); |
| 2156 | + $this->mRestrictionsExpiry[$type] = Block::decodeExpiry( '' ); |
2173 | 2157 | } |
2174 | 2158 | |
2175 | 2159 | $this->mCascadeRestriction = false; |
— | — | @@ -2202,16 +2186,17 @@ |
2203 | 2187 | $now = wfTimestampNow(); |
2204 | 2188 | $purgeExpired = false; |
2205 | 2189 | |
2206 | | - # Cycle through all the restrictions. |
2207 | 2190 | foreach ( $rows as $row ) { |
| 2191 | + # Cycle through all the restrictions. |
2208 | 2192 | |
2209 | 2193 | // Don't take care of restrictions types that aren't allowed |
| 2194 | + |
2210 | 2195 | if ( !in_array( $row->pr_type, $restrictionTypes ) ) |
2211 | 2196 | continue; |
2212 | 2197 | |
2213 | 2198 | // This code should be refactored, now that it's being used more generally, |
2214 | 2199 | // But I don't really see any harm in leaving it in Block for now -werdna |
2215 | | - $expiry = $wgContLang->formatExpiry( $row->pr_expiry, TS_MW ); |
| 2200 | + $expiry = Block::decodeExpiry( $row->pr_expiry ); |
2216 | 2201 | |
2217 | 2202 | // Only apply the restrictions if they haven't expired! |
2218 | 2203 | if ( !$expiry || $expiry > $now ) { |
— | — | @@ -2236,21 +2221,16 @@ |
2237 | 2222 | /** |
2238 | 2223 | * Load restrictions from the page_restrictions table |
2239 | 2224 | * |
2240 | | - * @param $oldFashionedRestrictions String comma-separated list of page |
| 2225 | + * @param $oldFashionedRestrictions string comma-separated list of page |
2241 | 2226 | * restrictions from page table (pre 1.10) |
2242 | 2227 | */ |
2243 | 2228 | public function loadRestrictions( $oldFashionedRestrictions = null ) { |
2244 | | - global $wgContLang; |
2245 | 2229 | if ( !$this->mRestrictionsLoaded ) { |
2246 | 2230 | if ( $this->exists() ) { |
2247 | 2231 | $dbr = wfGetDB( DB_SLAVE ); |
2248 | 2232 | |
2249 | | - $res = $dbr->select( |
2250 | | - 'page_restrictions', |
2251 | | - '*', |
2252 | | - array( 'pr_page' => $this->getArticleId() ), |
2253 | | - __METHOD__ |
2254 | | - ); |
| 2233 | + $res = $dbr->select( 'page_restrictions', '*', |
| 2234 | + array( 'pr_page' => $this->getArticleId() ), __METHOD__ ); |
2255 | 2235 | |
2256 | 2236 | $this->loadRestrictionsFromResultWrapper( $res, $oldFashionedRestrictions ); |
2257 | 2237 | } else { |
— | — | @@ -2258,7 +2238,7 @@ |
2259 | 2239 | |
2260 | 2240 | if ( $title_protection ) { |
2261 | 2241 | $now = wfTimestampNow(); |
2262 | | - $expiry = $wgContLang->formatExpiry( $title_protection['pt_expiry'], TS_MW ); |
| 2242 | + $expiry = Block::decodeExpiry( $title_protection['pt_expiry'] ); |
2263 | 2243 | |
2264 | 2244 | if ( !$expiry || $expiry > $now ) { |
2265 | 2245 | // Apply the restrictions |
— | — | @@ -2266,10 +2246,9 @@ |
2267 | 2247 | $this->mRestrictions['create'] = explode( ',', trim( $title_protection['pt_create_perm'] ) ); |
2268 | 2248 | } else { // Get rid of the old restrictions |
2269 | 2249 | Title::purgeExpiredRestrictions(); |
2270 | | - $this->mTitleProtection = false; |
2271 | 2250 | } |
2272 | 2251 | } else { |
2273 | | - $this->mRestrictionsExpiry['create'] = $wgContLang->formatExpiry( '', TS_MW ); |
| 2252 | + $this->mRestrictionsExpiry['create'] = Block::decodeExpiry( '' ); |
2274 | 2253 | } |
2275 | 2254 | $this->mRestrictionsLoaded = true; |
2276 | 2255 | } |
— | — | @@ -2297,8 +2276,8 @@ |
2298 | 2277 | /** |
2299 | 2278 | * Accessor/initialisation for mRestrictions |
2300 | 2279 | * |
2301 | | - * @param $action String action that permission needs to be checked for |
2302 | | - * @return Array of Strings the array of groups allowed to edit this article |
| 2280 | + * @param $action \type{\string} action that permission needs to be checked for |
| 2281 | + * @return \type{\arrayof{\string}} the array of groups allowed to edit this article |
2303 | 2282 | */ |
2304 | 2283 | public function getRestrictions( $action ) { |
2305 | 2284 | if ( !$this->mRestrictionsLoaded ) { |
— | — | @@ -2312,7 +2291,7 @@ |
2313 | 2292 | /** |
2314 | 2293 | * Get the expiry time for the restriction against a given action |
2315 | 2294 | * |
2316 | | - * @return String|Bool 14-char timestamp, or 'infinity' if the page is protected forever |
| 2295 | + * @return 14-char timestamp, or 'infinity' if the page is protected forever |
2317 | 2296 | * or not protected at all, or false if the action is not recognised. |
2318 | 2297 | */ |
2319 | 2298 | public function getRestrictionExpiry( $action ) { |
— | — | @@ -2325,7 +2304,7 @@ |
2326 | 2305 | /** |
2327 | 2306 | * Is there a version of this page in the deletion archive? |
2328 | 2307 | * |
2329 | | - * @return Int the number of archived revisions |
| 2308 | + * @return \type{\int} the number of archived revisions |
2330 | 2309 | */ |
2331 | 2310 | public function isDeleted() { |
2332 | 2311 | if ( $this->getNamespace() < 0 ) { |
— | — | @@ -2373,16 +2352,16 @@ |
2374 | 2353 | * Get the article ID for this Title from the link cache, |
2375 | 2354 | * adding it if necessary |
2376 | 2355 | * |
2377 | | - * @param $flags Int a bit field; may be Title::GAID_FOR_UPDATE to select |
| 2356 | + * @param $flags \type{\int} a bit field; may be GAID_FOR_UPDATE to select |
2378 | 2357 | * for update |
2379 | | - * @return Int the ID |
| 2358 | + * @return \type{\int} the ID |
2380 | 2359 | */ |
2381 | 2360 | public function getArticleID( $flags = 0 ) { |
2382 | 2361 | if ( $this->getNamespace() < 0 ) { |
2383 | 2362 | return $this->mArticleID = 0; |
2384 | 2363 | } |
2385 | 2364 | $linkCache = LinkCache::singleton(); |
2386 | | - if ( $flags & self::GAID_FOR_UPDATE ) { |
| 2365 | + if ( $flags & GAID_FOR_UPDATE ) { |
2387 | 2366 | $oldUpdate = $linkCache->forUpdate( true ); |
2388 | 2367 | $linkCache->clearLink( $this ); |
2389 | 2368 | $this->mArticleID = $linkCache->addLinkObj( $this ); |
— | — | @@ -2399,8 +2378,8 @@ |
2400 | 2379 | * Is this an article that is a redirect page? |
2401 | 2380 | * Uses link cache, adding it if necessary |
2402 | 2381 | * |
2403 | | - * @param $flags Int a bit field; may be Title::GAID_FOR_UPDATE to select for update |
2404 | | - * @return Bool |
| 2382 | + * @param $flags \type{\int} a bit field; may be GAID_FOR_UPDATE to select for update |
| 2383 | + * @return \type{\bool} |
2405 | 2384 | */ |
2406 | 2385 | public function isRedirect( $flags = 0 ) { |
2407 | 2386 | if ( !is_null( $this->mRedirect ) ) { |
— | — | @@ -2420,8 +2399,8 @@ |
2421 | 2400 | * What is the length of this page? |
2422 | 2401 | * Uses link cache, adding it if necessary |
2423 | 2402 | * |
2424 | | - * @param $flags Int a bit field; may be Title::GAID_FOR_UPDATE to select for update |
2425 | | - * @return Int |
| 2403 | + * @param $flags \type{\int} a bit field; may be GAID_FOR_UPDATE to select for update |
| 2404 | + * @return \type{\bool} |
2426 | 2405 | */ |
2427 | 2406 | public function getLength( $flags = 0 ) { |
2428 | 2407 | if ( $this->mLength != -1 ) { |
— | — | @@ -2440,8 +2419,8 @@ |
2441 | 2420 | /** |
2442 | 2421 | * What is the page_latest field for this page? |
2443 | 2422 | * |
2444 | | - * @param $flags Int a bit field; may be Title::GAID_FOR_UPDATE to select for update |
2445 | | - * @return Int or 0 if the page doesn't exist |
| 2423 | + * @param $flags \type{\int} a bit field; may be GAID_FOR_UPDATE to select for update |
| 2424 | + * @return \type{\int} or 0 if the page doesn't exist |
2446 | 2425 | */ |
2447 | 2426 | public function getLatestRevID( $flags = 0 ) { |
2448 | 2427 | if ( $this->mLatestID !== false ) { |
— | — | @@ -2461,11 +2440,7 @@ |
2462 | 2441 | * This clears some fields in this object, and clears any associated |
2463 | 2442 | * keys in the "bad links" section of the link cache. |
2464 | 2443 | * |
2465 | | - * - This is called from Article::doEdit() and Article::insertOn() to allow |
2466 | | - * loading of the new page_id. It's also called from |
2467 | | - * Article::doDeleteArticle() |
2468 | | - * |
2469 | | - * @param $newid Int the new Article ID |
| 2444 | + * @param $newid \type{\int} the new Article ID |
2470 | 2445 | */ |
2471 | 2446 | public function resetArticleID( $newid ) { |
2472 | 2447 | $linkCache = LinkCache::singleton(); |
— | — | @@ -2486,7 +2461,7 @@ |
2487 | 2462 | /** |
2488 | 2463 | * Updates page_touched for this page; called from LinksUpdate.php |
2489 | 2464 | * |
2490 | | - * @return Bool true if the update succeded |
| 2465 | + * @return \type{\bool} true if the update succeded |
2491 | 2466 | */ |
2492 | 2467 | public function invalidateCache() { |
2493 | 2468 | if ( wfReadOnly() ) { |
— | — | @@ -2507,16 +2482,15 @@ |
2508 | 2483 | * Prefix some arbitrary text with the namespace or interwiki prefix |
2509 | 2484 | * of this object |
2510 | 2485 | * |
2511 | | - * @param $name String the text |
2512 | | - * @return String the prefixed text |
| 2486 | + * @param $name \type{\string} the text |
| 2487 | + * @return \type{\string} the prefixed text |
2513 | 2488 | * @private |
2514 | 2489 | */ |
2515 | | - private function prefix( $name ) { |
| 2490 | + /* private */ function prefix( $name ) { |
2516 | 2491 | $p = ''; |
2517 | 2492 | if ( $this->mInterwiki != '' ) { |
2518 | 2493 | $p = $this->mInterwiki . ':'; |
2519 | 2494 | } |
2520 | | - |
2521 | 2495 | if ( 0 != $this->mNamespace ) { |
2522 | 2496 | $p .= $this->getNsText() . ':'; |
2523 | 2497 | } |
— | — | @@ -2528,7 +2502,7 @@ |
2529 | 2503 | * Note that this doesn't pick up many things that could be wrong with titles, but that |
2530 | 2504 | * replacing this regex with something valid will make many titles valid. |
2531 | 2505 | * |
2532 | | - * @return String regex string |
| 2506 | + * @return string regex string |
2533 | 2507 | */ |
2534 | 2508 | static function getTitleInvalidRegex() { |
2535 | 2509 | static $rxTc = false; |
— | — | @@ -2553,7 +2527,7 @@ |
2554 | 2528 | /** |
2555 | 2529 | * Capitalize a text string for a title if it belongs to a namespace that capitalizes |
2556 | 2530 | * |
2557 | | - * @param $text String containing title to capitalize |
| 2531 | + * @param $text string containing title to capitalize |
2558 | 2532 | * @param $ns int namespace index, defaults to NS_MAIN |
2559 | 2533 | * @return String containing capitalized title |
2560 | 2534 | */ |
— | — | @@ -2576,12 +2550,14 @@ |
2577 | 2551 | * namespace prefixes, sets the other forms, and canonicalizes |
2578 | 2552 | * everything. |
2579 | 2553 | * |
2580 | | - * @return Bool true on success |
| 2554 | + * @return \type{\bool} true on success |
2581 | 2555 | */ |
2582 | 2556 | private function secureAndSplit() { |
2583 | 2557 | global $wgContLang, $wgLocalInterwiki; |
2584 | 2558 | |
2585 | 2559 | # Initialisation |
| 2560 | + $rxTc = self::getTitleInvalidRegex(); |
| 2561 | + |
2586 | 2562 | $this->mInterwiki = $this->mFragment = ''; |
2587 | 2563 | $this->mNamespace = $this->mDefaultNamespace; # Usually NS_MAIN |
2588 | 2564 | |
— | — | @@ -2596,6 +2572,7 @@ |
2597 | 2573 | # Note: use of the /u option on preg_replace here will cause |
2598 | 2574 | # input with invalid UTF-8 sequences to be nullified out in PHP 5.2.x, |
2599 | 2575 | # conveniently disabling them. |
| 2576 | + # |
2600 | 2577 | $dbkey = preg_replace( '/[ _\xA0\x{1680}\x{180E}\x{2000}-\x{200A}\x{2028}\x{2029}\x{202F}\x{205F}\x{3000}]+/u', '_', $dbkey ); |
2601 | 2578 | $dbkey = trim( $dbkey, '_' ); |
2602 | 2579 | |
— | — | @@ -2632,11 +2609,9 @@ |
2633 | 2610 | # For Talk:X pages, check if X has a "namespace" prefix |
2634 | 2611 | if ( $ns == NS_TALK && preg_match( $prefixRegexp, $dbkey, $x ) ) { |
2635 | 2612 | if ( $wgContLang->getNsIndex( $x[1] ) ) { |
2636 | | - # Disallow Talk:File:x type titles... |
2637 | | - return false; |
| 2613 | + return false; # Disallow Talk:File:x type titles... |
2638 | 2614 | } else if ( Interwiki::isValidInterwiki( $x[1] ) ) { |
2639 | | - # Disallow Talk:Interwiki:x type titles... |
2640 | | - return false; |
| 2615 | + return false; # Disallow Talk:Interwiki:x type titles... |
2641 | 2616 | } |
2642 | 2617 | } |
2643 | 2618 | } elseif ( Interwiki::isValidInterwiki( $p ) ) { |
— | — | @@ -2651,9 +2626,7 @@ |
2652 | 2627 | $this->mInterwiki = $wgContLang->lc( $p ); |
2653 | 2628 | |
2654 | 2629 | # Redundant interwiki prefix to the local wiki |
2655 | | - if ( $wgLocalInterwiki !== false |
2656 | | - && 0 == strcasecmp( $this->mInterwiki, $wgLocalInterwiki ) ) |
2657 | | - { |
| 2630 | + if ( 0 == strcasecmp( $this->mInterwiki, $wgLocalInterwiki ) ) { |
2658 | 2631 | if ( $dbkey == '' ) { |
2659 | 2632 | # Can't have an empty self-link |
2660 | 2633 | return false; |
— | — | @@ -2678,12 +2651,13 @@ |
2679 | 2652 | } while ( true ); |
2680 | 2653 | |
2681 | 2654 | # We already know that some pages won't be in the database! |
| 2655 | + # |
2682 | 2656 | if ( $this->mInterwiki != '' || NS_SPECIAL == $this->mNamespace ) { |
2683 | 2657 | $this->mArticleID = 0; |
2684 | 2658 | } |
2685 | 2659 | $fragment = strstr( $dbkey, '#' ); |
2686 | 2660 | if ( false !== $fragment ) { |
2687 | | - $this->setFragment( $fragment ); |
| 2661 | + $this->setFragment( preg_replace( '/^#_*/', '#', $fragment ) ); |
2688 | 2662 | $dbkey = substr( $dbkey, 0, strlen( $dbkey ) - strlen( $fragment ) ); |
2689 | 2663 | # remove whitespace again: prevents "Foo_bar_#" |
2690 | 2664 | # becoming "Foo_bar_" |
— | — | @@ -2691,65 +2665,79 @@ |
2692 | 2666 | } |
2693 | 2667 | |
2694 | 2668 | # Reject illegal characters. |
2695 | | - $rxTc = self::getTitleInvalidRegex(); |
| 2669 | + # |
2696 | 2670 | if ( preg_match( $rxTc, $dbkey ) ) { |
2697 | 2671 | return false; |
2698 | 2672 | } |
2699 | 2673 | |
2700 | | - # Pages with "/./" or "/../" appearing in the URLs will often be un- |
2701 | | - # reachable due to the way web browsers deal with 'relative' URLs. |
2702 | | - # Also, they conflict with subpage syntax. Forbid them explicitly. |
| 2674 | + /** |
| 2675 | + * Pages with "/./" or "/../" appearing in the URLs will often be un- |
| 2676 | + * reachable due to the way web browsers deal with 'relative' URLs. |
| 2677 | + * Also, they conflict with subpage syntax. Forbid them explicitly. |
| 2678 | + */ |
2703 | 2679 | if ( strpos( $dbkey, '.' ) !== false && |
2704 | | - ( $dbkey === '.' || $dbkey === '..' || |
2705 | | - strpos( $dbkey, './' ) === 0 || |
2706 | | - strpos( $dbkey, '../' ) === 0 || |
2707 | | - strpos( $dbkey, '/./' ) !== false || |
2708 | | - strpos( $dbkey, '/../' ) !== false || |
2709 | | - substr( $dbkey, -2 ) == '/.' || |
2710 | | - substr( $dbkey, -3 ) == '/..' ) ) |
| 2680 | + ( $dbkey === '.' || $dbkey === '..' || |
| 2681 | + strpos( $dbkey, './' ) === 0 || |
| 2682 | + strpos( $dbkey, '../' ) === 0 || |
| 2683 | + strpos( $dbkey, '/./' ) !== false || |
| 2684 | + strpos( $dbkey, '/../' ) !== false || |
| 2685 | + substr( $dbkey, -2 ) == '/.' || |
| 2686 | + substr( $dbkey, -3 ) == '/..' ) ) |
2711 | 2687 | { |
2712 | 2688 | return false; |
2713 | 2689 | } |
2714 | 2690 | |
2715 | | - # Magic tilde sequences? Nu-uh! |
| 2691 | + /** |
| 2692 | + * Magic tilde sequences? Nu-uh! |
| 2693 | + */ |
2716 | 2694 | if ( strpos( $dbkey, '~~~' ) !== false ) { |
2717 | 2695 | return false; |
2718 | 2696 | } |
2719 | 2697 | |
2720 | | - # Limit the size of titles to 255 bytes. This is typically the size of the |
2721 | | - # underlying database field. We make an exception for special pages, which |
2722 | | - # don't need to be stored in the database, and may edge over 255 bytes due |
2723 | | - # to subpage syntax for long titles, e.g. [[Special:Block/Long name]] |
| 2698 | + /** |
| 2699 | + * Limit the size of titles to 255 bytes. |
| 2700 | + * This is typically the size of the underlying database field. |
| 2701 | + * We make an exception for special pages, which don't need to be stored |
| 2702 | + * in the database, and may edge over 255 bytes due to subpage syntax |
| 2703 | + * for long titles, e.g. [[Special:Block/Long name]] |
| 2704 | + */ |
2724 | 2705 | if ( ( $this->mNamespace != NS_SPECIAL && strlen( $dbkey ) > 255 ) || |
2725 | 2706 | strlen( $dbkey ) > 512 ) |
2726 | 2707 | { |
2727 | 2708 | return false; |
2728 | 2709 | } |
2729 | 2710 | |
2730 | | - # Normally, all wiki links are forced to have an initial capital letter so [[foo]] |
2731 | | - # and [[Foo]] point to the same place. Don't force it for interwikis, since the |
2732 | | - # other site might be case-sensitive. |
| 2711 | + /** |
| 2712 | + * Normally, all wiki links are forced to have |
| 2713 | + * an initial capital letter so [[foo]] and [[Foo]] |
| 2714 | + * point to the same place. |
| 2715 | + * |
| 2716 | + * Don't force it for interwikis, since the other |
| 2717 | + * site might be case-sensitive. |
| 2718 | + */ |
2733 | 2719 | $this->mUserCaseDBKey = $dbkey; |
2734 | 2720 | if ( $this->mInterwiki == '' ) { |
2735 | 2721 | $dbkey = self::capitalize( $dbkey, $this->mNamespace ); |
2736 | 2722 | } |
2737 | 2723 | |
2738 | | - # Can't make a link to a namespace alone... "empty" local links can only be |
2739 | | - # self-links with a fragment identifier. |
2740 | | - if ( $dbkey == '' && $this->mInterwiki == '' && $this->mNamespace != NS_MAIN ) { |
| 2724 | + /** |
| 2725 | + * Can't make a link to a namespace alone... |
| 2726 | + * "empty" local links can only be self-links |
| 2727 | + * with a fragment identifier. |
| 2728 | + */ |
| 2729 | + if ( $dbkey == '' && |
| 2730 | + $this->mInterwiki == '' && |
| 2731 | + $this->mNamespace != NS_MAIN ) { |
2741 | 2732 | return false; |
2742 | 2733 | } |
2743 | | - |
2744 | 2734 | // Allow IPv6 usernames to start with '::' by canonicalizing IPv6 titles. |
2745 | 2735 | // IP names are not allowed for accounts, and can only be referring to |
2746 | 2736 | // edits from the IP. Given '::' abbreviations and caps/lowercaps, |
2747 | 2737 | // there are numerous ways to present the same IP. Having sp:contribs scan |
2748 | 2738 | // them all is silly and having some show the edits and others not is |
2749 | 2739 | // inconsistent. Same for talk/userpages. Keep them normalized instead. |
2750 | | - $dbkey = ( $this->mNamespace == NS_USER || $this->mNamespace == NS_USER_TALK ) |
2751 | | - ? IP::sanitizeIP( $dbkey ) |
2752 | | - : $dbkey; |
2753 | | - |
| 2740 | + $dbkey = ( $this->mNamespace == NS_USER || $this->mNamespace == NS_USER_TALK ) ? |
| 2741 | + IP::sanitizeIP( $dbkey ) : $dbkey; |
2754 | 2742 | // Any remaining initial :s are illegal. |
2755 | 2743 | if ( $dbkey !== '' && ':' == $dbkey { 0 } ) { |
2756 | 2744 | return false; |
— | — | @@ -2772,7 +2760,7 @@ |
2773 | 2761 | * Deprecated for public use, use Title::makeTitle() with fragment parameter. |
2774 | 2762 | * Still in active use privately. |
2775 | 2763 | * |
2776 | | - * @param $fragment String text |
| 2764 | + * @param $fragment \type{\string} text |
2777 | 2765 | */ |
2778 | 2766 | public function setFragment( $fragment ) { |
2779 | 2767 | $this->mFragment = str_replace( '_', ' ', substr( $fragment, 1 ) ); |
— | — | @@ -2785,7 +2773,7 @@ |
2786 | 2774 | /** |
2787 | 2775 | * Get a Title object associated with the talk page of this article |
2788 | 2776 | * |
2789 | | - * @return Title the object for the talk page |
| 2777 | + * @return \type{Title} the object for the talk page |
2790 | 2778 | */ |
2791 | 2779 | public function getTalkPage() { |
2792 | 2780 | return Title::makeTitle( MWNamespace::getTalk( $this->getNamespace() ), $this->getDBkey() ); |
— | — | @@ -2795,7 +2783,7 @@ |
2796 | 2784 | * Get a title object associated with the subject page of this |
2797 | 2785 | * talk page |
2798 | 2786 | * |
2799 | | - * @return Title the object for the subject page |
| 2787 | + * @return \type{Title} the object for the subject page |
2800 | 2788 | */ |
2801 | 2789 | public function getSubjectPage() { |
2802 | 2790 | // Is this the same title? |
— | — | @@ -2816,7 +2804,7 @@ |
2817 | 2805 | * @param $options Array: may be FOR UPDATE |
2818 | 2806 | * @param $table String: table name |
2819 | 2807 | * @param $prefix String: fields prefix |
2820 | | - * @return Array of Title objects linking here |
| 2808 | + * @return \type{\arrayof{Title}} the Title objects linking here |
2821 | 2809 | */ |
2822 | 2810 | public function getLinksTo( $options = array(), $table = 'pagelinks', $prefix = 'pl' ) { |
2823 | 2811 | $linkCache = LinkCache::singleton(); |
— | — | @@ -2841,13 +2829,13 @@ |
2842 | 2830 | $retVal = array(); |
2843 | 2831 | if ( $db->numRows( $res ) ) { |
2844 | 2832 | foreach ( $res as $row ) { |
2845 | | - $titleObj = Title::makeTitle( $row->page_namespace, $row->page_title ); |
2846 | | - if ( $titleObj ) { |
| 2833 | + if ( $titleObj = Title::makeTitle( $row->page_namespace, $row->page_title ) ) { |
2847 | 2834 | $linkCache->addGoodLinkObj( $row->page_id, $titleObj, $row->page_len, $row->page_is_redirect, $row->page_latest ); |
2848 | 2835 | $retVal[] = $titleObj; |
2849 | 2836 | } |
2850 | 2837 | } |
2851 | 2838 | } |
| 2839 | + $db->freeResult( $res ); |
2852 | 2840 | return $retVal; |
2853 | 2841 | } |
2854 | 2842 | |
— | — | @@ -2859,7 +2847,7 @@ |
2860 | 2848 | * On heavily-used templates it will max out the memory. |
2861 | 2849 | * |
2862 | 2850 | * @param $options Array: may be FOR UPDATE |
2863 | | - * @return Array of Title the Title objects linking here |
| 2851 | + * @return \type{\arrayof{Title}} the Title objects linking here |
2864 | 2852 | */ |
2865 | 2853 | public function getTemplateLinksTo( $options = array() ) { |
2866 | 2854 | return $this->getLinksTo( $options, 'templatelinks', 'tl' ); |
— | — | @@ -2869,7 +2857,7 @@ |
2870 | 2858 | * Get an array of Title objects referring to non-existent articles linked from this page |
2871 | 2859 | * |
2872 | 2860 | * @todo check if needed (used only in SpecialBrokenRedirects.php, and should use redirect table in this case) |
2873 | | - * @return Array of Title the Title objects |
| 2861 | + * @return \type{\arrayof{Title}} the Title objects |
2874 | 2862 | */ |
2875 | 2863 | public function getBrokenLinksFrom() { |
2876 | 2864 | if ( $this->getArticleId() == 0 ) { |
— | — | @@ -2906,7 +2894,7 @@ |
2907 | 2895 | * Get a list of URLs to purge from the Squid cache when this |
2908 | 2896 | * page changes |
2909 | 2897 | * |
2910 | | - * @return Array of String the URLs |
| 2898 | + * @return \type{\arrayof{\string}} the URLs |
2911 | 2899 | */ |
2912 | 2900 | public function getSquidURLs() { |
2913 | 2901 | global $wgContLang; |
— | — | @@ -2942,8 +2930,8 @@ |
2943 | 2931 | /** |
2944 | 2932 | * Move this page without authentication |
2945 | 2933 | * |
2946 | | - * @param $nt Title the new page Title |
2947 | | - * @return Mixed true on success, getUserPermissionsErrors()-like array on failure |
| 2934 | + * @param $nt \type{Title} the new page Title |
| 2935 | + * @return \type{\mixed} true on success, getUserPermissionsErrors()-like array on failure |
2948 | 2936 | */ |
2949 | 2937 | public function moveNoAuth( &$nt ) { |
2950 | 2938 | return $this->moveTo( $nt, false ); |
— | — | @@ -2953,11 +2941,11 @@ |
2954 | 2942 | * Check whether a given move operation would be valid. |
2955 | 2943 | * Returns true if ok, or a getUserPermissionsErrors()-like array otherwise |
2956 | 2944 | * |
2957 | | - * @param $nt Title the new title |
2958 | | - * @param $auth Bool indicates whether $wgUser's permissions |
| 2945 | + * @param $nt \type{Title} the new title |
| 2946 | + * @param $auth \type{\bool} indicates whether $wgUser's permissions |
2959 | 2947 | * should be checked |
2960 | | - * @param $reason String is the log summary of the move, used for spam checking |
2961 | | - * @return Mixed True on success, getUserPermissionsErrors()-like array on failure |
| 2948 | + * @param $reason \type{\string} is the log summary of the move, used for spam checking |
| 2949 | + * @return \type{\mixed} True on success, getUserPermissionsErrors()-like array on failure |
2962 | 2950 | */ |
2963 | 2951 | public function isValidMoveOperation( &$nt, $auth = true, $reason = '' ) { |
2964 | 2952 | global $wgUser; |
— | — | @@ -2989,13 +2977,28 @@ |
2990 | 2978 | } |
2991 | 2979 | if ( ( $this->getDBkey() == '' ) || |
2992 | 2980 | ( !$oldid ) || |
2993 | | - ( $nt->getDBkey() == '' ) ) { |
| 2981 | + ( $nt->getDBkey() == '' ) ) { |
2994 | 2982 | $errors[] = array( 'badarticleerror' ); |
2995 | 2983 | } |
2996 | 2984 | |
2997 | 2985 | // Image-specific checks |
2998 | 2986 | if ( $this->getNamespace() == NS_FILE ) { |
2999 | | - $errors = array_merge( $errors, $this->validateFileMoveOperation( $nt ) ); |
| 2987 | + if ( $nt->getNamespace() != NS_FILE ) { |
| 2988 | + $errors[] = array( 'imagenocrossnamespace' ); |
| 2989 | + } |
| 2990 | + $file = wfLocalFile( $this ); |
| 2991 | + if ( $file->exists() ) { |
| 2992 | + if ( $nt->getText() != wfStripIllegalFilenameChars( $nt->getText() ) ) { |
| 2993 | + $errors[] = array( 'imageinvalidfilename' ); |
| 2994 | + } |
| 2995 | + if ( !File::checkExtensionCompatibility( $file, $nt->getDBkey() ) ) { |
| 2996 | + $errors[] = array( 'imagetypemismatch' ); |
| 2997 | + } |
| 2998 | + } |
| 2999 | + $destfile = wfLocalFile( $nt ); |
| 3000 | + if ( !$wgUser->isAllowed( 'reupload-shared' ) && !$destfile->exists() && wfFindFile( $nt ) ) { |
| 3001 | + $errors[] = array( 'file-exists-sharedrepo' ); |
| 3002 | + } |
3000 | 3003 | } |
3001 | 3004 | |
3002 | 3005 | if ( $nt->getNamespace() == NS_FILE && $this->getNamespace() != NS_FILE ) { |
— | — | @@ -3043,56 +3046,25 @@ |
3044 | 3047 | } |
3045 | 3048 | |
3046 | 3049 | /** |
3047 | | - * Check if the requested move target is a valid file move target |
3048 | | - * @param Title $nt Target title |
3049 | | - * @return array List of errors |
3050 | | - */ |
3051 | | - protected function validateFileMoveOperation( $nt ) { |
3052 | | - global $wgUser; |
3053 | | - |
3054 | | - $errors = array(); |
3055 | | - |
3056 | | - if ( $nt->getNamespace() != NS_FILE ) { |
3057 | | - $errors[] = array( 'imagenocrossnamespace' ); |
3058 | | - } |
3059 | | - |
3060 | | - $file = wfLocalFile( $this ); |
3061 | | - if ( $file->exists() ) { |
3062 | | - if ( $nt->getText() != wfStripIllegalFilenameChars( $nt->getText() ) ) { |
3063 | | - $errors[] = array( 'imageinvalidfilename' ); |
3064 | | - } |
3065 | | - if ( !File::checkExtensionCompatibility( $file, $nt->getDBkey() ) ) { |
3066 | | - $errors[] = array( 'imagetypemismatch' ); |
3067 | | - } |
3068 | | - } |
3069 | | - |
3070 | | - $destFile = wfLocalFile( $nt ); |
3071 | | - if ( !$wgUser->isAllowed( 'reupload-shared' ) && !$destFile->exists() && wfFindFile( $nt ) ) { |
3072 | | - $errors[] = array( 'file-exists-sharedrepo' ); |
3073 | | - } |
3074 | | - |
3075 | | - return $errors; |
3076 | | - } |
3077 | | - |
3078 | | - /** |
3079 | 3050 | * Move a title to a new location |
3080 | 3051 | * |
3081 | | - * @param $nt Title the new title |
3082 | | - * @param $auth Bool indicates whether $wgUser's permissions |
| 3052 | + * @param $nt \type{Title} the new title |
| 3053 | + * @param $auth \type{\bool} indicates whether $wgUser's permissions |
3083 | 3054 | * should be checked |
3084 | | - * @param $reason String the reason for the move |
3085 | | - * @param $createRedirect Bool Whether to create a redirect from the old title to the new title. |
| 3055 | + * @param $reason \type{\string} The reason for the move |
| 3056 | + * @param $createRedirect \type{\bool} Whether to create a redirect from the old title to the new title. |
3086 | 3057 | * Ignored if the user doesn't have the suppressredirect right. |
3087 | | - * @return Mixed true on success, getUserPermissionsErrors()-like array on failure |
| 3058 | + * @return \type{\mixed} true on success, getUserPermissionsErrors()-like array on failure |
3088 | 3059 | */ |
3089 | 3060 | public function moveTo( &$nt, $auth = true, $reason = '', $createRedirect = true ) { |
| 3061 | + global $wgContLang, $wgEnableInterwikiTemplatesTracking, $wgGlobalDatabase; |
| 3062 | + |
3090 | 3063 | $err = $this->isValidMoveOperation( $nt, $auth, $reason ); |
3091 | 3064 | if ( is_array( $err ) ) { |
3092 | 3065 | return $err; |
3093 | 3066 | } |
3094 | 3067 | |
3095 | | - // If it is a file, move it first. It is done before all other moving stuff is |
3096 | | - // done because it's hard to revert |
| 3068 | + // If it is a file, move it first. It is done before all other moving stuff is done because it's hard to revert |
3097 | 3069 | $dbw = wfGetDB( DB_MASTER ); |
3098 | 3070 | if ( $this->getNamespace() == NS_FILE ) { |
3099 | 3071 | $file = wfLocalFile( $this ); |
— | — | @@ -3104,42 +3076,43 @@ |
3105 | 3077 | } |
3106 | 3078 | } |
3107 | 3079 | |
3108 | | - $dbw->begin(); # If $file was a LocalFile, its transaction would have closed our own. |
3109 | | - $pageid = $this->getArticleID( GAID_FOR_UPDATE ); |
| 3080 | + $pageid = $this->getArticleID(); |
3110 | 3081 | $protected = $this->isProtected(); |
3111 | | - $pageCountChange = ( $createRedirect ? 1 : 0 ) - ( $nt->exists() ? 1 : 0 ); |
| 3082 | + if ( $nt->exists() ) { |
| 3083 | + $err = $this->moveOverExistingRedirect( $nt, $reason, $createRedirect ); |
| 3084 | + $pageCountChange = ( $createRedirect ? 0 : -1 ); |
| 3085 | + } else { # Target didn't exist, do normal move. |
| 3086 | + $err = $this->moveToNewTitle( $nt, $reason, $createRedirect ); |
| 3087 | + $pageCountChange = ( $createRedirect ? 1 : 0 ); |
| 3088 | + } |
3112 | 3089 | |
3113 | | - // Do the actual move |
3114 | | - $err = $this->moveToInternal( $nt, $reason, $createRedirect ); |
3115 | 3090 | if ( is_array( $err ) ) { |
3116 | | - # FIXME: What about the File we have already moved? |
3117 | | - $dbw->rollback(); |
3118 | 3091 | return $err; |
3119 | 3092 | } |
3120 | | - |
3121 | 3093 | $redirid = $this->getArticleID(); |
3122 | 3094 | |
3123 | 3095 | // Refresh the sortkey for this row. Be careful to avoid resetting |
3124 | 3096 | // cl_timestamp, which may disturb time-based lists on some sites. |
3125 | | - $prefixes = $dbw->select( |
| 3097 | + $prefix = $dbw->selectField( |
3126 | 3098 | 'categorylinks', |
3127 | | - array( 'cl_sortkey_prefix', 'cl_to' ), |
| 3099 | + 'cl_sortkey_prefix', |
3128 | 3100 | array( 'cl_from' => $pageid ), |
3129 | 3101 | __METHOD__ |
3130 | 3102 | ); |
3131 | | - foreach ( $prefixes as $prefixRow ) { |
3132 | | - $prefix = $prefixRow->cl_sortkey_prefix; |
3133 | | - $catTo = $prefixRow->cl_to; |
3134 | | - $dbw->update( 'categorylinks', |
3135 | | - array( |
3136 | | - 'cl_sortkey' => Collation::singleton()->getSortKey( |
3137 | | - $nt->getCategorySortkey( $prefix ) ), |
3138 | | - 'cl_timestamp=cl_timestamp' ), |
3139 | | - array( |
3140 | | - 'cl_from' => $pageid, |
3141 | | - 'cl_to' => $catTo ), |
3142 | | - __METHOD__ |
3143 | | - ); |
| 3103 | + $dbw->update( 'categorylinks', |
| 3104 | + array( |
| 3105 | + 'cl_sortkey' => $wgContLang->convertToSortkey( $nt->getCategorySortkey( $prefix ) ), |
| 3106 | + 'cl_timestamp=cl_timestamp' ), |
| 3107 | + array( 'cl_from' => $pageid ), |
| 3108 | + __METHOD__ ); |
| 3109 | + |
| 3110 | + if ( $wgEnableInterwikiTemplatesTracking && $wgGlobalDatabase ) { |
| 3111 | + $dbw2 = wfGetDB( DB_MASTER, array(), $wgGlobalDatabase ); |
| 3112 | + $dbw2->update( 'globaltemplatelinks', |
| 3113 | + array( 'gtl_from_namespace' => $nt->getNsText(), |
| 3114 | + 'gtl_from_title' => $nt->getText() ), |
| 3115 | + array ( 'gtl_from_page' => $pageid ), |
| 3116 | + __METHOD__ ); |
3144 | 3117 | } |
3145 | 3118 | |
3146 | 3119 | if ( $protected ) { |
— | — | @@ -3182,8 +3155,6 @@ |
3183 | 3156 | $u = new SearchUpdate( $redirid, $this->getPrefixedDBkey(), '' ); |
3184 | 3157 | $u->doUpdate(); |
3185 | 3158 | |
3186 | | - $dbw->commit(); |
3187 | | - |
3188 | 3159 | # Update site_stats |
3189 | 3160 | if ( $this->isContentPage() && !$nt->isContentPage() ) { |
3190 | 3161 | # No longer a content page |
— | — | @@ -3204,19 +3175,20 @@ |
3205 | 3176 | $u->doUpdate(); |
3206 | 3177 | } |
3207 | 3178 | # Update message cache for interface messages |
3208 | | - if ( $this->getNamespace() == NS_MEDIAWIKI ) { |
| 3179 | + if ( $nt->getNamespace() == NS_MEDIAWIKI ) { |
| 3180 | + global $wgMessageCache; |
| 3181 | + |
3209 | 3182 | # @bug 17860: old article can be deleted, if this the case, |
3210 | 3183 | # delete it from message cache |
3211 | 3184 | if ( $this->getArticleID() === 0 ) { |
3212 | | - MessageCache::singleton()->replace( $this->getDBkey(), false ); |
| 3185 | + $wgMessageCache->replace( $this->getDBkey(), false ); |
3213 | 3186 | } else { |
3214 | 3187 | $oldarticle = new Article( $this ); |
3215 | | - MessageCache::singleton()->replace( $this->getDBkey(), $oldarticle->getContent() ); |
| 3188 | + $wgMessageCache->replace( $this->getDBkey(), $oldarticle->getContent() ); |
3216 | 3189 | } |
3217 | | - } |
3218 | | - if ( $nt->getNamespace() == NS_MEDIAWIKI ) { |
| 3190 | + |
3219 | 3191 | $newarticle = new Article( $nt ); |
3220 | | - MessageCache::singleton()->replace( $nt->getDBkey(), $newarticle->getContent() ); |
| 3192 | + $wgMessageCache->replace( $nt->getDBkey(), $newarticle->getContent() ); |
3221 | 3193 | } |
3222 | 3194 | |
3223 | 3195 | global $wgUser; |
— | — | @@ -3225,74 +3197,72 @@ |
3226 | 3198 | } |
3227 | 3199 | |
3228 | 3200 | /** |
3229 | | - * Move page to a title which is either a redirect to the |
3230 | | - * source page or nonexistent |
| 3201 | + * Move page to a title which is at present a redirect to the |
| 3202 | + * source page |
3231 | 3203 | * |
3232 | | - * @param $nt Title the page to move to, which should be a redirect or nonexistent |
3233 | | - * @param $reason String The reason for the move |
3234 | | - * @param $createRedirect Bool Whether to leave a redirect at the old title. Ignored |
3235 | | - * if the user doesn't have the suppressredirect right |
| 3204 | + * @param $nt \type{Title} the page to move to, which should currently |
| 3205 | + * be a redirect |
| 3206 | + * @param $reason \type{\string} The reason for the move |
| 3207 | + * @param $createRedirect \type{\bool} Whether to leave a redirect at the old title. |
| 3208 | + * Ignored if the user doesn't have the suppressredirect right |
3236 | 3209 | */ |
3237 | | - private function moveToInternal( &$nt, $reason = '', $createRedirect = true ) { |
3238 | | - global $wgUser, $wgContLang; |
| 3210 | + private function moveOverExistingRedirect( &$nt, $reason = '', $createRedirect = true ) { |
| 3211 | + global $wgUseSquid, $wgUser, $wgContLang, $wgEnableInterwikiTemplatesTracking, $wgGlobalDatabase; |
3239 | 3212 | |
3240 | | - $moveOverRedirect = $nt->exists(); |
| 3213 | + $comment = wfMsgForContent( '1movedto2_redir', $this->getPrefixedText(), $nt->getPrefixedText() ); |
3241 | 3214 | |
3242 | | - $commentMsg = ( $moveOverRedirect ? '1movedto2_redir' : '1movedto2' ); |
3243 | | - $comment = wfMsgForContent( $commentMsg, $this->getPrefixedText(), $nt->getPrefixedText() ); |
3244 | | - |
3245 | 3215 | if ( $reason ) { |
3246 | 3216 | $comment .= wfMsgForContent( 'colon-separator' ) . $reason; |
3247 | 3217 | } |
3248 | | - # Truncate for whole multibyte characters. |
3249 | | - $comment = $wgContLang->truncate( $comment, 255 ); |
| 3218 | + # Truncate for whole multibyte characters. +5 bytes for ellipsis |
| 3219 | + $comment = $wgContLang->truncate( $comment, 250 ); |
3250 | 3220 | |
| 3221 | + $now = wfTimestampNow(); |
| 3222 | + $newid = $nt->getArticleID(); |
3251 | 3223 | $oldid = $this->getArticleID(); |
3252 | 3224 | $latest = $this->getLatestRevID(); |
3253 | 3225 | |
3254 | | - $oldns = $this->getNamespace(); |
3255 | | - $olddbk = $this->getDBkey(); |
3256 | | - |
3257 | 3226 | $dbw = wfGetDB( DB_MASTER ); |
3258 | 3227 | |
3259 | | - if ( $moveOverRedirect ) { |
3260 | | - $rcts = $dbw->timestamp( $nt->getEarliestRevTime() ); |
| 3228 | + $rcts = $dbw->timestamp( $nt->getEarliestRevTime() ); |
| 3229 | + $newns = $nt->getNamespace(); |
| 3230 | + $newdbk = $nt->getDBkey(); |
3261 | 3231 | |
3262 | | - $newid = $nt->getArticleID(); |
3263 | | - $newns = $nt->getNamespace(); |
3264 | | - $newdbk = $nt->getDBkey(); |
3265 | | - |
3266 | | - # Delete the old redirect. We don't save it to history since |
3267 | | - # by definition if we've got here it's rather uninteresting. |
3268 | | - # We have to remove it so that the next step doesn't trigger |
3269 | | - # a conflict on the unique namespace+title index... |
3270 | | - $dbw->delete( 'page', array( 'page_id' => $newid ), __METHOD__ ); |
3271 | | - if ( !$dbw->cascadingDeletes() ) { |
3272 | | - $dbw->delete( 'revision', array( 'rev_page' => $newid ), __METHOD__ ); |
3273 | | - global $wgUseTrackbacks; |
3274 | | - if ( $wgUseTrackbacks ) { |
3275 | | - $dbw->delete( 'trackbacks', array( 'tb_page' => $newid ), __METHOD__ ); |
3276 | | - } |
3277 | | - $dbw->delete( 'pagelinks', array( 'pl_from' => $newid ), __METHOD__ ); |
3278 | | - $dbw->delete( 'imagelinks', array( 'il_from' => $newid ), __METHOD__ ); |
3279 | | - $dbw->delete( 'categorylinks', array( 'cl_from' => $newid ), __METHOD__ ); |
3280 | | - $dbw->delete( 'templatelinks', array( 'tl_from' => $newid ), __METHOD__ ); |
3281 | | - $dbw->delete( 'externallinks', array( 'el_from' => $newid ), __METHOD__ ); |
3282 | | - $dbw->delete( 'langlinks', array( 'll_from' => $newid ), __METHOD__ ); |
3283 | | - $dbw->delete( 'redirect', array( 'rd_from' => $newid ), __METHOD__ ); |
| 3232 | + # Delete the old redirect. We don't save it to history since |
| 3233 | + # by definition if we've got here it's rather uninteresting. |
| 3234 | + # We have to remove it so that the next step doesn't trigger |
| 3235 | + # a conflict on the unique namespace+title index... |
| 3236 | + $dbw->delete( 'page', array( 'page_id' => $newid ), __METHOD__ ); |
| 3237 | + if ( !$dbw->cascadingDeletes() ) { |
| 3238 | + $dbw->delete( 'revision', array( 'rev_page' => $newid ), __METHOD__ ); |
| 3239 | + global $wgUseTrackbacks; |
| 3240 | + if ( $wgUseTrackbacks ) { |
| 3241 | + $dbw->delete( 'trackbacks', array( 'tb_page' => $newid ), __METHOD__ ); |
3284 | 3242 | } |
3285 | | - // If the target page was recently created, it may have an entry in recentchanges still |
3286 | | - $dbw->delete( 'recentchanges', |
3287 | | - array( 'rc_timestamp' => $rcts, 'rc_namespace' => $newns, 'rc_title' => $newdbk, 'rc_new' => 1 ), |
3288 | | - __METHOD__ |
3289 | | - ); |
| 3243 | + $dbw->delete( 'pagelinks', array( 'pl_from' => $newid ), __METHOD__ ); |
| 3244 | + $dbw->delete( 'imagelinks', array( 'il_from' => $newid ), __METHOD__ ); |
| 3245 | + $dbw->delete( 'categorylinks', array( 'cl_from' => $newid ), __METHOD__ ); |
| 3246 | + $dbw->delete( 'templatelinks', array( 'tl_from' => $newid ), __METHOD__ ); |
| 3247 | + $dbw->delete( 'externallinks', array( 'el_from' => $newid ), __METHOD__ ); |
| 3248 | + $dbw->delete( 'langlinks', array( 'll_from' => $newid ), __METHOD__ ); |
| 3249 | + $dbw->delete( 'redirect', array( 'rd_from' => $newid ), __METHOD__ ); |
| 3250 | + |
| 3251 | + if ( $wgEnableInterwikiTemplatesTracking && $wgGlobalDatabase ) { |
| 3252 | + $dbw2 = wfGetDB( DB_MASTER, array(), $wgGlobalDatabase ); |
| 3253 | + $dbw2->delete( 'globaltemplatelinks', |
| 3254 | + array( 'gtl_from_wiki' => wfWikiID( ), |
| 3255 | + 'gtl_from_page' => $newid ), |
| 3256 | + __METHOD__ ); |
| 3257 | + } |
3290 | 3258 | } |
| 3259 | + // If the redirect was recently created, it may have an entry in recentchanges still |
| 3260 | + $dbw->delete( 'recentchanges', |
| 3261 | + array( 'rc_timestamp' => $rcts, 'rc_namespace' => $newns, 'rc_title' => $newdbk, 'rc_new' => 1 ), |
| 3262 | + __METHOD__ |
| 3263 | + ); |
3291 | 3264 | |
3292 | 3265 | # Save a null revision in the page's history notifying of the move |
3293 | 3266 | $nullRevision = Revision::newNullRevision( $dbw, $oldid, $comment, true ); |
3294 | | - if ( !is_object( $nullRevision ) ) { |
3295 | | - throw new MWException( 'No valid null revision produced in ' . __METHOD__ ); |
3296 | | - } |
3297 | 3267 | $nullRevId = $nullRevision->insertOn( $dbw ); |
3298 | 3268 | |
3299 | 3269 | $article = new Article( $this ); |
— | — | @@ -3301,7 +3271,7 @@ |
3302 | 3272 | # Change the name of the target page: |
3303 | 3273 | $dbw->update( 'page', |
3304 | 3274 | /* SET */ array( |
3305 | | - 'page_touched' => $dbw->timestamp(), |
| 3275 | + 'page_touched' => $dbw->timestamp( $now ), |
3306 | 3276 | 'page_namespace' => $nt->getNamespace(), |
3307 | 3277 | 'page_title' => $nt->getDBkey(), |
3308 | 3278 | 'page_latest' => $nullRevId, |
— | — | @@ -3337,34 +3307,110 @@ |
3338 | 3308 | __METHOD__ ); |
3339 | 3309 | $redirectSuppressed = false; |
3340 | 3310 | } else { |
3341 | | - // Get rid of old new page entries in Special:NewPages and RC. |
3342 | | - // Needs to be before $this->resetArticleID( 0 ). |
3343 | | - $dbw->delete( 'recentchanges', array( |
3344 | | - 'rc_timestamp' => $dbw->timestamp( $this->getEarliestRevTime() ), |
3345 | | - 'rc_namespace' => $oldns, |
3346 | | - 'rc_title' => $olddbk, |
3347 | | - 'rc_new' => 1 |
3348 | | - ), |
3349 | | - __METHOD__ |
3350 | | - ); |
3351 | | - |
3352 | 3311 | $this->resetArticleID( 0 ); |
3353 | 3312 | $redirectSuppressed = true; |
3354 | 3313 | } |
3355 | 3314 | |
3356 | 3315 | # Log the move |
3357 | 3316 | $log = new LogPage( 'move' ); |
3358 | | - $logType = ( $moveOverRedirect ? 'move_redir' : 'move' ); |
3359 | | - $log->addEntry( $logType, $this, $reason, array( 1 => $nt->getPrefixedText(), 2 => $redirectSuppressed ) ); |
| 3317 | + $log->addEntry( 'move_redir', $this, $reason, array( 1 => $nt->getPrefixedText(), 2 => $redirectSuppressed ) ); |
3360 | 3318 | |
3361 | | - # Purge caches for old and new titles |
3362 | | - if ( $moveOverRedirect ) { |
3363 | | - # A simple purge is enough when moving over a redirect |
3364 | | - $nt->purgeSquid(); |
| 3319 | + # Purge squid |
| 3320 | + if ( $wgUseSquid ) { |
| 3321 | + $urls = array_merge( $nt->getSquidURLs(), $this->getSquidURLs() ); |
| 3322 | + $u = new SquidUpdate( $urls ); |
| 3323 | + $u->doUpdate(); |
| 3324 | + } |
| 3325 | + |
| 3326 | + } |
| 3327 | + |
| 3328 | + /** |
| 3329 | + * Move page to non-existing title. |
| 3330 | + * |
| 3331 | + * @param $nt \type{Title} the new Title |
| 3332 | + * @param $reason \type{\string} The reason for the move |
| 3333 | + * @param $createRedirect \type{\bool} Whether to create a redirect from the old title to the new title |
| 3334 | + * Ignored if the user doesn't have the suppressredirect right |
| 3335 | + */ |
| 3336 | + private function moveToNewTitle( &$nt, $reason = '', $createRedirect = true ) { |
| 3337 | + global $wgUser, $wgContLang; |
| 3338 | + |
| 3339 | + $comment = wfMsgForContent( '1movedto2', $this->getPrefixedText(), $nt->getPrefixedText() ); |
| 3340 | + if ( $reason ) { |
| 3341 | + $comment .= wfMsgExt( 'colon-separator', |
| 3342 | + array( 'escapenoentities', 'content' ) ); |
| 3343 | + $comment .= $reason; |
| 3344 | + } |
| 3345 | + # Truncate for whole multibyte characters. +5 bytes for ellipsis |
| 3346 | + $comment = $wgContLang->truncate( $comment, 250 ); |
| 3347 | + |
| 3348 | + $newid = $nt->getArticleID(); |
| 3349 | + $oldid = $this->getArticleID(); |
| 3350 | + $latest = $this->getLatestRevId(); |
| 3351 | + |
| 3352 | + $dbw = wfGetDB( DB_MASTER ); |
| 3353 | + $now = $dbw->timestamp(); |
| 3354 | + |
| 3355 | + # Save a null revision in the page's history notifying of the move |
| 3356 | + $nullRevision = Revision::newNullRevision( $dbw, $oldid, $comment, true ); |
| 3357 | + if ( !is_object( $nullRevision ) ) { |
| 3358 | + throw new MWException( 'No valid null revision produced in ' . __METHOD__ ); |
| 3359 | + } |
| 3360 | + $nullRevId = $nullRevision->insertOn( $dbw ); |
| 3361 | + |
| 3362 | + $article = new Article( $this ); |
| 3363 | + wfRunHooks( 'NewRevisionFromEditComplete', array( $article, $nullRevision, $latest, $wgUser ) ); |
| 3364 | + |
| 3365 | + # Rename page entry |
| 3366 | + $dbw->update( 'page', |
| 3367 | + /* SET */ array( |
| 3368 | + 'page_touched' => $now, |
| 3369 | + 'page_namespace' => $nt->getNamespace(), |
| 3370 | + 'page_title' => $nt->getDBkey(), |
| 3371 | + 'page_latest' => $nullRevId, |
| 3372 | + ), |
| 3373 | + /* WHERE */ array( 'page_id' => $oldid ), |
| 3374 | + __METHOD__ |
| 3375 | + ); |
| 3376 | + $nt->resetArticleID( $oldid ); |
| 3377 | + |
| 3378 | + if ( $createRedirect || !$wgUser->isAllowed( 'suppressredirect' ) ) { |
| 3379 | + # Insert redirect |
| 3380 | + $mwRedir = MagicWord::get( 'redirect' ); |
| 3381 | + $redirectText = $mwRedir->getSynonym( 0 ) . ' [[' . $nt->getPrefixedText() . "]]\n"; |
| 3382 | + $redirectArticle = new Article( $this ); |
| 3383 | + $newid = $redirectArticle->insertOn( $dbw ); |
| 3384 | + $redirectRevision = new Revision( array( |
| 3385 | + 'page' => $newid, |
| 3386 | + 'comment' => $comment, |
| 3387 | + 'text' => $redirectText ) ); |
| 3388 | + $redirectRevision->insertOn( $dbw ); |
| 3389 | + $redirectArticle->updateRevisionOn( $dbw, $redirectRevision, 0 ); |
| 3390 | + |
| 3391 | + wfRunHooks( 'NewRevisionFromEditComplete', array( $redirectArticle, $redirectRevision, false, $wgUser ) ); |
| 3392 | + |
| 3393 | + # Record the just-created redirect's linking to the page |
| 3394 | + $dbw->insert( 'pagelinks', |
| 3395 | + array( |
| 3396 | + 'pl_from' => $newid, |
| 3397 | + 'pl_namespace' => $nt->getNamespace(), |
| 3398 | + 'pl_title' => $nt->getDBkey() ), |
| 3399 | + __METHOD__ ); |
| 3400 | + $redirectSuppressed = false; |
3365 | 3401 | } else { |
3366 | | - # Purge caches as per article creation, including any pages that link to this title |
3367 | | - Article::onArticleCreate( $nt ); |
| 3402 | + $this->resetArticleID( 0 ); |
| 3403 | + $redirectSuppressed = true; |
3368 | 3404 | } |
| 3405 | + |
| 3406 | + # Log the move |
| 3407 | + $log = new LogPage( 'move' ); |
| 3408 | + $log->addEntry( 'move', $this, $reason, array( 1 => $nt->getPrefixedText(), 2 => $redirectSuppressed ) ); |
| 3409 | + |
| 3410 | + # Purge caches as per article creation |
| 3411 | + Article::onArticleCreate( $nt ); |
| 3412 | + |
| 3413 | + # Purge old title from squid |
| 3414 | + # The new title, and links to the new title, are purged in Article::onArticleCreate() |
3369 | 3415 | $this->purgeSquid(); |
3370 | 3416 | } |
3371 | 3417 | |
— | — | @@ -3374,11 +3420,10 @@ |
3375 | 3421 | * @param $nt Title Move target |
3376 | 3422 | * @param $auth bool Whether $wgUser's permissions should be checked |
3377 | 3423 | * @param $reason string The reason for the move |
3378 | | - * @param $createRedirect bool Whether to create redirects from the old subpages to |
3379 | | - * the new ones Ignored if the user doesn't have the 'suppressredirect' right |
| 3424 | + * @param $createRedirect bool Whether to create redirects from the old subpages to the new ones |
| 3425 | + * Ignored if the user doesn't have the 'suppressredirect' right |
3380 | 3426 | * @return mixed array with old page titles as keys, and strings (new page titles) or |
3381 | | - * arrays (errors) as values, or an error array with numeric indices if no pages |
3382 | | - * were moved |
| 3427 | + * arrays (errors) as values, or an error array with numeric indices if no pages were moved |
3383 | 3428 | */ |
3384 | 3429 | public function moveSubpages( $nt, $auth = true, $reason = '', $createRedirect = true ) { |
3385 | 3430 | global $wgMaximumMovedPages; |
— | — | @@ -3445,7 +3490,7 @@ |
3446 | 3491 | * Checks if this page is just a one-rev redirect. |
3447 | 3492 | * Adds lock, so don't use just for light purposes. |
3448 | 3493 | * |
3449 | | - * @return Bool |
| 3494 | + * @return \type{\bool} |
3450 | 3495 | */ |
3451 | 3496 | public function isSingleRevRedirect() { |
3452 | 3497 | $dbw = wfGetDB( DB_MASTER ); |
— | — | @@ -3482,8 +3527,8 @@ |
3483 | 3528 | * Checks if $this can be moved to a given Title |
3484 | 3529 | * - Selects for update, so don't call it unless you mean business |
3485 | 3530 | * |
3486 | | - * @param $nt Title the new title to check |
3487 | | - * @return Bool |
| 3531 | + * @param $nt \type{Title} the new title to check |
| 3532 | + * @return \type{\bool} TRUE or FALSE |
3488 | 3533 | */ |
3489 | 3534 | public function isValidMoveTarget( $nt ) { |
3490 | 3535 | # Is it an existing file? |
— | — | @@ -3524,7 +3569,7 @@ |
3525 | 3570 | /** |
3526 | 3571 | * Can this title be added to a user's watchlist? |
3527 | 3572 | * |
3528 | | - * @return Bool TRUE or FALSE |
| 3573 | + * @return \type{\bool} TRUE or FALSE |
3529 | 3574 | */ |
3530 | 3575 | public function isWatchable() { |
3531 | 3576 | return !$this->isExternal() && MWNamespace::isWatchable( $this->getNamespace() ); |
— | — | @@ -3534,35 +3579,31 @@ |
3535 | 3580 | * Get categories to which this Title belongs and return an array of |
3536 | 3581 | * categories' names. |
3537 | 3582 | * |
3538 | | - * @return Array of parents in the form: |
3539 | | - * $parent => $currentarticle |
| 3583 | + * @return \type{\array} array an array of parents in the form: |
| 3584 | + * $parent => $currentarticle |
3540 | 3585 | */ |
3541 | 3586 | public function getParentCategories() { |
3542 | 3587 | global $wgContLang; |
3543 | 3588 | |
3544 | | - $data = array(); |
3545 | | - |
3546 | | - $titleKey = $this->getArticleId(); |
3547 | | - |
3548 | | - if ( $titleKey === 0 ) { |
3549 | | - return $data; |
3550 | | - } |
3551 | | - |
| 3589 | + $titlekey = $this->getArticleId(); |
3552 | 3590 | $dbr = wfGetDB( DB_SLAVE ); |
| 3591 | + $categorylinks = $dbr->tableName( 'categorylinks' ); |
3553 | 3592 | |
3554 | | - $res = $dbr->select( 'categorylinks', '*', |
3555 | | - array( |
3556 | | - 'cl_from' => $titleKey, |
3557 | | - ), |
3558 | | - __METHOD__, |
3559 | | - array() |
3560 | | - ); |
| 3593 | + # NEW SQL |
| 3594 | + $sql = "SELECT * FROM $categorylinks" |
| 3595 | + . " WHERE cl_from='$titlekey'" |
| 3596 | + . " AND cl_from <> '0'" |
| 3597 | + . " ORDER BY cl_sortkey"; |
3561 | 3598 | |
| 3599 | + $res = $dbr->query( $sql ); |
| 3600 | + |
3562 | 3601 | if ( $dbr->numRows( $res ) > 0 ) { |
3563 | | - foreach ( $res as $row ) { |
| 3602 | + foreach ( $res as $row ) |
3564 | 3603 | // $data[] = Title::newFromText($wgContLang->getNSText ( NS_CATEGORY ).':'.$row->cl_to); |
3565 | 3604 | $data[$wgContLang->getNSText( NS_CATEGORY ) . ':' . $row->cl_to] = $this->getFullText(); |
3566 | | - } |
| 3605 | + $dbr->freeResult( $res ); |
| 3606 | + } else { |
| 3607 | + $data = array(); |
3567 | 3608 | } |
3568 | 3609 | return $data; |
3569 | 3610 | } |
— | — | @@ -3570,8 +3611,8 @@ |
3571 | 3612 | /** |
3572 | 3613 | * Get a tree of parent categories |
3573 | 3614 | * |
3574 | | - * @param $children Array with the children in the keys, to check for circular refs |
3575 | | - * @return Array Tree of parent categories |
| 3615 | + * @param $children \type{\array} an array with the children in the keys, to check for circular refs |
| 3616 | + * @return \type{\array} Tree of parent categories |
3576 | 3617 | */ |
3577 | 3618 | public function getParentCategoryTree( $children = array() ) { |
3578 | 3619 | $stack = array(); |
— | — | @@ -3589,16 +3630,18 @@ |
3590 | 3631 | } |
3591 | 3632 | } |
3592 | 3633 | } |
| 3634 | + return $stack; |
| 3635 | + } else { |
| 3636 | + return array(); |
3593 | 3637 | } |
3594 | | - |
3595 | | - return $stack; |
3596 | 3638 | } |
3597 | 3639 | |
| 3640 | + |
3598 | 3641 | /** |
3599 | 3642 | * Get an associative array for selecting this title from |
3600 | 3643 | * the "page" table |
3601 | 3644 | * |
3602 | | - * @return Array suitable for the $where parameter of DB::select() |
| 3645 | + * @return \type{\array} Selection array |
3603 | 3646 | */ |
3604 | 3647 | public function pageCond() { |
3605 | 3648 | if ( $this->mArticleID > 0 ) { |
— | — | @@ -3612,12 +3655,12 @@ |
3613 | 3656 | /** |
3614 | 3657 | * Get the revision ID of the previous revision |
3615 | 3658 | * |
3616 | | - * @param $revId Int Revision ID. Get the revision that was before this one. |
3617 | | - * @param $flags Int Title::GAID_FOR_UPDATE |
3618 | | - * @return Int|Bool Old revision ID, or FALSE if none exists |
| 3659 | + * @param $revId \type{\int} Revision ID. Get the revision that was before this one. |
| 3660 | + * @param $flags \type{\int} GAID_FOR_UPDATE |
| 3661 | + * @return \twotypes{\int,\bool} Old revision ID, or FALSE if none exists |
3619 | 3662 | */ |
3620 | 3663 | public function getPreviousRevisionID( $revId, $flags = 0 ) { |
3621 | | - $db = ( $flags & self::GAID_FOR_UPDATE ) ? wfGetDB( DB_MASTER ) : wfGetDB( DB_SLAVE ); |
| 3664 | + $db = ( $flags & GAID_FOR_UPDATE ) ? wfGetDB( DB_MASTER ) : wfGetDB( DB_SLAVE ); |
3622 | 3665 | return $db->selectField( 'revision', 'rev_id', |
3623 | 3666 | array( |
3624 | 3667 | 'rev_page' => $this->getArticleId( $flags ), |
— | — | @@ -3631,12 +3674,12 @@ |
3632 | 3675 | /** |
3633 | 3676 | * Get the revision ID of the next revision |
3634 | 3677 | * |
3635 | | - * @param $revId Int Revision ID. Get the revision that was after this one. |
3636 | | - * @param $flags Int Title::GAID_FOR_UPDATE |
3637 | | - * @return Int|Bool Next revision ID, or FALSE if none exists |
| 3678 | + * @param $revId \type{\int} Revision ID. Get the revision that was after this one. |
| 3679 | + * @param $flags \type{\int} GAID_FOR_UPDATE |
| 3680 | + * @return \twotypes{\int,\bool} Next revision ID, or FALSE if none exists |
3638 | 3681 | */ |
3639 | 3682 | public function getNextRevisionID( $revId, $flags = 0 ) { |
3640 | | - $db = ( $flags & self::GAID_FOR_UPDATE ) ? wfGetDB( DB_MASTER ) : wfGetDB( DB_SLAVE ); |
| 3683 | + $db = ( $flags & GAID_FOR_UPDATE ) ? wfGetDB( DB_MASTER ) : wfGetDB( DB_SLAVE ); |
3641 | 3684 | return $db->selectField( 'revision', 'rev_id', |
3642 | 3685 | array( |
3643 | 3686 | 'rev_page' => $this->getArticleId( $flags ), |
— | — | @@ -3650,37 +3693,28 @@ |
3651 | 3694 | /** |
3652 | 3695 | * Get the first revision of the page |
3653 | 3696 | * |
3654 | | - * @param $flags Int Title::GAID_FOR_UPDATE |
3655 | | - * @return Revision|Null if page doesn't exist |
| 3697 | + * @param $flags \type{\int} GAID_FOR_UPDATE |
| 3698 | + * @return Revision (or NULL if page doesn't exist) |
3656 | 3699 | */ |
3657 | 3700 | public function getFirstRevision( $flags = 0 ) { |
| 3701 | + $db = ( $flags & GAID_FOR_UPDATE ) ? wfGetDB( DB_MASTER ) : wfGetDB( DB_SLAVE ); |
3658 | 3702 | $pageId = $this->getArticleId( $flags ); |
3659 | | - if ( $pageId ) { |
3660 | | - $db = ( $flags & self::GAID_FOR_UPDATE ) ? wfGetDB( DB_MASTER ) : wfGetDB( DB_SLAVE ); |
3661 | | - $row = $db->selectRow( 'revision', '*', |
3662 | | - array( 'rev_page' => $pageId ), |
3663 | | - __METHOD__, |
3664 | | - array( 'ORDER BY' => 'rev_timestamp ASC', 'LIMIT' => 1 ) |
3665 | | - ); |
3666 | | - if ( $row ) { |
3667 | | - return new Revision( $row ); |
3668 | | - } |
| 3703 | + if ( !$pageId ) { |
| 3704 | + return null; |
3669 | 3705 | } |
3670 | | - return null; |
| 3706 | + $row = $db->selectRow( 'revision', '*', |
| 3707 | + array( 'rev_page' => $pageId ), |
| 3708 | + __METHOD__, |
| 3709 | + array( 'ORDER BY' => 'rev_timestamp ASC', 'LIMIT' => 1 ) |
| 3710 | + ); |
| 3711 | + if ( !$row ) { |
| 3712 | + return null; |
| 3713 | + } else { |
| 3714 | + return new Revision( $row ); |
| 3715 | + } |
3671 | 3716 | } |
3672 | 3717 | |
3673 | 3718 | /** |
3674 | | - * Get the oldest revision timestamp of this page |
3675 | | - * |
3676 | | - * @param $flags Int Title::GAID_FOR_UPDATE |
3677 | | - * @return String: MW timestamp |
3678 | | - */ |
3679 | | - public function getEarliestRevTime( $flags = 0 ) { |
3680 | | - $rev = $this->getFirstRevision( $flags ); |
3681 | | - return $rev ? $rev->getTimestamp() : null; |
3682 | | - } |
3683 | | - |
3684 | | - /** |
3685 | 3719 | * Check if this is a new page |
3686 | 3720 | * |
3687 | 3721 | * @return bool |
— | — | @@ -3691,70 +3725,45 @@ |
3692 | 3726 | } |
3693 | 3727 | |
3694 | 3728 | /** |
3695 | | - * Get the number of revisions between the given revision. |
3696 | | - * Used for diffs and other things that really need it. |
| 3729 | + * Get the oldest revision timestamp of this page |
3697 | 3730 | * |
3698 | | - * @param $old int|Revision Old revision or rev ID (first before range) |
3699 | | - * @param $new int|Revision New revision or rev ID (first after range) |
3700 | | - * @return Int Number of revisions between these revisions. |
| 3731 | + * @return String: MW timestamp |
3701 | 3732 | */ |
3702 | | - public function countRevisionsBetween( $old, $new ) { |
3703 | | - if ( !( $old instanceof Revision ) ) { |
3704 | | - $old = Revision::newFromTitle( $this, (int)$old ); |
3705 | | - } |
3706 | | - if ( !( $new instanceof Revision ) ) { |
3707 | | - $new = Revision::newFromTitle( $this, (int)$new ); |
3708 | | - } |
3709 | | - if ( !$old || !$new ) { |
3710 | | - return 0; // nothing to compare |
3711 | | - } |
| 3733 | + public function getEarliestRevTime() { |
3712 | 3734 | $dbr = wfGetDB( DB_SLAVE ); |
3713 | | - return (int)$dbr->selectField( 'revision', 'count(*)', |
3714 | | - array( |
3715 | | - 'rev_page' => $this->getArticleId(), |
3716 | | - 'rev_timestamp > ' . $dbr->addQuotes( $dbr->timestamp( $old->getTimestamp() ) ), |
3717 | | - 'rev_timestamp < ' . $dbr->addQuotes( $dbr->timestamp( $new->getTimestamp() ) ) |
3718 | | - ), |
3719 | | - __METHOD__ |
3720 | | - ); |
| 3735 | + if ( $this->exists() ) { |
| 3736 | + $min = $dbr->selectField( 'revision', |
| 3737 | + 'MIN(rev_timestamp)', |
| 3738 | + array( 'rev_page' => $this->getArticleId() ), |
| 3739 | + __METHOD__ ); |
| 3740 | + return wfTimestampOrNull( TS_MW, $min ); |
| 3741 | + } |
| 3742 | + return null; |
3721 | 3743 | } |
3722 | 3744 | |
3723 | 3745 | /** |
3724 | | - * Get the number of authors between the given revision IDs. |
| 3746 | + * Get the number of revisions between the given revision IDs. |
3725 | 3747 | * Used for diffs and other things that really need it. |
3726 | 3748 | * |
3727 | | - * @param $old int|Revision Old revision or rev ID (first before range) |
3728 | | - * @param $new int|Revision New revision or rev ID (first after range) |
3729 | | - * @param $limit Int Maximum number of authors |
3730 | | - * @return Int Number of revision authors between these revisions. |
| 3749 | + * @param $old \type{\int} Revision ID. |
| 3750 | + * @param $new \type{\int} Revision ID. |
| 3751 | + * @return \type{\int} Number of revisions between these IDs. |
3731 | 3752 | */ |
3732 | | - public function countAuthorsBetween( $old, $new, $limit ) { |
3733 | | - if ( !( $old instanceof Revision ) ) { |
3734 | | - $old = Revision::newFromTitle( $this, (int)$old ); |
3735 | | - } |
3736 | | - if ( !( $new instanceof Revision ) ) { |
3737 | | - $new = Revision::newFromTitle( $this, (int)$new ); |
3738 | | - } |
3739 | | - if ( !$old || !$new ) { |
3740 | | - return 0; // nothing to compare |
3741 | | - } |
| 3753 | + public function countRevisionsBetween( $old, $new ) { |
3742 | 3754 | $dbr = wfGetDB( DB_SLAVE ); |
3743 | | - $res = $dbr->select( 'revision', 'DISTINCT rev_user_text', |
3744 | | - array( |
3745 | | - 'rev_page' => $this->getArticleID(), |
3746 | | - 'rev_timestamp > ' . $dbr->addQuotes( $dbr->timestamp( $old->getTimestamp() ) ), |
3747 | | - 'rev_timestamp < ' . $dbr->addQuotes( $dbr->timestamp( $new->getTimestamp() ) ) |
3748 | | - ), __METHOD__, |
3749 | | - array( 'LIMIT' => $limit + 1 ) // add one so caller knows it was truncated |
| 3755 | + return (int)$dbr->selectField( 'revision', 'count(*)', |
| 3756 | + 'rev_page = ' . intval( $this->getArticleId() ) . |
| 3757 | + ' AND rev_id > ' . intval( $old ) . |
| 3758 | + ' AND rev_id < ' . intval( $new ), |
| 3759 | + __METHOD__ |
3750 | 3760 | ); |
3751 | | - return (int)$dbr->numRows( $res ); |
3752 | 3761 | } |
3753 | 3762 | |
3754 | 3763 | /** |
3755 | 3764 | * Compare with another title. |
3756 | 3765 | * |
3757 | | - * @param $title Title |
3758 | | - * @return Bool |
| 3766 | + * @param $title \type{Title} |
| 3767 | + * @return \type{\bool} TRUE or FALSE |
3759 | 3768 | */ |
3760 | 3769 | public function equals( Title $title ) { |
3761 | 3770 | // Note: === is necessary for proper matching of number-like titles. |
— | — | @@ -3765,10 +3774,7 @@ |
3766 | 3775 | |
3767 | 3776 | /** |
3768 | 3777 | * Callback for usort() to do title sorts by (namespace, title) |
3769 | | - * |
3770 | | - * @param $a Title |
3771 | | - * @param $b Title |
3772 | | - * |
| 3778 | + * |
3773 | 3779 | * @return Integer: result of string comparison, or namespace comparison |
3774 | 3780 | */ |
3775 | 3781 | public static function compare( $a, $b ) { |
— | — | @@ -3782,7 +3788,7 @@ |
3783 | 3789 | /** |
3784 | 3790 | * Return a string representation of this title |
3785 | 3791 | * |
3786 | | - * @return String representation of this title |
| 3792 | + * @return \type{\string} String representation of this title |
3787 | 3793 | */ |
3788 | 3794 | public function __toString() { |
3789 | 3795 | return $this->getPrefixedText(); |
— | — | @@ -3795,7 +3801,7 @@ |
3796 | 3802 | * If you want to know if a title can be meaningfully viewed, you should |
3797 | 3803 | * probably call the isKnown() method instead. |
3798 | 3804 | * |
3799 | | - * @return Bool |
| 3805 | + * @return \type{\bool} |
3800 | 3806 | */ |
3801 | 3807 | public function exists() { |
3802 | 3808 | return $this->getArticleId() != 0; |
— | — | @@ -3815,28 +3821,28 @@ |
3816 | 3822 | * existing code, but we might want to add an optional parameter to skip |
3817 | 3823 | * it and any other expensive checks.) |
3818 | 3824 | * |
3819 | | - * @return Bool |
| 3825 | + * @return \type{\bool} |
3820 | 3826 | */ |
3821 | 3827 | public function isAlwaysKnown() { |
3822 | 3828 | if ( $this->mInterwiki != '' ) { |
3823 | 3829 | return true; // any interwiki link might be viewable, for all we know |
3824 | 3830 | } |
3825 | 3831 | switch( $this->mNamespace ) { |
3826 | | - case NS_MEDIA: |
3827 | | - case NS_FILE: |
3828 | | - // file exists, possibly in a foreign repo |
3829 | | - return (bool)wfFindFile( $this ); |
3830 | | - case NS_SPECIAL: |
3831 | | - // valid special page |
3832 | | - return SpecialPageFactory::exists( $this->getDBkey() ); |
3833 | | - case NS_MAIN: |
3834 | | - // selflink, possibly with fragment |
3835 | | - return $this->mDbkeyform == ''; |
3836 | | - case NS_MEDIAWIKI: |
3837 | | - // known system message |
3838 | | - return $this->getDefaultMessageText() !== false; |
3839 | | - default: |
3840 | | - return false; |
| 3832 | + case NS_MEDIA: |
| 3833 | + case NS_FILE: |
| 3834 | + return (bool)wfFindFile( $this ); // file exists, possibly in a foreign repo |
| 3835 | + case NS_SPECIAL: |
| 3836 | + return SpecialPage::exists( $this->getDBkey() ); // valid special page |
| 3837 | + case NS_MAIN: |
| 3838 | + return $this->mDbkeyform == ''; // selflink, possibly with fragment |
| 3839 | + case NS_MEDIAWIKI: |
| 3840 | + // If the page is form Mediawiki:message/lang, calling wfMsgWeirdKey causes |
| 3841 | + // the full l10n of that language to be loaded. That takes much memory and |
| 3842 | + // isn't needed. So we strip the language part away. |
| 3843 | + list( $basename, /* rest */ ) = explode( '/', $this->mDbkeyform, 2 ); |
| 3844 | + return (bool)wfMsgWeirdKey( $basename ); // known system message |
| 3845 | + default: |
| 3846 | + return false; |
3841 | 3847 | } |
3842 | 3848 | } |
3843 | 3849 | |
— | — | @@ -3846,10 +3852,10 @@ |
3847 | 3853 | * links to the title should be rendered as "bluelinks" (as opposed to |
3848 | 3854 | * "redlinks" to non-existent pages). |
3849 | 3855 | * |
3850 | | - * @return Bool |
| 3856 | + * @return \type{\bool} |
3851 | 3857 | */ |
3852 | 3858 | public function isKnown() { |
3853 | | - return $this->isAlwaysKnown() || $this->exists(); |
| 3859 | + return $this->exists() || $this->isAlwaysKnown(); |
3854 | 3860 | } |
3855 | 3861 | |
3856 | 3862 | /** |
— | — | @@ -3865,38 +3871,20 @@ |
3866 | 3872 | if ( $this->mNamespace == NS_MEDIAWIKI ) { |
3867 | 3873 | // If the page doesn't exist but is a known system message, default |
3868 | 3874 | // message content will be displayed, same for language subpages |
3869 | | - return $this->getDefaultMessageText() !== false; |
| 3875 | + // Also, if the page is form Mediawiki:message/lang, calling wfMsgWeirdKey |
| 3876 | + // causes the full l10n of that language to be loaded. That takes much |
| 3877 | + // memory and isn't needed. So we strip the language part away. |
| 3878 | + list( $basename, /* rest */ ) = explode( '/', $this->mDbkeyform, 2 ); |
| 3879 | + return (bool)wfMsgWeirdKey( $basename ); |
3870 | 3880 | } |
3871 | 3881 | |
3872 | 3882 | return false; |
3873 | 3883 | } |
3874 | 3884 | |
3875 | 3885 | /** |
3876 | | - * Get the default message text or false if the message doesn't exist |
3877 | | - * |
3878 | | - * @return String or false |
3879 | | - */ |
3880 | | - public function getDefaultMessageText() { |
3881 | | - global $wgContLang; |
3882 | | - |
3883 | | - if ( $this->getNamespace() != NS_MEDIAWIKI ) { // Just in case |
3884 | | - return false; |
3885 | | - } |
3886 | | - |
3887 | | - list( $name, $lang ) = MessageCache::singleton()->figureMessage( $wgContLang->lcfirst( $this->getText() ) ); |
3888 | | - $message = wfMessage( $name )->inLanguage( $lang )->useDatabase( false ); |
3889 | | - |
3890 | | - if ( $message->exists() ) { |
3891 | | - return $message->plain(); |
3892 | | - } else { |
3893 | | - return false; |
3894 | | - } |
3895 | | - } |
3896 | | - |
3897 | | - /** |
3898 | 3886 | * Is this in a namespace that allows actual pages? |
3899 | 3887 | * |
3900 | | - * @return Bool |
| 3888 | + * @return \type{\bool} |
3901 | 3889 | * @internal note -- uses hardcoded namespace index instead of constants |
3902 | 3890 | */ |
3903 | 3891 | public function canExist() { |
— | — | @@ -3922,7 +3910,7 @@ |
3923 | 3911 | * Get the last touched timestamp |
3924 | 3912 | * |
3925 | 3913 | * @param $db DatabaseBase: optional db |
3926 | | - * @return String last-touched timestamp |
| 3914 | + * @return \type{\string} Last touched timestamp |
3927 | 3915 | */ |
3928 | 3916 | public function getTouched( $db = null ) { |
3929 | 3917 | $db = isset( $db ) ? $db : wfGetDB( DB_SLAVE ); |
— | — | @@ -3934,7 +3922,7 @@ |
3935 | 3923 | * Get the timestamp when this page was updated since the user last saw it. |
3936 | 3924 | * |
3937 | 3925 | * @param $user User |
3938 | | - * @return String|Null |
| 3926 | + * @return Mixed: string/null |
3939 | 3927 | */ |
3940 | 3928 | public function getNotificationTimestamp( $user = null ) { |
3941 | 3929 | global $wgUser, $wgShowUpdatedMarker; |
— | — | @@ -3944,8 +3932,7 @@ |
3945 | 3933 | } |
3946 | 3934 | // Check cache first |
3947 | 3935 | $uid = $user->getId(); |
3948 | | - // avoid isset here, as it'll return false for null entries |
3949 | | - if ( array_key_exists( $uid, $this->mNotificationTimestamp ) ) { |
| 3936 | + if ( isset( $this->mNotificationTimestamp[$uid] ) ) { |
3950 | 3937 | return $this->mNotificationTimestamp[$uid]; |
3951 | 3938 | } |
3952 | 3939 | if ( !$uid || !$wgShowUpdatedMarker ) { |
— | — | @@ -3970,7 +3957,7 @@ |
3971 | 3958 | /** |
3972 | 3959 | * Get the trackback URL for this page |
3973 | 3960 | * |
3974 | | - * @return String Trackback URL |
| 3961 | + * @return \type{\string} Trackback URL |
3975 | 3962 | */ |
3976 | 3963 | public function trackbackURL() { |
3977 | 3964 | global $wgScriptPath, $wgServer, $wgScriptExtension; |
— | — | @@ -3982,7 +3969,7 @@ |
3983 | 3970 | /** |
3984 | 3971 | * Get the trackback RDF for this page |
3985 | 3972 | * |
3986 | | - * @return String Trackback RDF |
| 3973 | + * @return \type{\string} Trackback RDF |
3987 | 3974 | */ |
3988 | 3975 | public function trackbackRDF() { |
3989 | 3976 | $url = htmlspecialchars( $this->getFullURL() ); |
— | — | @@ -3996,8 +3983,8 @@ |
3997 | 3984 | // Spec: http://www.sixapart.com/pronet/docs/trackback_spec |
3998 | 3985 | return "<!-- |
3999 | 3986 | <rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\" |
4000 | | - xmlns:dc=\"http://purl.org/dc/elements/1.1/\" |
4001 | | - xmlns:trackback=\"http://madskills.com/public/xml/rss/module/trackback/\"> |
| 3987 | + xmlns:dc=\"http://purl.org/dc/elements/1.1/\" |
| 3988 | + xmlns:trackback=\"http://madskills.com/public/xml/rss/module/trackback/\"> |
4002 | 3989 | <rdf:Description |
4003 | 3990 | rdf:about=\"$url\" |
4004 | 3991 | dc:identifier=\"$url\" |
— | — | @@ -4011,7 +3998,7 @@ |
4012 | 3999 | * Generate strings used for xml 'id' names in monobook tabs |
4013 | 4000 | * |
4014 | 4001 | * @param $prepend string defaults to 'nstab-' |
4015 | | - * @return String XML 'id' name |
| 4002 | + * @return \type{\string} XML 'id' name |
4016 | 4003 | */ |
4017 | 4004 | public function getNamespaceKey( $prepend = 'nstab-' ) { |
4018 | 4005 | global $wgContLang; |
— | — | @@ -4050,12 +4037,12 @@ |
4051 | 4038 | /** |
4052 | 4039 | * Returns true if this title resolves to the named special page |
4053 | 4040 | * |
4054 | | - * @param $name String The special page name |
| 4041 | + * @param $name \type{\string} The special page name |
4055 | 4042 | * @return boolean |
4056 | 4043 | */ |
4057 | 4044 | public function isSpecial( $name ) { |
4058 | 4045 | if ( $this->getNamespace() == NS_SPECIAL ) { |
4059 | | - list( $thisName, /* $subpage */ ) = SpecialPageFactory::resolveAlias( $this->getDBkey() ); |
| 4046 | + list( $thisName, /* $subpage */ ) = SpecialPage::resolveAliasWithSubpage( $this->getDBkey() ); |
4060 | 4047 | if ( $name == $thisName ) { |
4061 | 4048 | return true; |
4062 | 4049 | } |
— | — | @@ -4064,16 +4051,16 @@ |
4065 | 4052 | } |
4066 | 4053 | |
4067 | 4054 | /** |
4068 | | - * If the Title refers to a special page alias which is not the local default, resolve |
4069 | | - * the alias, and localise the name as necessary. Otherwise, return $this |
| 4055 | + * If the Title refers to a special page alias which is not the local default, |
4070 | 4056 | * |
4071 | | - * @return Title |
| 4057 | + * @return \type{Title} A new Title which points to the local default. |
| 4058 | + * Otherwise, returns $this. |
4072 | 4059 | */ |
4073 | 4060 | public function fixSpecialName() { |
4074 | 4061 | if ( $this->getNamespace() == NS_SPECIAL ) { |
4075 | | - list( $canonicalName, /*...*/ ) = SpecialPageFactory::resolveAlias( $this->mDbkeyform ); |
| 4062 | + $canonicalName = SpecialPage::resolveAlias( $this->mDbkeyform ); |
4076 | 4063 | if ( $canonicalName ) { |
4077 | | - $localName = SpecialPageFactory::getLocalNameFor( $canonicalName ); |
| 4064 | + $localName = SpecialPage::getLocalNameFor( $canonicalName ); |
4078 | 4065 | if ( $localName != $this->mDbkeyform ) { |
4079 | 4066 | return Title::makeTitle( NS_SPECIAL, $localName ); |
4080 | 4067 | } |
— | — | @@ -4087,7 +4074,7 @@ |
4088 | 4075 | * In other words, is this a content page, for the purposes of calculating |
4089 | 4076 | * statistics, etc? |
4090 | 4077 | * |
4091 | | - * @return Boolean |
| 4078 | + * @return \type{\bool} |
4092 | 4079 | */ |
4093 | 4080 | public function isContentPage() { |
4094 | 4081 | return MWNamespace::isContent( $this->getNamespace() ); |
— | — | @@ -4096,8 +4083,9 @@ |
4097 | 4084 | /** |
4098 | 4085 | * Get all extant redirects to this Title |
4099 | 4086 | * |
4100 | | - * @param $ns Int|Null Single namespace to consider; NULL to consider all namespaces |
4101 | | - * @return Array of Title redirects to this title |
| 4087 | + * @param $ns \twotypes{\int,\null} Single namespace to consider; |
| 4088 | + * NULL to consider all namespaces |
| 4089 | + * @return \type{\arrayof{Title}} Redirects to this title |
4102 | 4090 | */ |
4103 | 4091 | public function getRedirectsHere( $ns = null ) { |
4104 | 4092 | $redirs = array(); |
— | — | @@ -4119,6 +4107,7 @@ |
4120 | 4108 | __METHOD__ |
4121 | 4109 | ); |
4122 | 4110 | |
| 4111 | + |
4123 | 4112 | foreach ( $res as $row ) { |
4124 | 4113 | $redirs[] = self::newFromRow( $row ); |
4125 | 4114 | } |
— | — | @@ -4128,7 +4117,7 @@ |
4129 | 4118 | /** |
4130 | 4119 | * Check if this Title is a valid redirect target |
4131 | 4120 | * |
4132 | | - * @return Bool |
| 4121 | + * @return \type{\bool} |
4133 | 4122 | */ |
4134 | 4123 | public function isValidRedirectTarget() { |
4135 | 4124 | global $wgInvalidRedirectTargets; |
— | — | @@ -4160,7 +4149,8 @@ |
4161 | 4150 | } |
4162 | 4151 | |
4163 | 4152 | /** |
4164 | | - * Whether the magic words __INDEX__ and __NOINDEX__ function for this page. |
| 4153 | + * Whether the magic words __INDEX__ and __NOINDEX__ function for |
| 4154 | + * this page. |
4165 | 4155 | * |
4166 | 4156 | * @return Boolean |
4167 | 4157 | */ |
— | — | @@ -4181,47 +4171,21 @@ |
4182 | 4172 | * @return array applicable restriction types |
4183 | 4173 | */ |
4184 | 4174 | public function getRestrictionTypes() { |
4185 | | - if ( $this->getNamespace() == NS_SPECIAL ) { |
4186 | | - return array(); |
4187 | | - } |
| 4175 | + global $wgRestrictionTypes; |
| 4176 | + $types = $this->exists() ? $wgRestrictionTypes : array( 'create' ); |
4188 | 4177 | |
4189 | | - $types = self::getFilteredRestrictionTypes( $this->exists() ); |
4190 | | - |
4191 | | - if ( $this->getNamespace() != NS_FILE ) { |
4192 | | - # Remove the upload restriction for non-file titles |
4193 | | - $types = array_diff( $types, array( 'upload' ) ); |
| 4178 | + if ( $this->getNamespace() == NS_FILE ) { |
| 4179 | + $types[] = 'upload'; |
4194 | 4180 | } |
4195 | 4181 | |
4196 | 4182 | wfRunHooks( 'TitleGetRestrictionTypes', array( $this, &$types ) ); |
4197 | 4183 | |
4198 | | - wfDebug( __METHOD__ . ': applicable restriction types for ' . |
4199 | | - $this->getPrefixedText() . ' are ' . implode( ',', $types ) . "\n" ); |
4200 | | - |
4201 | 4184 | return $types; |
4202 | 4185 | } |
4203 | | - /** |
4204 | | - * Get a filtered list of all restriction types supported by this wiki. |
4205 | | - * @param bool $exists True to get all restriction types that apply to |
4206 | | - * titles that do exist, False for all restriction types that apply to |
4207 | | - * titles that do not exist |
4208 | | - * @return array |
4209 | | - */ |
4210 | | - public static function getFilteredRestrictionTypes( $exists = true ) { |
4211 | | - global $wgRestrictionTypes; |
4212 | | - $types = $wgRestrictionTypes; |
4213 | | - if ( $exists ) { |
4214 | | - # Remove the create restriction for existing titles |
4215 | | - $types = array_diff( $types, array( 'create' ) ); |
4216 | | - } else { |
4217 | | - # Only the create and upload restrictions apply to non-existing titles |
4218 | | - $types = array_intersect( $types, array( 'create', 'upload' ) ); |
4219 | | - } |
4220 | | - return $types; |
4221 | | - } |
4222 | 4186 | |
4223 | 4187 | /** |
4224 | 4188 | * Returns the raw sort key to be used for categories, with the specified |
4225 | | - * prefix. This will be fed to Collation::getSortKey() to get a |
| 4189 | + * prefix. This will be fed to Language::convertToSortkey() to get a |
4226 | 4190 | * binary sortkey that can be used for actual sorting. |
4227 | 4191 | * |
4228 | 4192 | * @param $prefix string The prefix to be used, specified using |
— | — | @@ -4232,47 +4196,11 @@ |
4233 | 4197 | public function getCategorySortkey( $prefix = '' ) { |
4234 | 4198 | $unprefixed = $this->getText(); |
4235 | 4199 | if ( $prefix !== '' ) { |
4236 | | - # Separate with a line feed, so the unprefixed part is only used as |
4237 | | - # a tiebreaker when two pages have the exact same prefix. |
4238 | | - # In UCA, tab is the only character that can sort above LF |
4239 | | - # so we strip both of them from the original prefix. |
4240 | | - $prefix = strtr( $prefix, "\n\t", ' ' ); |
4241 | | - return "$prefix\n$unprefixed"; |
| 4200 | + # Separate with a null byte, so the unprefixed part is only used as |
| 4201 | + # a tiebreaker when two pages have the exact same prefix -- null |
| 4202 | + # sorts before everything else (hopefully). |
| 4203 | + return "$prefix\0$unprefixed"; |
4242 | 4204 | } |
4243 | 4205 | return $unprefixed; |
4244 | 4206 | } |
4245 | 4207 | } |
4246 | | - |
4247 | | -/** |
4248 | | - * A BadTitle is generated in MediaWiki::parseTitle() if the title is invalid; the |
4249 | | - * software uses this to display an error page. Internally it's basically a Title |
4250 | | - * for an empty special page |
4251 | | - */ |
4252 | | -class BadTitle extends Title { |
4253 | | - public function __construct(){ |
4254 | | - $this->mTextform = ''; |
4255 | | - $this->mUrlform = ''; |
4256 | | - $this->mDbkeyform = ''; |
4257 | | - $this->mNamespace = NS_SPECIAL; // Stops talk page link, etc, being shown |
4258 | | - } |
4259 | | - |
4260 | | - public function exists(){ |
4261 | | - return false; |
4262 | | - } |
4263 | | - |
4264 | | - public function getPrefixedText(){ |
4265 | | - return ''; |
4266 | | - } |
4267 | | - |
4268 | | - public function getText(){ |
4269 | | - return ''; |
4270 | | - } |
4271 | | - |
4272 | | - public function getPrefixedURL(){ |
4273 | | - return ''; |
4274 | | - } |
4275 | | - |
4276 | | - public function getPrefixedDBKey(){ |
4277 | | - return ''; |
4278 | | - } |
4279 | | -} |
Property changes on: branches/iwtransclusion/phase3v2/includes/Title.php |
___________________________________________________________________ |
Modified: svn:mergeinfo |
4280 | 4208 | Merged /branches/iwtransclusion/phase3/includes/Title.php:r70764 |
Index: branches/iwtransclusion/phase3v2/languages/messages/MessagesEn.php |
— | — | @@ -1399,6 +1399,9 @@ |
1400 | 1400 | 'templatesused' => '{{PLURAL:$1|Template|Templates}} used on this page:', |
1401 | 1401 | 'templatesusedpreview' => '{{PLURAL:$1|Template|Templates}} used in this preview:', |
1402 | 1402 | 'templatesusedsection' => '{{PLURAL:$1|Template|Templates}} used in this section:', |
| 1403 | +'distanttemplatesused' => 'Distant {{PLURAL:$1|template|templates}} used on this page:', |
| 1404 | +'distanttemplatesusedpreview' => 'Distant {{PLURAL:$1|template|templates}} used in this preview:', |
| 1405 | +'distanttemplatesusedsection' => 'Distant {{PLURAL:$1|template|templates}} used in this section:', |
1403 | 1406 | 'template-protected' => '(protected)', |
1404 | 1407 | 'template-semiprotected' => '(semi-protected)', |
1405 | 1408 | 'hiddencategories' => 'This page is a member of {{PLURAL:$1|1 hidden category|$1 hidden categories}}:', |
Property changes on: branches/iwtransclusion/phase3v2 |
___________________________________________________________________ |
Modified: svn:mergeinfo |
1406 | 1409 | Merged /branches/iwtransclusion/phase3:r70764 |