summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'MLEB/Translate/src/PageTranslation/MoveTranslatablePageSpecialPage.php')
-rw-r--r--MLEB/Translate/src/PageTranslation/MoveTranslatablePageSpecialPage.php402
1 files changed, 402 insertions, 0 deletions
diff --git a/MLEB/Translate/src/PageTranslation/MoveTranslatablePageSpecialPage.php b/MLEB/Translate/src/PageTranslation/MoveTranslatablePageSpecialPage.php
new file mode 100644
index 00000000..1197aa31
--- /dev/null
+++ b/MLEB/Translate/src/PageTranslation/MoveTranslatablePageSpecialPage.php
@@ -0,0 +1,402 @@
+<?php
+declare( strict_types = 1 );
+
+namespace MediaWiki\Extension\Translate\PageTranslation;
+
+use CommentStore;
+use ErrorPageError;
+use Html;
+use HTMLForm;
+use MediaWiki\Permissions\PermissionManager;
+use OutputPage;
+use PermissionsError;
+use ReadOnlyError;
+use SplObjectStorage;
+use ThrottledError;
+use Title;
+use TranslatablePage;
+use UnlistedSpecialPage;
+use Wikimedia\ObjectFactory;
+
+/**
+ * Replacement for Special:Movepage to allow renaming a translatable page and
+ * all pages associated with it.
+ *
+ * @author Niklas Laxström
+ * @license GPL-2.0-or-later
+ * @ingroup SpecialPage PageTranslation
+ */
+class MoveTranslatablePageSpecialPage extends UnlistedSpecialPage {
+ // Form parameters both as text and as titles
+ /** @var string */
+ private $oldText;
+ /** @var string */
+ private $reason;
+ /** @var bool */
+ private $moveTalkpages = true;
+ /** @var bool */
+ private $moveSubpages = true;
+ // Dependencies
+ /** @var ObjectFactory */
+ private $objectFactory;
+ /** @var TranslatablePageMover */
+ private $pageMover;
+ /** @var PermissionManager */
+ private $permissionManager;
+ private $movePageSpec;
+ // Other
+ /** @var Title */
+ private $oldTitle;
+
+ public function __construct(
+ ObjectFactory $objectFactory,
+ PermissionManager $permissionManager,
+ TranslatablePageMover $pageMover,
+ $movePageSpec
+ ) {
+ parent::__construct( 'Movepage' );
+ $this->objectFactory = $objectFactory;
+ $this->permissionManager = $permissionManager;
+ $this->pageMover = $pageMover;
+ // SpecialMovepage started using service injection in
+ // I6d4fe09891a126d803fee90bc3bb4959e8b29eb9
+ // Needed for MW < 1.36
+ if ( is_string( $movePageSpec ) ) {
+ $this->movePageSpec = [ 'class' => $movePageSpec ];
+ } else {
+ $this->movePageSpec = $movePageSpec;
+ }
+ }
+
+ public function doesWrites(): bool {
+ return true;
+ }
+
+ protected function getGroupName(): string {
+ return 'pagetools';
+ }
+
+ /** @inheritDoc */
+ public function execute( $par ) {
+ $request = $this->getRequest();
+ $user = $this->getUser();
+ $this->addHelpLink( 'Help:Extension:Translate/Move_translatable_page' );
+
+ $this->oldText = $request->getText( 'wpOldTitle', $request->getText( 'target', $par ) );
+ $newText = $request->getText( 'wpNewTitle' );
+
+ $this->oldTitle = Title::newFromText( $this->oldText );
+ $newTitle = Title::newFromText( $newText );
+ // Normalize input
+ if ( $this->oldTitle ) {
+ $this->oldText = $this->oldTitle->getPrefixedText();
+ }
+
+ $this->reason = $request->getText( 'reason' );
+
+ // This will throw exceptions if there is an error.
+ $this->doBasicChecks();
+
+ // Real stuff starts here
+ $page = TranslatablePage::newFromTitle( $this->oldTitle );
+ if ( $page->getMarkedTag() !== false ) {
+ $this->getOutput()->setPageTitle( $this->msg( 'pt-movepage-title', $this->oldText ) );
+
+ if ( !$user->isAllowed( 'pagetranslation' ) ) {
+ throw new PermissionsError( 'pagetranslation' );
+ }
+
+ // Is there really no better way to do this?
+ $subactionText = $request->getText( 'subaction' );
+ switch ( $subactionText ) {
+ case $this->msg( 'pt-movepage-action-check' )->text():
+ $subaction = 'check';
+ break;
+ case $this->msg( 'pt-movepage-action-perform' )->text():
+ $subaction = 'perform';
+ break;
+ case $this->msg( 'pt-movepage-action-other' )->text():
+ $subaction = '';
+ break;
+ default:
+ $subaction = '';
+ }
+
+ if ( $subaction === 'check' && $this->checkToken() && $request->wasPosted() ) {
+ try {
+ $pageCollection = $this->pageMover->getPageMoveCollection(
+ $this->oldTitle,
+ $newTitle,
+ $user,
+ $this->reason,
+ $this->moveSubpages,
+ $this->moveTalkpages
+ );
+ } catch ( ImpossiblePageMove $e ) {
+ $this->showErrors( $e->getBlockers() );
+ $this->showForm( [] );
+ return;
+ }
+
+ $this->showConfirmation( $pageCollection );
+ } elseif ( $subaction === 'perform' && $this->checkToken() && $request->wasPosted() ) {
+ $this->moveSubpages = $request->getBool( 'subpages' );
+ $this->moveTalkpages = $request->getBool( 'talkpages' );
+
+ $this->pageMover->moveAsynchronously(
+ $this->oldTitle,
+ $newTitle,
+ $this->moveSubpages,
+ $this->getUser(),
+ $this->msg( 'pt-movepage-logreason', $this->oldTitle )->inContentLanguage()->text(),
+ $this->moveTalkpages
+ );
+ $this->getOutput()->addWikiMsg( 'pt-movepage-started' );
+ } else {
+ $this->showForm( [] );
+ }
+ } else {
+ // Delegate... don't want to reimplement this
+ $sp = $this->objectFactory->createObject( $this->movePageSpec );
+ $sp->execute( $par );
+ }
+ }
+
+ /**
+ * Do the basic checks whether moving is possible and whether
+ * the input looks anywhere near sane.
+ * @throws PermissionsError|ErrorPageError|ReadOnlyError|ThrottledError
+ */
+ protected function doBasicChecks(): void {
+ $this->checkReadOnly();
+
+ if ( $this->oldTitle === null ) {
+ throw new ErrorPageError( 'notargettitle', 'notargettext' );
+ }
+
+ if ( !$this->oldTitle->exists() ) {
+ throw new ErrorPageError( 'nopagetitle', 'nopagetext' );
+ }
+
+ if ( $this->getUser()->pingLimiter( 'move' ) ) {
+ throw new ThrottledError;
+ }
+
+ // Check rights
+ $permErrors = $this->permissionManager
+ ->getPermissionErrors( 'move', $this->getUser(), $this->oldTitle );
+ if ( count( $permErrors ) ) {
+ throw new PermissionsError( 'move', $permErrors );
+ }
+ }
+
+ /**
+ * Checks token to protect against CSRF.
+ *
+ * FIXME: make this a form special page instead of manually checking stuff?
+ * @return bool
+ */
+ protected function checkToken(): bool {
+ return $this->getUser()->matchEditToken( $this->getRequest()->getVal( 'wpEditToken' ) );
+ }
+
+ /**
+ * Pretty-print the list of errors.
+ * @param SplObjectStorage $errors Array with message key and parameters
+ */
+ protected function showErrors( SplObjectStorage $errors ): void {
+ $out = $this->getOutput();
+
+ $out->addHTML( Html::openElement( 'div', [ 'class' => 'errorbox' ] ) );
+ $out->addWikiMsg(
+ 'pt-movepage-blockers',
+ $this->getLanguage()->formatNum( count( $errors ) )
+ );
+
+ // If there are many errors, for performance reasons we must parse them all at once
+ $s = '';
+ $context = 'pt-movepage-error-placeholder';
+ foreach ( $errors as $title ) {
+ $titleText = $title->getPrefixedText();
+ $s .= "'''$titleText'''\n\n";
+ $s .= $errors[ $title ]->getWikiText( false, $context );
+ }
+
+ $out->addWikiTextAsInterface( $s );
+ $out->addHTML( Html::closeElement( 'div' ) );
+ }
+
+ /**
+ * The query form.
+ * @param array $err Unused.
+ * @param bool $isPermError Unused.
+ */
+ public function showForm( $err, $isPermError = false ): void {
+ $this->getOutput()->addWikiMsg( 'pt-movepage-intro' );
+
+ HTMLForm::factory( 'ooui', $this->getCommonFormFields(), $this->getContext() )
+ ->setMethod( 'post' )
+ ->setAction( $this->getPageTitle( $this->oldText )->getLocalURL() )
+ ->setSubmitName( 'subaction' )
+ ->setSubmitTextMsg( 'pt-movepage-action-check' )
+ ->setWrapperLegendMsg( 'pt-movepage-legend' )
+ ->prepareForm()
+ ->displayForm( false );
+ }
+
+ /**
+ * The second form, which still allows changing some things.
+ * Lists all the action which would take place.
+ * @param PageMoveCollection $pageCollection
+ */
+ protected function showConfirmation( PageMoveCollection $pageCollection ): void {
+ $out = $this->getOutput();
+
+ $out->addWikiMsg( 'pt-movepage-intro' );
+
+ $count = 0;
+ $subpagesCount = 0;
+ $talkpagesCount = 0;
+
+ /** @var PageMoveOperation[][] */
+ $pagesToMove = [
+ 'pt-movepage-list-pages' => [ $pageCollection->getTranslatablePage() ],
+ 'pt-movepage-list-translation' => $pageCollection->getTranslationPagesPair(),
+ 'pt-movepage-list-section' => $pageCollection->getUnitPagesPair()
+ ];
+
+ $subpages = $pageCollection->getSubpagesPair();
+ if ( $subpages ) {
+ $pagesToMove[ 'pt-movepage-list-other'] = $subpages;
+ }
+
+ foreach ( $pagesToMove as $type => $pages ) {
+ $this->addSectionHeader( $out, $type, $pages );
+
+ if ( !$pages ) {
+ $out->addWikiMsg( 'pt-movepage-list-no-pages' );
+ continue;
+ }
+
+ $lines = [];
+
+ foreach ( $pages as $pagePairs ) {
+ $count++;
+
+ if ( $type === 'pt-movepage-list-other' ) {
+ $subpagesCount++;
+ }
+
+ $old = $pagePairs->getOldTitle();
+ $new = $pagePairs->getNewTitle();
+ $line = '* ' . $old->getPrefixedText() . ' → ' . $new->getPrefixedText();
+ if ( $pagePairs->hasTalkpage() ) {
+ $count++;
+ $talkpagesCount++;
+ $line .= ' ' . $this->msg( 'pt-movepage-talkpage-exists' )->text();
+ }
+
+ $lines[] = $line;
+ }
+
+ $out->addWikiTextAsInterface( implode( "\n", $lines ) );
+ }
+
+ $translatableSubpages = $pageCollection->getTranslatableSubpages();
+ $sectionType = 'pt-movepage-list-translatable';
+ $this->addSectionHeader( $out, $sectionType, $translatableSubpages );
+ if ( $translatableSubpages ) {
+ $lines = [];
+ $out->wrapWikiMsg( "'''$1'''", $this->msg( 'pt-movepage-list-translatable-note' ) );
+ foreach ( $translatableSubpages as $page ) {
+ $lines[] = '* ' . $page->getPrefixedText();
+ }
+ $out->addWikiTextAsInterface( implode( "\n", $lines ) );
+ }
+
+ $out->addWikiTextAsInterface( "----\n" );
+ $out->addWikiMsg(
+ 'pt-movepage-list-count',
+ $this->getLanguage()->formatNum( $count ),
+ $this->getLanguage()->formatNum( $subpagesCount ),
+ $this->getLanguage()->formatNum( $talkpagesCount )
+ );
+
+ $formDescriptor = array_merge(
+ $this->getCommonFormFields(),
+ [
+ 'subpages' => [
+ 'type' => 'check',
+ 'name' => 'subpages',
+ 'id' => 'mw-subpages',
+ 'label-message' => 'pt-movepage-subpages',
+ 'default' => $this->moveSubpages,
+ ],
+ 'talkpages' => [
+ 'type' => 'check',
+ 'name' => 'talkpages',
+ 'id' => 'mw-talkpages',
+ 'label-message' => 'pt-movepage-talkpages',
+ 'default' => $this->moveTalkpages
+ ]
+ ]
+ );
+
+ $htmlForm = HTMLForm::factory( 'ooui', $formDescriptor, $this->getContext() );
+ $htmlForm
+ ->addButton( [
+ 'name' => 'subaction',
+ 'value' => $this->msg( 'pt-movepage-action-other' )->text(),
+ ] )
+ ->setMethod( 'post' )
+ ->setAction( $this->getPageTitle( $this->oldText )->getLocalURL() )
+ ->setSubmitName( 'subaction' )
+ ->setSubmitTextMsg( 'pt-movepage-action-perform' )
+ ->setWrapperLegendMsg( 'pt-movepage-legend' )
+ ->prepareForm()
+ ->displayForm( false );
+ }
+
+ private function addSectionHeader( OutputPage $out, string $type, array $pages ): void {
+ $pageCount = count( $pages );
+ $out->wrapWikiMsg( '=== $1 ===', [ $type, $pageCount ] );
+
+ if ( !$pageCount ) {
+ $out->addWikiMsg( 'pt-movepage-list-no-pages' );
+ }
+ }
+
+ private function getCommonFormFields(): array {
+ return [
+ 'wpOldTitle' => [
+ 'type' => 'text',
+ 'name' => 'wpOldTitle',
+ 'label-message' => 'pt-movepage-current',
+ 'default' => $this->oldText,
+ 'readonly' => true,
+ ],
+ 'wpNewTitle' => [
+ 'type' => 'text',
+ 'name' => 'wpNewTitle',
+ 'label-message' => 'pt-movepage-new',
+ ],
+ 'reason' => [
+ 'type' => 'text',
+ 'name' => 'reason',
+ 'label-message' => 'pt-movepage-reason',
+ 'maxlength' => CommentStore::COMMENT_CHARACTER_LIMIT,
+ 'default' => $this->reason,
+ ],
+ 'subpages' => [
+ 'type' => 'hidden',
+ 'name' => 'subpages',
+ 'default' => $this->moveSubpages,
+ ],
+ 'talkpages' => [
+ 'type' => 'hidden',
+ 'name' => 'talkpages',
+ 'default' => $this->moveTalkpages
+ ]
+ ];
+ }
+}