Matthias Mullie has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/296511

Change subject: Script to restore LQT topics to their pre-import state
......................................................................

Script to restore LQT topics to their pre-import state

The script doesn't entirely "revert" the LQT conversion.
The history of the conversion still exists & it will just create
a new revision with the pre-conversion content (similar to other
"revert" operations in MW).
We also don't really want to "revert" anyway: we want to keep Flow
around because there may already be newer content there.

LQT->Flow conversion does a few things to LQT content:
1/ it archives the LQT board to a subpage
2/ it disables LQT: {{#useliquidthreads:0}}
3/ it edits LQT content to redirect to the Flow post

2 & 3 are relatively straightforward: we want to undo those content
changes (in the way described earlier)

1 is slightly more annoying: that archived LQT board may (and has,
in some cases) already have been moved elsewhere (e.g. to localize
the archive name). Such moves make it impossible to find the LQT
board if we want to run the import again, which intentionally
ignores manual page moves.
To work around this, we'll figure out the original & current LQT
board name and make sure it's moved to its current location AGAIN,
but this time by "Flow talk page manager", and in 1 go.
This will allow the Flow importer to find it and associate it with
the Flow board that has already been created in an earlier
conversion attempt.

Bug: T119509
Change-Id: I1bf5fb41e33c7ffb195675aa0453008566ef9b67
(cherry picked from commit 5aa5f79d1cc42174cd51455ebad9565b8860e934)
---
A maintenance/FlowRestoreLQT.php
1 file changed, 300 insertions(+), 0 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/Flow 
refs/changes/11/296511/1

diff --git a/maintenance/FlowRestoreLQT.php b/maintenance/FlowRestoreLQT.php
new file mode 100644
index 0000000..5b97b60
--- /dev/null
+++ b/maintenance/FlowRestoreLQT.php
@@ -0,0 +1,300 @@
+<?php
+
+use Flow\Container;
+use Flow\DbFactory;
+use Flow\Import\ArchiveNameHelper;
+
+require_once ( getenv( 'MW_INSTALL_PATH' ) !== false
+       ? getenv( 'MW_INSTALL_PATH' ) . '/maintenance/Maintenance.php'
+       : dirname( __FILE__ ) . '/../../../maintenance/Maintenance.php' );
+
+class FlowRestoreLQT extends Maintenance {
+       /**
+        * @var User
+        */
+       protected $talkpageManagerUser;
+
+       /**
+        * @var DbFactory
+        */
+       protected $dbFactory;
+
+       /**
+        * @var bool
+        */
+       protected $dryRun = false;
+
+       /**
+        * @var bool
+        */
+       protected $overwrite = false;
+
+       public function __construct() {
+               parent::__construct();
+
+               $this->mDescription = 'Restores LQT boards after a Flow 
conversion (revert LQT conversion edits & move LQT boards back)';
+
+               $this->addOption( 'dryrun', 'Simulate script run, without 
making actual changes' );
+               $this->addOption( 'overwrite-flow', 'Removes the Flow board 
entirely, restoring LQT to its original location' );
+
+               $this->setBatchSize( 1 );
+       }
+
+       public function execute() {
+               $this->talkpageManagerUser = 
FlowHooks::getOccupationController()->getTalkpageManager();
+               $this->dbFactory = Container::get( 'db.factory' );
+               $this->dryRun = $this->getOption( 'dryrun', false );
+               $this->overwrite = $this->getOption( 'overwrite-flow', false );
+
+               $this->output( "Restoring posts...\n" );
+               $this->restoreLQTThreads();
+
+               $this->output( "Restoring boards...\n" );
+               $this->restoreLQTBoards();
+       }
+
+       /**
+        * During an import, LQT boards are moved out of the way (archived) to 
make
+        * place for the Flow board.
+        * And after completing an import, LQT boards are disabled with
+        * {{#useliquidthreads:0}}
+        * That's all perfectly fine assuming the conversion goes well, but 
we'll
+        * want to go back to the original content with this script...
+        */
+       protected function restoreLQTBoards() {
+               $dbr = $this->dbFactory->getWikiDB( DB_SLAVE );
+               $startId = 0;
+
+               do {
+                       // fetch all LQT boards that have been moved out of the 
way,
+                       // with their original title & their current title
+                       $rows = $dbr->select(
+                               array( 'logging', 'page', 'revision' ),
+                               // log_namespace & log_title will be the 
original location
+                               // page_namespace & page_title will be the 
current location
+                               // rev_id is the first Flow talk page manager 
edit id
+                               // log_id is the log entry for when importer 
moved LQT page
+                               array( 'log_namespace', 'log_title', 'page_id', 
'page_namespace', 'page_title', 'rev_id' => 'MIN(rev_id)', 'log_id' ),
+                               array(
+                                       'log_user' => 
$this->talkpageManagerUser->getId(),
+                                       'log_type' => 'move',
+                                       'page_content_model' => 'wikitext',
+                                       'page_id > ' . $dbr->addQuotes( 
$startId ),
+                               ),
+                               __METHOD__,
+                               array(
+                                       'GROUP BY' => 'rev_page',
+                                       'LIMIT' => $this->mBatchSize,
+                                       'ORDER BY' => 'log_id ASC',
+                               ),
+                               array(
+                                       'page' => array(
+                                               'INNER JOIN',
+                                               array( 'page_id = log_page' ),
+                                       ),
+                                       'revision' => array(
+                                               'INNER JOIN',
+                                               array( 'rev_page = log_page', 
'rev_user = log_user' ),
+                                       ),
+                               )
+                       );
+
+                       foreach ( $rows as $row ) {
+                               $from = Title::newFromText( $row->page_title, 
$row->page_namespace );
+                               $to = Title::newFromText( $row->log_title, 
$row->log_namespace );
+
+                               // undo {{#useliquidthreads:0}}
+                               $this->restorePageRevision( $row->page_id, 
$row->rev_id );
+                               // undo page move to archive location
+                               $this->restoreLQTPage( $from, $to, $row->log_id 
);
+
+                               $startId = $row->page_id;
+                       }
+
+                       wfWaitForSlaves();
+               } while ( $rows->numRows() >= $this->mBatchSize );
+       }
+
+       /**
+        * After converting an LQT thread to Flow, it's content is altered to
+        * redirect to the new Flow topic.
+        * This finds all last original revisions & restores them.
+        */
+       protected function restoreLQTThreads() {
+               $dbr = $this->dbFactory->getWikiDB( DB_SLAVE );
+               $startId = 0;
+
+               do {
+                       // for every LQT post, find the first edit by Flow talk 
page manager
+                       // (to redirect to the new Flow copy)
+                       $rows = $dbr->select(
+                               array( 'page', 'revision' ),
+                               array( 'rev_page', 'rev_id' => ' MIN(rev_id)' ),
+                               array(
+                                       'page_namespace' => array( 
NS_LQT_THREAD, NS_LQT_SUMMARY ),
+                                       'rev_user' => 
$this->talkpageManagerUser->getId(),
+                                       'page_id > ' . $dbr->addQuotes( 
$startId ),
+                               ),
+                               __METHOD__,
+                               array(
+                                       'GROUP BY' => 'page_id',
+                                       'LIMIT' => $this->mBatchSize,
+                                       'ORDER BY' => 'page_id ASC',
+                               ),
+                               array(
+                                       'revision' => array(
+                                               'INNER JOIN',
+                                               array( 'rev_page = page_id' ),
+                                       ),
+                               )
+                       );
+
+                       foreach ( $rows as $row ) {
+                               // undo #REDIRECT edit
+                               $this->restorePageRevision( $row->rev_page, 
$row->rev_id );
+                               $startId = $row->rev_page;
+                       }
+
+                       wfWaitForSlaves();
+               } while ( $rows->numRows() >= $this->mBatchSize );
+       }
+
+       /**
+        * @param Title $lqt Title of the LQT board
+        * @param Title $flow Title of the Flow board
+        * @param int $logId Log id for when LQT board was moved by import
+        * @return Status
+        * @throws MWException
+        */
+       protected function restoreLQTPage( Title $lqt, Title $flow, $logId ) {
+               if ( $lqt->equals( $flow ) ) {
+                       // is at correct location already (probably a rerun of 
this script)
+                       return Status::newGood();
+               }
+
+               $archiveNameHelper = new ArchiveNameHelper();
+
+               if ( !$flow->exists() ) {
+                       $this->movePage( $lqt, $flow, '/* Restore LQT board to 
original location */' );
+               } else {
+                       /*
+                        * The importer will query the log table to find the 
LQT archive
+                        * location. It will assume that Flow talk page manager 
moved the
+                        * LQT board to its archive location, and will not 
recognize the
+                        * board if it's been moved by someone else.
+                        * Because of that feature (yes, that is intended), we 
need to make
+                        * sure that - in order to enable LQT imports to be 
picked up again
+                        * after this - the move from <original page> to 
<archive page>
+                        * happens in 1 go, by Flow talk page manager.
+                        */
+                       if ( !$this->overwrite ) {
+                               /*
+                                * Before we go moving pages around like crazy, 
let's see if we
+                                * actually need to. While it's certainly 
possible that the LQT
+                                * pages have been moved since the import and 
we need to fix
+                                * them, it's very likely that they haven't. In 
that case, we
+                                * won't have to do the complex moves.
+                                */
+                               $dbr = $this->dbFactory->getDB( DB_SLAVE );
+                               $count = $dbr->selectRowCount(
+                                       array( 'logging' ),
+                                       '*',
+                                       array(
+                                               'log_page' => 
$lqt->getArticleID(),
+                                               'log_type' => 'move',
+                                               'log_id > ' . $dbr->addQuotes( 
$logId ),
+                                       ),
+                                       __METHOD__
+                               );
+
+                               if ( $count > 0 ) {
+                                       $this->output( "Ensuring LQT board 
'{$lqt->getPrefixedDBkey()}' is recognized as archive of Flow board 
'{$flow->getPrefixedDBkey()}'.\n" );
+
+                                       // 1: move Flow board out of the way so 
we can restore LQT to
+                                       // its original location
+                                       $archive = 
$archiveNameHelper->decideArchiveTitle( $flow, array( '%s/Flow Archive %d' ) );
+                                       $this->movePage( $flow, $archive, '/* 
Make place to restore LQT board */' );
+
+                                       // 2: move LQT board to the original 
location
+                                       $this->movePage( $lqt, $flow, '/* 
Restore LQT board to original location */' );
+
+                                       // 3: move LQT board back to archive 
location
+                                       $this->movePage( $flow, $lqt, '/* 
Restore LQT board to archive location */' );
+
+                                       // 4: move Flow board back to the 
original location
+                                       $this->movePage( $archive, $flow, '/* 
Restore Flow board to correct location */' );
+                               }
+                       } else {
+                               $this->output( "Deleting 
'{$flow->getPrefixedDBkey()}' & moving '{$lqt->getPrefixedDBkey()}' there.\n" );
+
+                               if ( !$this->dryRun ) {
+                                       $page = WikiPage::factory( $flow );
+                                       $page->doDeleteArticleReal( '/* Make 
place to restore LQT board */', false, null, null, $error, 
$this->talkpageManagerUser );
+                               }
+
+                               $this->movePage( $lqt, $flow, '/* Restore LQT 
board to original location */' );
+                       }
+               }
+       }
+
+       /**
+        * @param Title $from
+        * @param Title $to
+        * @param string $reason
+        * @return Status
+        */
+       protected function movePage( Title $from, Title $to, $reason ) {
+               $this->output( "        Moving '{$from->getPrefixedDBkey()}' to 
'{$to->getPrefixedDBkey()}'.\n" );
+
+               $movePage = new MovePage( $from, $to );
+               $status = $movePage->isValidMove();
+               if ( !$status->isGood() ) {
+                       return $status;
+               }
+
+               if ( $this->dryRun ) {
+                       return Status::newGood();
+               }
+
+               return $movePage->move( $this->talkpageManagerUser, $reason, 
false );
+       }
+
+       /**
+        * @param int $pageId
+        * @param int $nextRevisionId Revision of the first *bad* revision
+        * @return Status
+        * @throws MWException
+        */
+       protected function restorePageRevision( $pageId, $nextRevisionId ) {
+               global $wgLang;
+
+               $page = WikiPage::newFromID( $pageId );
+               $revisionId = $page->getTitle()->getPreviousRevisionID( 
$nextRevisionId );
+               $revision = Revision::newFromPageId( $pageId, $revisionId );
+
+               if ( $page->getContent()->equals( $revision->getContent() ) ) {
+                       // has correct content already (probably a rerun of 
this script)
+                       return Status::newGood();
+               }
+
+               $content = $revision->getContent()->serialize();
+               $content = $wgLang->truncate( $content, 150 );
+               $content = str_replace( "\n", '\n', $content );
+               $this->output( "Restoring revision {$revisionId} for LQT page 
{$pageId}: {$content}\n" );
+
+               if ( $this->dryRun ) {
+                       return Status::newGood();
+               } else {
+                       return $page->doEditContent(
+                               $revision->getContent( Revision::RAW ),
+                               '/* Restore LQT topic content */',
+                               EDIT_UPDATE | EDIT_MINOR | EDIT_FORCE_BOT,
+                               $revision->getId(),
+                               $this->talkpageManagerUser
+                       );
+               }
+       }
+}
+
+$maintClass = 'FlowRestoreLQT';
+require_once ( RUN_MAINTENANCE_IF_MAIN );

-- 
To view, visit https://gerrit.wikimedia.org/r/296511
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I1bf5fb41e33c7ffb195675aa0453008566ef9b67
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/Flow
Gerrit-Branch: wmf/1.28.0-wmf.7
Gerrit-Owner: Matthias Mullie <mmul...@wikimedia.org>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to