X-Git-Url: https://git.heureux-cyclage.org/?a=blobdiff_plain;f=includes%2FTitle.php;h=a8cfad8748966a14f0ae1b7178cb594ea5324391;hb=58e1e472ba48c3271902ccbcadb7c142220c4d58;hp=f16f0c5311149ec7af8f8370a69495e9c694b5c0;hpb=8e8b4167ce356e3d516f4607c1c72a909bb3c24c;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/Title.php b/includes/Title.php index f16f0c5311..a8cfad8748 100644 --- a/includes/Title.php +++ b/includes/Title.php @@ -22,6 +22,7 @@ * @file */ +use Wikimedia\Rdbms\Database; use Wikimedia\Rdbms\IDatabase; use MediaWiki\Linker\LinkTarget; use MediaWiki\Interwiki\InterwikiLookup; @@ -1683,6 +1684,33 @@ class Title implements LinkTarget { return $url; } + /** + * Get a url appropriate for making redirects based on an untrusted url arg + * + * This is basically the same as getFullUrl(), but in the case of external + * interwikis, we send the user to a landing page, to prevent possible + * phishing attacks and the like. + * + * @note Uses current protocol by default, since technically relative urls + * aren't allowed in redirects per HTTP spec, so this is not suitable for + * places where the url gets cached, as might pollute between + * https and non-https users. + * @see self::getLocalURL for the arguments. + * @param array|string $query + * @param string $proto Protocol type to use in URL + * @return String. A url suitable to use in an HTTP location header. + */ + public function getFullUrlForRedirect( $query = '', $proto = PROTO_CURRENT ) { + $target = $this; + if ( $this->isExternal() ) { + $target = SpecialPage::getTitleFor( + 'GoToInterwiki', + $this->getPrefixedDBKey() + ); + } + return $target->getFullUrl( $query, false, $proto ); + } + /** * Get a URL with no fragment or server name (relative URL) from a Title object. * If this page is generated with action=render, however, @@ -2095,7 +2123,7 @@ class Title implements LinkTarget { private function checkSpecialsAndNSPermissions( $action, $user, $errors, $rigor, $short ) { # Only 'createaccount' can be performed on special pages, # which don't actually exist in the DB. - if ( NS_SPECIAL == $this->mNamespace && $action !== 'createaccount' ) { + if ( $this->isSpecialPage() && $action !== 'createaccount' ) { $errors[] = [ 'ns-specialprotected' ]; } @@ -2124,8 +2152,7 @@ class Title implements LinkTarget { private function checkCSSandJSPermissions( $action, $user, $errors, $rigor, $short ) { # Protect css/js subpages of user pages # XXX: this might be better using restrictions - # XXX: right 'editusercssjs' is deprecated, for backward compatibility only - if ( $action != 'patrol' && !$user->isAllowed( 'editusercssjs' ) ) { + if ( $action != 'patrol' ) { if ( preg_match( '/^' . preg_quote( $user->getName(), '/' ) . '\//', $this->mTextform ) ) { if ( $this->isCssSubpage() && !$user->isAllowedAny( 'editmyusercss', 'editusercss' ) ) { $errors[] = [ 'mycustomcssprotected', $action ]; @@ -2289,6 +2316,17 @@ class Title implements LinkTarget { ) { $errors[] = [ 'delete-toobig', $wgLang->formatNum( $wgDeleteRevisionsLimit ) ]; } + } elseif ( $action === 'undelete' ) { + if ( count( $this->getUserPermissionsErrorsInternal( 'edit', $user, $rigor, true ) ) ) { + // Undeleting implies editing + $errors[] = [ 'undelete-cantedit' ]; + } + if ( !$this->exists() + && count( $this->getUserPermissionsErrorsInternal( 'create', $user, $rigor, true ) ) + ) { + // Undeleting where nothing currently exists implies creating + $errors[] = [ 'undelete-cantcreate' ]; + } } return $errors; } @@ -3388,7 +3426,7 @@ class Title implements LinkTarget { $this->mTextform = strtr( $this->mDbkeyform, '_', ' ' ); # We already know that some pages won't be in the database! - if ( $this->isExternal() || $this->mNamespace == NS_SPECIAL ) { + if ( $this->isExternal() || $this->isSpecialPage() ) { $this->mArticleID = 0; } @@ -3956,29 +3994,52 @@ class Title implements LinkTarget { } /** - * Get the revision ID of the previous revision - * + * Get next/previous revision ID relative to another revision ID * @param int $revId Revision ID. Get the revision that was before this one. * @param int $flags Title::GAID_FOR_UPDATE - * @return int|bool Old revision ID, or false if none exists - */ - public function getPreviousRevisionID( $revId, $flags = 0 ) { - /* This function and getNextRevisionID have bad performance when - used on a page with many revisions on mysql. An explicit extended - primary key may help in some cases, if the PRIMARY KEY is banned: - T159319 */ + * @param string $dir 'next' or 'prev' + * @return int|bool New revision ID, or false if none exists + */ + private function getRelativeRevisionID( $revId, $flags, $dir ) { + $revId = (int)$revId; + if ( $dir === 'next' ) { + $op = '>'; + $sort = 'ASC'; + } elseif ( $dir === 'prev' ) { + $op = '<'; + $sort = 'DESC'; + } else { + throw new InvalidArgumentException( '$dir must be "next" or "prev"' ); + } + if ( $flags & self::GAID_FOR_UPDATE ) { $db = wfGetDB( DB_MASTER ); } else { $db = wfGetDB( DB_REPLICA, 'contributions' ); } + + // Intentionally not caring if the specified revision belongs to this + // page. We only care about the timestamp. + $ts = $db->selectField( 'revision', 'rev_timestamp', [ 'rev_id' => $revId ], __METHOD__ ); + if ( $ts === false ) { + $ts = $db->selectField( 'archive', 'ar_timestamp', [ 'ar_rev_id' => $revId ], __METHOD__ ); + if ( $ts === false ) { + // Or should this throw an InvalidArgumentException or something? + return false; + } + } + $ts = $db->addQuotes( $ts ); + $revId = $db->selectField( 'revision', 'rev_id', [ 'rev_page' => $this->getArticleID( $flags ), - 'rev_id < ' . intval( $revId ) + "rev_timestamp $op $ts OR (rev_timestamp = $ts AND rev_id $op $revId)" ], __METHOD__, - [ 'ORDER BY' => 'rev_id DESC', 'IGNORE INDEX' => 'PRIMARY' ] + [ + 'ORDER BY' => "rev_timestamp $sort, rev_id $sort", + 'IGNORE INDEX' => 'rev_timestamp', // Probably needed for T159319 + ] ); if ( $revId === false ) { @@ -3988,6 +4049,17 @@ class Title implements LinkTarget { } } + /** + * Get the revision ID of the previous revision + * + * @param int $revId Revision ID. Get the revision that was before this one. + * @param int $flags Title::GAID_FOR_UPDATE + * @return int|bool Old revision ID, or false if none exists + */ + public function getPreviousRevisionID( $revId, $flags = 0 ) { + return $this->getRelativeRevisionID( $revId, $flags, 'prev' ); + } + /** * Get the revision ID of the next revision * @@ -3996,25 +4068,7 @@ class Title implements LinkTarget { * @return int|bool Next revision ID, or false if none exists */ public function getNextRevisionID( $revId, $flags = 0 ) { - if ( $flags & self::GAID_FOR_UPDATE ) { - $db = wfGetDB( DB_MASTER ); - } else { - $db = wfGetDB( DB_REPLICA, 'contributions' ); - } - $revId = $db->selectField( 'revision', 'rev_id', - [ - 'rev_page' => $this->getArticleID( $flags ), - 'rev_id > ' . intval( $revId ) - ], - __METHOD__, - [ 'ORDER BY' => 'rev_id', 'IGNORE INDEX' => 'PRIMARY' ] - ); - - if ( $revId === false ) { - return false; - } else { - return intval( $revId ); - } + return $this->getRelativeRevisionID( $revId, $flags, 'next' ); } /** @@ -4031,8 +4085,8 @@ class Title implements LinkTarget { [ 'rev_page' => $pageId ], __METHOD__, [ - 'ORDER BY' => 'rev_timestamp ASC', - 'IGNORE INDEX' => 'rev_timestamp' + 'ORDER BY' => 'rev_timestamp ASC, rev_id ASC', + 'IGNORE INDEX' => 'rev_timestamp', // See T159319 ] ); if ( $row ) { @@ -4613,7 +4667,7 @@ class Title implements LinkTarget { } /** - * Whether the magic words __INDEX__ and __NOINDEX__ function for this page. + * Whether the magic words __INDEX__ and __NOINDEX__ function for this page. * * @return bool */