jenkins-bot has submitted this change and it was merged. ( 
https://gerrit.wikimedia.org/r/337895 )

Change subject: Import dump: support importing a board that exist in the farm
......................................................................


Import dump: support importing a board that exist in the farm

When a board already exist, we generate new ids and
try to look users up with their global ids.

Bug: T154830
Change-Id: Ic493dc132dcf7dc0000d37115b22b0382346c815
---
M includes/Dump/Exporter.php
M includes/Dump/Importer.php
M includes/Dump/flow-1.0.xsd
M includes/WorkflowLoaderFactory.php
4 files changed, 186 insertions(+), 8 deletions(-)

Approvals:
  Mattflaschen: Looks good to me, approved
  jenkins-bot: Verified



diff --git a/includes/Dump/Exporter.php b/includes/Dump/Exporter.php
index 75fe0ee..948b07f 100644
--- a/includes/Dump/Exporter.php
+++ b/includes/Dump/Exporter.php
@@ -73,6 +73,13 @@
        protected $changeTypeProperty;
 
        /**
+        * To convert between local and global user ids
+        *
+        * @var \CentralIdLookup
+        */
+       protected $lookup;
+
+       /**
         * {@inheritDoc}
         */
        function __construct( $db, $history = WikiExporter::CURRENT,
@@ -84,6 +91,8 @@
 
                $this->changeTypeProperty = new ReflectionProperty( 
'Flow\Model\AbstractRevision', 'changeType' );
                $this->changeTypeProperty->setAccessible( true );
+
+               $this->lookup = \CentralIdLookup::factory( 'CentralAuth' );
        }
 
        public static function schemaVersion() {
@@ -388,6 +397,22 @@
                $format = $revision->getContentFormat();
                $attribs['flags'] = 'utf-8,' . $format;
 
+               if ( $this->lookup ) {
+                       $userIdFields = [ 'userid', 'treeoriguserid', 
'moduserid', 'edituserid' ];
+                       foreach ( $userIdFields as $userIdField ) {
+                               if ( isset( $attribs[ $userIdField ] ) ) {
+                                       $user = User::newFromId( $attribs[ 
$userIdField ] );
+                                       $globalUserId = 
$this->lookup->centralIdFromLocalUser(
+                                               $user,
+                                               \CentralIdLookup::AUDIENCE_RAW
+                                       );
+                                       if ( $globalUserId ) {
+                                               $attribs[ 'global' . 
$userIdField ] = $globalUserId;
+                                       }
+                               }
+                       }
+               }
+
                $output = Xml::element(
                        'revision',
                        $attribs,
diff --git a/includes/Dump/Importer.php b/includes/Dump/Importer.php
index 9341017..0c8607c 100644
--- a/includes/Dump/Importer.php
+++ b/includes/Dump/Importer.php
@@ -4,6 +4,9 @@
 
 use Flow\Container;
 use Flow\Data\ManagerGroup;
+use Flow\DbFactory;
+use Flow\Import\HistoricalUIDGenerator;
+use Flow\Import\ImportException;
 use Flow\Model\AbstractRevision;
 use Flow\Model\Header;
 use Flow\Model\PostRevision;
@@ -42,10 +45,30 @@
        protected $topicWorkflow;
 
        /**
+        * @var array Map of old to new IDs
+        */
+       protected $idMap = [];
+
+       /**
+        * To convert between global and local user ids
+        *
+        * @var \CentralIdLookup
+        */
+       protected $lookup;
+
+       /**
+        * Whether the current board is being imported in trans-wiki mode
+        *
+        * @var bool
+        */
+       protected $transWikiMode = false;
+
+       /**
         * @param WikiImporter $importer
         */
        public function __construct( WikiImporter $importer ) {
                $this->importer = $importer;
+               $this->lookup = \CentralIdLookup::factory( 'CentralAuth' );
        }
 
        /**
@@ -77,7 +100,12 @@
        }
 
        public function handleBoard() {
-               $id = $this->importer->nodeAttribute( 'id' );
+               $this->checkTransWikiMode(
+                       $this->importer->nodeAttribute( 'id' ),
+                       $this->importer->nodeAttribute( 'title' )
+               );
+
+               $id = $this->mapId( $this->importer->nodeAttribute( 'id' ) );
                $this->importer->debug( 'Enter board handler for ' . $id );
 
                $uuid = UUID::create( $id );
@@ -110,7 +138,7 @@
        }
 
        public function handleHeader() {
-               $id = $this->importer->nodeAttribute( 'id' );
+               $id = $this->mapId( $this->importer->nodeAttribute( 'id' ) );
                $this->importer->debug( 'Enter description handler for ' . $id 
);
 
                $metadata = array( 'workflow' => $this->boardWorkflow );
@@ -127,10 +155,10 @@
        }
 
        public function handleTopic() {
-               $id = $this->importer->nodeAttribute( 'id' );
+               $id = $this->mapId( $this->importer->nodeAttribute( 'id' ) );
                $this->importer->debug( 'Enter topic handler for ' . $id );
 
-               $uuid = UUID::create( $this->importer->nodeAttribute( 'id' ) );
+               $uuid = UUID::create( $id );
                $title = $this->boardWorkflow->getArticleTitle();
 
                $this->topicWorkflow = Workflow::fromStorageRow( array(
@@ -150,12 +178,31 @@
                        // @todo: topic-title & first-post? (used only in 
NotificationListener)
                );
 
+               // create page if it does not yet exist
+               /** @var OccupationController $occupationController */
+               $occupationController = Container::get( 'occupation_controller' 
);
+               $creationStatus = $occupationController->safeAllowCreation(
+                       $this->topicWorkflow->getArticleTitle(),
+                       $occupationController->getTalkpageManager()
+               );
+               if ( !$creationStatus->isOK() ) {
+                       throw new MWException( $creationStatus->getWikiText() );
+               }
+
+               $ensureStatus = $occupationController->ensureFlowRevision(
+                       new \Article( $this->topicWorkflow->getArticleTitle() ),
+                       $this->topicWorkflow
+               );
+               if ( !$ensureStatus->isOK() ) {
+                       throw new MWException( $ensureStatus->getWikiText() );
+               }
+
                $this->put( $this->topicWorkflow, $metadata );
                $this->put( $topicListEntry, $metadata );
        }
 
        public function handlePost() {
-               $id = $this->importer->nodeAttribute( 'id' );
+               $id = $this->mapId( $this->importer->nodeAttribute( 'id' ) );
                $this->importer->debug( 'Enter post handler for ' . $id );
 
                $metadata = array(
@@ -175,7 +222,7 @@
        }
 
        public function handleSummary() {
-               $id = $this->importer->nodeAttribute( 'id' );
+               $id = $this->mapId( $this->importer->nodeAttribute( 'id' ) );
                $this->importer->debug( 'Enter summary handler for ' . $id );
 
                $metadata = array( 'workflow' => $this->topicWorkflow );
@@ -214,7 +261,7 @@
         * @return AbstractRevision
         */
        protected function getRevision( $callback ) {
-               $id = $this->importer->nodeAttribute( 'id' );
+               $id = $this->mapId( $this->importer->nodeAttribute( 'id' ) );
                $this->importer->debug( 'Enter revision handler for ' . $id );
 
                // isEmptyElement will no longer be valid after we've started 
iterating
@@ -227,6 +274,34 @@
                do {
                        $attribs[$this->importer->getReader()->name] = 
$this->importer->getReader()->value;
                } while ( $this->importer->getReader()->moveToNextAttribute() );
+
+               $idFields = [ 'id', 'typeid', 'treedescendantid', 'treerevid', 
'parentid', 'treeparentid', 'lasteditid' ];
+               foreach ( $idFields as $idField ) {
+                       if ( isset( $attribs[ $idField ] ) ) {
+                               $attribs[ $idField ] = $this->mapId( $attribs[ 
$idField ] );
+                       }
+               }
+
+               if ( $this->transWikiMode && $this->lookup ) {
+                       $userFields = [ 'user', 'treeoriguser', 'moduser', 
'edituser' ];
+                       foreach ( $userFields as $userField ) {
+                               $globalUserIdField = 'global' . $userField . 
'id';
+                               if ( isset( $attribs[ $globalUserIdField ] ) ) {
+                                       $localUser = 
$this->lookup->localUserFromCentralId(
+                                               $attribs[ $globalUserIdField ],
+                                               \CentralIdLookup::AUDIENCE_RAW
+                                       );
+                                       if ( !$localUser ) {
+                                               $localUser = 
$this->createLocalUser( $attribs[ $globalUserIdField ] );
+                                       }
+                                       $attribs[ $userField . 'id' ] = 
$localUser->getId();
+                                       $attribs[ $userField . 'wiki' ] = 
wfWikiID();
+                               } else if ( isset( $attribs[ $userField . 'ip' 
] ) ) {
+                                       // make anons local users
+                                       $attribs[ $userField . 'wiki' ] = 
wfWikiID();
+                               }
+                       }
+               }
 
                // now that we've moved inside the node (to fetch attributes),
                // nodeContents() is no longer reliable: is uses isEmptyContent 
(which
@@ -248,4 +323,78 @@
 
                return call_user_func( $callback, $attribs );
        }
+
+       /**
+        * When in trans-wiki mode, return a new id based on the same timestamp
+        *
+        * @param string $id
+        * @return string
+        */
+       private function mapId( $id ) {
+               if ( !$this->transWikiMode ) {
+                       return $id;
+               }
+
+               if ( !isset( $this->idMap[ $id ] ) ) {
+                       $this->idMap[ $id ] = UUID::create( 
HistoricalUIDGenerator::historicalTimestampedUID88(
+                               UUID::hex2timestamp( UUID::create( $id 
)->getHex() )
+                       ) )->getAlphadecimal();
+               }
+               return $this->idMap[ $id ];
+       }
+
+       /**
+        * Check if a board already exist and should be imported in trans-wiki 
mode
+        *
+        * @param string $boardWorkflowId
+        * @param string $title
+        */
+       private function checkTransWikiMode( $boardWorkflowId, $title ) {
+               /** @var DbFactory $dbFactory */
+               $dbFactory = Container::get( 'db.factory' );
+               $workflowExist = !!$dbFactory->getDB( DB_MASTER )->selectField(
+                       'flow_workflow',
+                       'workflow_id',
+                       [ 'workflow_id' => UUID::create( $boardWorkflowId 
)->getBinary() ],
+                       __METHOD__
+               );
+
+               if ( $workflowExist ) {
+                       $this->importer->debug( "$title will be imported in 
trans-wiki mode" );
+               }
+               $this->transWikiMode = $workflowExist;
+       }
+
+       /**
+        * Create a local user corresponding to a global id
+        *
+        * @param integer $globalUserId
+        * @return \User Local user
+        * @throws ImportException
+        */
+       private function createLocalUser( $globalUserId ) {
+               if ( !( $this->lookup instanceof \CentralAuthIdLookup ) ) {
+                       throw new ImportException( 'Creating local users is not 
supported with central id provider: ' . get_class( $this->lookup ) );
+               }
+
+               $globalUser = \CentralAuthUser::newFromId( $globalUserId );
+               $localUser = \User::newFromName( $globalUser->getName() );
+
+               if ( $localUser->getId() ) {
+                       throw new ImportException( "User 
'{$localUser->getName()}' already exists" );
+               }
+
+               $status = \CentralAuthUtils::autoCreateUser( $localUser );
+               if ( !$status->isGood() ) {
+                       throw new ImportException(
+                               "autoCreateUser failed for 
{$localUser->getName()}: " . print_r( $status->getErrors(), true )
+                       );
+               }
+
+               # Update user count
+               $ssUpdate = \SiteStatsUpdate::factory( [ 'users' => 1 ] );
+               $ssUpdate->doUpdate();
+
+               return $localUser;
+       }
 }
diff --git a/includes/Dump/flow-1.0.xsd b/includes/Dump/flow-1.0.xsd
index a670f85..275c5a6 100644
--- a/includes/Dump/flow-1.0.xsd
+++ b/includes/Dump/flow-1.0.xsd
@@ -12,6 +12,7 @@
                        <extension base="string">
                                <attribute type="string" name="id" 
use="required"/>
                                <attribute type="nonNegativeInteger" 
name="userid" use="required"/>
+                               <attribute type="nonNegativeInteger" 
name="globaluserid" use="optional"/>
                                <attribute type="string" name="userip" 
use="optional"/>
                                <attribute type="string" name="userwiki" 
use="required"/>
                                <attribute type="string" name="parentid" 
use="optional"/>
@@ -20,6 +21,7 @@
                                <attribute type="string" name="typeid" 
use="required"/>
                                <attribute type="string" name="flags" 
use="required"/>
                                <attribute type="string" name="modstate" 
use="required"/>
+                               <attribute type="nonNegativeInteger" 
name="globalmoduserid" use="optional"/>
                                <attribute type="string" name="moduserid" 
use="optional"/>
                                <attribute type="string" name="moduserip" 
use="optional"/>
                                <attribute type="string" name="moduserwiki" 
use="optional"/>
@@ -27,6 +29,7 @@
                                <attribute type="string" name="modreason" 
use="optional"/>
                                <attribute type="string" name="lasteditid" 
use="optional"/>
                                <attribute type="string" name="edituserid" 
use="optional"/>
+                               <attribute type="nonNegativeInteger" 
name="globaledituserid" use="optional"/>
                                <attribute type="string" name="edituserip" 
use="optional"/>
                                <attribute type="string" name="edituserwiki" 
use="optional"/>
                                <attribute type="nonNegativeInteger" 
name="contentlength" use="required"/>
@@ -42,6 +45,7 @@
                                <attribute type="string" 
name="treedescendantid" use="required"/>
                                <attribute type="string" name="treerevid" 
use="required"/>
                                <attribute type="nonNegativeInteger" 
name="treeoriguserid" use="required"/>
+                               <attribute type="nonNegativeInteger" 
name="globaltreeoriguserid" use="optional"/>
                                <attribute type="string" name="treeoriguserip" 
use="optional"/>
                                <attribute type="string" 
name="treeoriguserwiki" use="required"/>
                        </extension>
diff --git a/includes/WorkflowLoaderFactory.php 
b/includes/WorkflowLoaderFactory.php
index fd52375..5350e2f 100644
--- a/includes/WorkflowLoaderFactory.php
+++ b/includes/WorkflowLoaderFactory.php
@@ -110,7 +110,7 @@
         * @param Title|false $title
         * @param UUID $workflowId
         * @return Workflow
-        * @throws InvalidInputException
+        * @throws InvalidDataException
         * @throws UnknownWorkflowIdException
         */
        public function loadWorkflowById( /* Title or false */ $title, 
$workflowId ) {

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

Gerrit-MessageType: merged
Gerrit-Change-Id: Ic493dc132dcf7dc0000d37115b22b0382346c815
Gerrit-PatchSet: 5
Gerrit-Project: mediawiki/extensions/Flow
Gerrit-Branch: master
Gerrit-Owner: Sbisson <sbis...@wikimedia.org>
Gerrit-Reviewer: Catrope <r...@wikimedia.org>
Gerrit-Reviewer: Mattflaschen <mflasc...@wikimedia.org>
Gerrit-Reviewer: Nikerabbit <niklas.laxst...@gmail.com>
Gerrit-Reviewer: Sbisson <sbis...@wikimedia.org>
Gerrit-Reviewer: jenkins-bot <>

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

Reply via email to