X-Git-Url: https://git.heureux-cyclage.org/?a=blobdiff_plain;f=includes%2FTitle.php;h=d1d1e782317f2650509eb03a59678de3d80492da;hb=5bd492d51c9d3ec0c60c8c10c39b0441accd3baf;hp=3ea7a8614ce5fa4825cdf9e3169b658648360a1b;hpb=6c4360c9510b4890c548006e610a8c281dde5120;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/Title.php b/includes/Title.php index 3ea7a8614c..d1d1e78231 100644 --- a/includes/Title.php +++ b/includes/Title.php @@ -959,7 +959,7 @@ class Title { * for anonymous users). * @return String the URL */ - public function getLinkUrl( $query = array(), $variant = false ) { + public function getLinkURL( $query = array(), $variant = false ) { wfProfileIn( __METHOD__ ); if ( $this->isExternal() ) { $ret = $this->getFullURL( $query ); @@ -996,6 +996,7 @@ class Title { /** * HTML-escaped version of getCanonicalURL() + * @since 1.18 */ public function escapeCanonicalURL( $query = '', $variant = false ) { return htmlspecialchars( $this->getCanonicalURL( $query, $variant ) ); @@ -1032,6 +1033,7 @@ class Title { * @param $query string An optional query string * @param $variant string Language variant of URL (for sr, zh, ...) * @return string The URL + * @since 1.18 */ public function getCanonicalURL( $query = '', $variant = false ) { $url = wfExpandUrl( $this->getLocalURL( $query, $variant ) . $this->getFragmentForURL(), PROTO_CANONICAL ); @@ -1131,6 +1133,26 @@ class Title { return false; } + /** + * Determines if $user is unable to edit this page because it has been protected + * by $wgNamespaceProtection. + * + * @param $user User object to check permissions + * @return Bool + */ + public function isNamespaceProtected( User $user ) { + global $wgNamespaceProtection; + + if ( isset( $wgNamespaceProtection[$this->mNamespace] ) ) { + foreach ( (array)$wgNamespaceProtection[$this->mNamespace] as $right ) { + if ( $right != '' && !$user->isAllowed( $right ) ) { + return true; + } + } + } + return false; + } + /** * Is this a conversion table for the LanguageConverter? * @@ -1166,6 +1188,17 @@ class Title { return $this->mWatched; } + /** + * Can $wgUser read this page? + * + * @deprecated in 1.19; use userCan(), quickUserCan() or getUserPermissionsErrors() instead + * @return Bool + * @todo fold these checks into userCan() + */ + public function userCanRead() { + return $this->userCan( 'read' ); + } + /** * Can $wgUser perform $action on this page? * This skips potentially expensive cascading permission checks @@ -1177,42 +1210,27 @@ class Title { * May provide false positives, but should never provide a false negative. * * @param $action String action that permission needs to be checked for + * @param $user User to check (since 1.19) * @return Bool */ - public function quickUserCan( $action ) { - return $this->userCan( $action, false ); - } - - /** - * Determines if $user is unable to edit this page because it has been protected - * by $wgNamespaceProtection. - * - * @param $user User object to check permissions - * @return Bool - */ - public function isNamespaceProtected( User $user ) { - global $wgNamespaceProtection; - - if ( isset( $wgNamespaceProtection[$this->mNamespace] ) ) { - foreach ( (array)$wgNamespaceProtection[$this->mNamespace] as $right ) { - if ( $right != '' && !$user->isAllowed( $right ) ) { - return true; - } - } - } - return false; + public function quickUserCan( $action, $user = null ) { + return $this->userCan( $action, $user, false ); } /** * Can $wgUser perform $action on this page? * * @param $action String action that permission needs to be checked for + * @param $user User to check (since 1.19) * @param $doExpensiveQueries Bool Set this to false to avoid doing unnecessary queries. * @return Bool */ - public function userCan( $action, $doExpensiveQueries = true ) { - global $wgUser; - return ( $this->getUserPermissionsErrorsInternal( $action, $wgUser, $doExpensiveQueries, true ) === array() ); + public function userCan( $action, $user = null, $doExpensiveQueries = true ) { + if ( !$user instanceof User ) { + global $wgUser; + $user = $wgUser; + } + return !count( $this->getUserPermissionsErrorsInternal( $action, $user, $doExpensiveQueries, true ) ); } /** @@ -1298,24 +1316,7 @@ class Title { $errors[] = array( 'cant-move-to-user-page' ); } } elseif ( !$user->isAllowed( $action, $ns ) ) { - // We avoid expensive display logic for quickUserCan's and such - $groups = false; - if ( !$short ) { - $groups = array_map( array( 'User', 'makeGroupLinkWiki' ), - User::getGroupsWithPermission( $action, $ns ) ); - } - - if ( $groups ) { - global $wgLang; - $return = array( - 'badaccess-groups', - $wgLang->commaList( $groups ), - count( $groups ) - ); - } else { - $return = array( 'badaccess-group0' ); - } - $errors[] = $return; + $errors[] = $this->missingPermissionError( $action, $short ); } return $errors; @@ -1390,7 +1391,7 @@ class Title { private function checkSpecialsAndNSPermissions( $action, $user, $errors, $doExpensiveQueries, $short ) { # Only 'createaccount' and 'execute' can be performed on # special pages, which don't actually exist in the DB. - $specialOKActions = array( 'createaccount', 'execute' ); + $specialOKActions = array( 'createaccount', 'execute', 'read' ); if ( NS_SPECIAL == $this->mNamespace && !in_array( $action, $specialOKActions ) ) { $errors[] = array( 'ns-specialprotected' ); } @@ -1454,13 +1455,10 @@ class Title { } if ( $right != '' && !$user->isAllowed( $right, $this->mNamespace ) ) { // Users with 'editprotected' permission can edit protected pages - if ( $action == 'edit' && $user->isAllowed( 'editprotected', $this->mNamespace ) ) { - // Users with 'editprotected' permission cannot edit protected pages - // with cascading option turned on. - if ( $this->mCascadeRestriction ) { - $errors[] = array( 'protectedpagetext', $right ); - } - } else { + // without cascading option turned on. + if ( $action != 'edit' || !$user->isAllowed( 'editprotected', $this->mNamespace ) + || $this->mCascadeRestriction ) + { $errors[] = array( 'protectedpagetext', $right ); } } @@ -1568,21 +1566,19 @@ class Title { * @return Array list of errors */ private function checkUserBlock( $action, $user, $errors, $doExpensiveQueries, $short ) { - if( !$doExpensiveQueries ) { + // Account creation blocks handled at userlogin. + // Unblocking handled in SpecialUnblock + if( !$doExpensiveQueries || in_array( $action, array( 'createaccount', 'unblock' ) ) ) { return $errors; } global $wgContLang, $wgLang, $wgEmailConfirmToEdit; - if ( $wgEmailConfirmToEdit && !$user->isEmailConfirmed() && $action != 'createaccount' ) { + if ( $wgEmailConfirmToEdit && !$user->isEmailConfirmed() ) { $errors[] = array( 'confirmedittext' ); } - if ( in_array( $action, array( 'read', 'createaccount', 'unblock' ) ) ){ - // Edit blocks should not affect reading. - // Account creation blocks handled at userlogin. - // Unblocking handled in SpecialUnblock - } elseif( ( $action == 'edit' || $action == 'create' ) && !$user->isBlockedFrom( $this ) ){ + if ( ( $action == 'edit' || $action == 'create' ) && !$user->isBlockedFrom( $this ) ) { // Don't block the user from editing their own talk page unless they've been // explicitly blocked from that too. } elseif( $user->isBlocked() && $user->mBlock->prevents( $action ) !== false ) { @@ -1623,6 +1619,128 @@ class Title { return $errors; } + /** + * Check that the user is allowed to read this page. + * + * @param $action String the action to check + * @param $user User to check + * @param $errors Array list of current errors + * @param $doExpensiveQueries Boolean whether or not to perform expensive queries + * @param $short Boolean short circuit on first error + * + * @return Array list of errors + */ + private function checkReadPermissions( $action, $user, $errors, $doExpensiveQueries, $short ) { + static $useShortcut = null; + + # Initialize the $useShortcut boolean, to determine if we can skip quite a bit of code below + if ( is_null( $useShortcut ) ) { + global $wgGroupPermissions, $wgRevokePermissions; + $useShortcut = true; + if ( empty( $wgGroupPermissions['*']['read'] ) ) { + # Not a public wiki, so no shortcut + $useShortcut = false; + } elseif ( !empty( $wgRevokePermissions ) ) { + /** + * Iterate through each group with permissions being revoked (key not included since we don't care + * what the group name is), then check if the read permission is being revoked. If it is, then + * we don't use the shortcut below since the user might not be able to read, even though anon + * reading is allowed. + */ + foreach ( $wgRevokePermissions as $perms ) { + if ( !empty( $perms['read'] ) ) { + # We might be removing the read right from the user, so no shortcut + $useShortcut = false; + break; + } + } + } + } + + # Shortcut for public wikis, allows skipping quite a bit of code + if ( $useShortcut ) { + return $errors; + } + + # If the user is allowed to read pages, he is allowed to read all pages + if ( $user->isAllowed( 'read', $this->mNamespace ) ) { + return $errors; + } + + # Always grant access to the login page. + # Even anons need to be able to log in. + if ( $this->isSpecial( 'Userlogin' ) || $this->isSpecial( 'ChangePassword' ) ) { + return $errors; + } + + # Time to check the whitelist + global $wgWhitelistRead; + + # Only to these checks is there's something to check against + if ( is_array( $wgWhitelistRead ) && count( $wgWhitelistRead ) ) { + # Check for explicit whitelisting + $name = $this->getPrefixedText(); + $dbName = $this->getPrefixedDBKey(); + + // Check with and without underscores + if ( in_array( $name, $wgWhitelistRead, true ) || in_array( $dbName, $wgWhitelistRead, true ) ) { + return $errors; + } + + # Old settings might have the title prefixed with + # a colon for main-namespace pages + if ( $this->getNamespace() == NS_MAIN ) { + if ( in_array( ':' . $name, $wgWhitelistRead ) ) { + return $errors; + } + } + + # If it's a special page, ditch the subpage bit and check again + if ( $this->isSpecialPage() ) { + $name = $this->getDBkey(); + list( $name, /* $subpage */ ) = SpecialPageFactory::resolveAlias( $name ); + if ( $name !== false ) { + $pure = SpecialPage::getTitleFor( $name )->getPrefixedText(); + if ( in_array( $pure, $wgWhitelistRead, true ) ) { + return $errors; + } + } + } + } + + $errors[] = $this->missingPermissionError( $action, $short ); + return $errors; + } + + /** + * Get a description array when the user doesn't have the right to perform + * $action (i.e. when User::isAllowed() returns false) + * + * @param $action String the action to check + * @param $short Boolean short circuit on first error + * @return Array list of errors + */ + private function missingPermissionError( $action, $short ) { + // We avoid expensive display logic for quickUserCan's and such + if ( $short ) { + return array( 'badaccess-group0' ); + } + + $groups = array_map( array( 'User', 'makeGroupLinkWiki' ), + User::getGroupsWithPermission( $action, $this->mNamespace ) ); + + if ( count( $groups ) ) { + global $wgLang; + return array( + 'badaccess-groups', + $wgLang->commaList( $groups ), + count( $groups ) + ); + } else { + return array( 'badaccess-group0' ); + } + } + /** * Can $user perform $action on this page? This is an internal function, * which checks ONLY that previously checked by userCan (i.e. it leaves out @@ -1637,20 +1755,28 @@ class Title { protected function getUserPermissionsErrorsInternal( $action, $user, $doExpensiveQueries = true, $short = false ) { wfProfileIn( __METHOD__ ); - $errors = array(); - $checks = array( - 'checkQuickPermissions', - 'checkPermissionHooks', - 'checkSpecialsAndNSPermissions', - 'checkCSSandJSPermissions', - 'checkPageRestrictions', - 'checkCascadingSourcesRestrictions', - 'checkActionPermissions', - 'checkUserBlock' - ); + # Read has special handling + if ( $action == 'read' ) { + $checks = array( + 'checkPermissionHooks', + 'checkReadPermissions', + ); + } else { + $checks = array( + 'checkQuickPermissions', + 'checkPermissionHooks', + 'checkSpecialsAndNSPermissions', + 'checkCSSandJSPermissions', + 'checkPageRestrictions', + 'checkCascadingSourcesRestrictions', + 'checkActionPermissions', + 'checkUserBlock' + ); + } + $errors = array(); while( count( $checks ) > 0 && - !( $short && count( $errors ) > 0 ) ) { + !( $short && count( $errors ) > 0 ) ) { $method = array_shift( $checks ); $errors = $this->$method( $action, $user, $errors, $doExpensiveQueries, $short ); } @@ -1785,102 +1911,6 @@ class Title { return $result; } - /** - * Can $wgUser read this page? - * - * @return Bool - * @todo fold these checks into userCan() - */ - public function userCanRead() { - global $wgUser, $wgGroupPermissions; - - static $useShortcut = null; - - # Initialize the $useShortcut boolean, to determine if we can skip quite a bit of code below - if ( is_null( $useShortcut ) ) { - global $wgRevokePermissions; - $useShortcut = true; - if ( empty( $wgGroupPermissions['*']['read'] ) ) { - # Not a public wiki, so no shortcut - $useShortcut = false; - } elseif ( !empty( $wgRevokePermissions ) ) { - /** - * Iterate through each group with permissions being revoked (key not included since we don't care - * what the group name is), then check if the read permission is being revoked. If it is, then - * we don't use the shortcut below since the user might not be able to read, even though anon - * reading is allowed. - */ - foreach ( $wgRevokePermissions as $perms ) { - if ( !empty( $perms['read'] ) ) { - # We might be removing the read right from the user, so no shortcut - $useShortcut = false; - break; - } - } - } - } - - $result = null; - wfRunHooks( 'userCan', array( &$this, &$wgUser, 'read', &$result ) ); - if ( $result !== null ) { - return $result; - } - - # Shortcut for public wikis, allows skipping quite a bit of code - if ( $useShortcut ) { - return true; - } - - if ( $wgUser->isAllowed( 'read' ) ) { - return true; - } else { - global $wgWhitelistRead; - - # Always grant access to the login page. - # Even anons need to be able to log in. - if ( $this->isSpecial( 'Userlogin' ) || $this->isSpecial( 'ChangePassword' ) ) { - return true; - } - - # Bail out if there isn't whitelist - if ( !is_array( $wgWhitelistRead ) ) { - return false; - } - - # Check for explicit whitelisting - $name = $this->getPrefixedText(); - $dbName = $this->getPrefixedDBKey(); - // Check with and without underscores - if ( in_array( $name, $wgWhitelistRead, true ) || in_array( $dbName, $wgWhitelistRead, true ) ) - return true; - - # Old settings might have the title prefixed with - # a colon for main-namespace pages - if ( $this->getNamespace() == NS_MAIN ) { - if ( in_array( ':' . $name, $wgWhitelistRead ) ) { - return true; - } - } - - # If it's a special page, ditch the subpage bit and check again - if ( $this->isSpecialPage() ) { - $name = $this->getDBkey(); - list( $name, /* $subpage */ ) = SpecialPageFactory::resolveAlias( $name ); - if ( $name === false ) { - # Invalid special page, but we show standard login required message - return false; - } - - $pure = SpecialPage::getTitleFor( $name )->getPrefixedText(); - if ( in_array( $pure, $wgWhitelistRead, true ) ) { - return true; - } - } - - } - return false; - } - /** * Is this the mainpage? * @note Title::newFromText seams to be sufficiently optimized by the title @@ -1915,6 +1945,57 @@ class Title { : false; } + /** + * Returns true if the title is inside the specified namespace. + * + * Please make use of this instead of comparing to getNamespace() + * This function is much more resistant to changes we may make + * to namespaces than code that makes direct comparisons. + * @param $ns The namespace + * @return bool + * @since 1.19 + */ + public function inNamespace( $ns ) { + return MWNamespace::equals( $this->getNamespace(), $ns ); + } + + /** + * Returns true if the title is inside one of the specified namespaces. + * + * @param ...$namespaces The namespaces to check for + * @return bool + * @since 1.19 + */ + public function inNamespaces( /* ... */ ) { + $namespaces = func_get_args(); + if ( count( $namespaces ) > 0 && is_array( $namespaces[0] ) ) { + $namespaces = $namespaces[0]; + } + + foreach ( $namespaces as $ns ) { + if ( $this->inNamespace( $ns ) ) { + return true; + } + } + + return false; + } + + /** + * Returns true if the title has the same subject namespace as the + * namespace specified. + * For example this method will take NS_USER and return true if namespace + * is either NS_USER or NS_USER_TALK since both of them have NS_USER + * as their subject namespace. + * + * This is MUCH simpler than individually testing for equivilance + * against both NS_USER and NS_USER_TALK, and is also forward compatible. + * @since 1.19 + */ + public function hasSubjectNamespace( $ns ) { + return MWNamespace::subjectEquals( $this->getNamespace(), $ns ); + } + /** * Does this have subpages? (Warning, usually requires an extra DB query.) * @@ -3353,10 +3434,7 @@ class Title { $dbw->delete( 'page', array( 'page_id' => $newid ), __METHOD__ ); if ( !$dbw->cascadingDeletes() ) { $dbw->delete( 'revision', array( 'rev_page' => $newid ), __METHOD__ ); - global $wgUseTrackbacks; - if ( $wgUseTrackbacks ) { - $dbw->delete( 'trackbacks', array( 'tb_page' => $newid ), __METHOD__ ); - } + $dbw->delete( 'pagelinks', array( 'pl_from' => $newid ), __METHOD__ ); $dbw->delete( 'imagelinks', array( 'il_from' => $newid ), __METHOD__ ); $dbw->delete( 'categorylinks', array( 'cl_from' => $newid ), __METHOD__ ); @@ -4065,46 +4143,6 @@ class Title { return $this->mNotificationTimestamp[$uid]; } - /** - * Get the trackback URL for this page - * - * @return String Trackback URL - */ - public function trackbackURL() { - global $wgScriptPath, $wgServer, $wgScriptExtension; - - return "$wgServer$wgScriptPath/trackback$wgScriptExtension?article=" - . htmlspecialchars( urlencode( $this->getPrefixedDBkey() ) ); - } - - /** - * Get the trackback RDF for this page - * - * @return String Trackback RDF - */ - public function trackbackRDF() { - $url = htmlspecialchars( $this->getFullURL() ); - $title = htmlspecialchars( $this->getText() ); - $tburl = $this->trackbackURL(); - - // Autodiscovery RDF is placed in comments so HTML validator - // won't barf. This is a rather icky workaround, but seems - // frequently used by this kind of RDF thingy. - // - // Spec: http://www.sixapart.com/pronet/docs/trackback_spec - return ""; - } - /** * Generate strings used for xml 'id' names in monobook tabs * @@ -4248,7 +4286,7 @@ class Title { /** * Get a backlink cache object * - * @return object BacklinkCache + * @return BacklinkCache */ function getBacklinkCache() { if ( is_null( $this->mBacklinkCache ) ) {