use Exception;
use Hooks;
use MediaWiki\Linker\LinkTarget;
+use MediaWiki\Revision\RevisionLookup;
+use MediaWiki\Revision\RevisionRecord;
use MediaWiki\Session\SessionManager;
use MediaWiki\Special\SpecialPageFactory;
use MediaWiki\User\UserIdentity;
/** @var SpecialPageFactory */
private $specialPageFactory;
+ /** @var RevisionLookup */
+ private $revisionLookup;
+
/** @var string[] List of pages names anonymous user may see */
private $whitelistRead;
'editmyusercss',
'editmyuserjson',
'editmyuserjs',
+ 'editmyuserjsredirect',
'editmywatchlist',
'editsemiprotected',
'editsitecss',
/**
* @param SpecialPageFactory $specialPageFactory
+ * @param RevisionLookup $revisionLookup
* @param string[] $whitelistRead
* @param string[] $whitelistReadRegexp
* @param bool $emailConfirmToEdit
*/
public function __construct(
SpecialPageFactory $specialPageFactory,
+ RevisionLookup $revisionLookup,
$whitelistRead,
$whitelistReadRegexp,
$emailConfirmToEdit,
NamespaceInfo $nsInfo
) {
$this->specialPageFactory = $specialPageFactory;
+ $this->revisionLookup = $revisionLookup;
$this->whitelistRead = $whitelistRead;
$this->whitelistReadRegexp = $whitelistReadRegexp;
$this->emailConfirmToEdit = $emailConfirmToEdit;
&& !$user->isAllowedAny( 'editmyuserjs', 'edituserjs' )
) {
$errors[] = [ 'mycustomjsprotected', $action ];
+ } elseif (
+ $page->isUserJsConfigPage()
+ && !$user->isAllowedAny( 'edituserjs', 'editmyuserjsredirect' )
+ ) {
+ // T207750 - do not allow users to edit a redirect if they couldn't edit the target
+ $rev = $this->revisionLookup->getRevisionByTitle( $page );
+ $content = $rev ? $rev->getContent( 'main', RevisionRecord::RAW ) : null;
+ $target = $content ? $content->getUltimateRedirectTarget() : null;
+ if ( $target && (
+ !$target->inNamespace( NS_USER )
+ || !preg_match( '/^' . preg_quote( $user->getName(), '/' ) . '\//', $target->getText() )
+ ) ) {
+ $errors[] = [ 'mycustomjsredirectprotected', $action ];
+ }
}
} else {
// Users need editmyuser* to edit their own CSS/JSON/JS subpages, except for
* to make bot-flagged actions through certain special pages.
* Returns a "scope guard" variable; whenever that variable goes out of scope or is consumed
* via ScopedCallback::consume(), the temporary rights are revoked.
+ *
+ * @since 1.34
+ *
* @param UserIdentity $user
* @param string|string[] $rights
* @return ScopedCallback
*/
public function addTemporaryUserRights( UserIdentity $user, $rights ) {
- $nextKey = count( $this->temporaryUserRights[$user->getId()] ?? [] );
- $this->temporaryUserRights[$user->getId()][$nextKey] = (array)$rights;
- return new ScopedCallback( [ $this, 'revokeTemporaryUserRights' ], [ $user->getId(), $nextKey ] );
- }
-
- /**
- * Revoke rights added by addTemporaryUserRights().
- * @param int $userId
- * @param int $rightsGroupKey Key in self::$temporaryUserRights
- * @internal For use by addTemporaryUserRights() only.
- */
- public function revokeTemporaryUserRights( $userId, $rightsGroupKey ) {
- unset( $this->temporaryUserRights[$userId][$rightsGroupKey] );
+ $userId = $user->getId();
+ $nextKey = count( $this->temporaryUserRights[$userId] ?? [] );
+ $this->temporaryUserRights[$userId][$nextKey] = (array)$rights;
+ return new ScopedCallback( function () use ( $userId, $nextKey ) {
+ unset( $this->temporaryUserRights[$userId][$nextKey] );
+ } );
}
/**