Bsitu has uploaded a new change for review.

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

Change subject: [WIP]Flow revision multi-state support
......................................................................

[WIP]Flow revision multi-state support

DON'T REVIEW, this is a very initial draft

The ultimate goal is to support revision delete. Move delete/suppress
action from board action menu to history page. hide/close actions are kept
in flow history.  delete/suppress will be removed from flow history and
logged in general logging table

This is step one of the followings:

* Add multi-state support to collect data to new table

* Maintenance script to populate data to new table for old records

* Have flow code reference the new table for revision state

* Drop old data columns

Change-Id: I269b5cd143ea956b670d46483486b3cecaeebcd7
---
M Flow.php
M Hooks.php
A db_patches/patch-flow_revision_state.sql
M flow.sql
M includes/Data/RevisionStorage.php
M includes/Model/AbstractRevision.php
A includes/Model/RevisionState.php
7 files changed, 361 insertions(+), 2 deletions(-)


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

diff --git a/Flow.php b/Flow.php
index effc0a0..d3e4ca4 100755
--- a/Flow.php
+++ b/Flow.php
@@ -97,6 +97,7 @@
 $wgAutoloadClasses['Flow\Model\PostSummary'] = $dir . 
'includes/Model/PostSummary.php';
 $wgAutoloadClasses['Flow\Model\TopicListEntry'] = $dir . 
'includes/Model/TopicListEntry.php';
 $wgAutoloadClasses['Flow\Model\Workflow'] = $dir . 
'includes/Model/Workflow.php';
+$wgAutoloadClasses['Flow\Model\RevisionState'] = $dir . 
'includes/Model/RevisionState.php';
 $wgAutoloadClasses['Flow\Model\UUID'] = "$dir/includes/Model/UUID.php";
 $wgAutoloadClasses['Flow\Collection\AbstractCollection'] = $dir . 
'includes/Collection/AbstractCollection.php';
 $wgAutoloadClasses['Flow\Collection\CollectionCache'] = $dir . 
'includes/Collection/CollectionCache.php';
diff --git a/Hooks.php b/Hooks.php
index 75d5eea..953aaf5 100644
--- a/Hooks.php
+++ b/Hooks.php
@@ -126,6 +126,7 @@
                $updater->modifyExtensionField( 'flow_revision', 'rev_user_ip', 
"$dir/db_patches/patch-revision_user_ip.sql" );
                $updater->addExtensionField( 'flow_revision', 'rev_type_id', 
"$dir/db_patches/patch-rev_type_id.sql" );
                $updater->addExtensionTable( 'flow_ext_ref', 
"$dir/db_patches/patch-add-linkstables.sql" );
+               $updater->addExtensionTable( 'flow_revision_state', 
"$dir/db_patches/patch-flow_revision_state.sql" );
 
                require_once 
__DIR__.'/maintenance/FlowInsertDefaultDefinitions.php';
                $updater->addPostDatabaseUpdateMaintenance( 
'FlowInsertDefaultDefinitions' );
diff --git a/db_patches/patch-flow_revision_state.sql 
b/db_patches/patch-flow_revision_state.sql
new file mode 100644
index 0000000..1520a1b
--- /dev/null
+++ b/db_patches/patch-flow_revision_state.sql
@@ -0,0 +1,10 @@
+CREATE TABLE /*_*/flow_revision_state (
+       frs_rev_id binary(11) not null,
+       frs_state varchar(32) binary not null,
+       frs_user_id bigint unsigned not null default 0,
+       frs_user_ip varbinary(39) default null,
+       frs_user_wiki varchar(32) binary not null default '',
+       frs_comment varchar(255) binary
+) /*$wgDBTableOptions*/;
+
+CREATE UNIQUE INDEX /*i*/flow_revision_state_rev_id_state ON 
/*_*/flow_revision_state (frs_rev_id,frs_state);
diff --git a/flow.sql b/flow.sql
index 67fdbd0..0165461 100644
--- a/flow.sql
+++ b/flow.sql
@@ -136,6 +136,17 @@
 CREATE INDEX /*i*/flow_revision_user ON
        /*_*/flow_revision (rev_user_id, rev_user_ip, rev_user_wiki);
 
+CREATE TABLE /*_*/flow_revision_state (
+       frs_rev_id binary(11) not null,
+       frs_state varchar(32) binary not null,
+       frs_user_id bigint unsigned not null default 0,
+       frs_user_ip varbinary(39) default null,
+       frs_user_wiki varchar(32) binary not null default '',
+       frs_comment varchar(255) binary
+) /*$wgDBTableOptions*/;
+
+CREATE UNIQUE INDEX /*i*/flow_revision_state_rev_id_state ON 
/*_*/flow_revision_state (frs_rev_id,frs_state);
+
 -- Closure table implementation of tree storage in sql
 -- We may be able to go simpler than this
 CREATE TABLE /*_*/flow_tree_node (
diff --git a/includes/Data/RevisionStorage.php 
b/includes/Data/RevisionStorage.php
index 2d6b7c6..2cff084 100644
--- a/includes/Data/RevisionStorage.php
+++ b/includes/Data/RevisionStorage.php
@@ -92,6 +92,96 @@
        abstract protected function getRevType();
 
        /**
+        * Insert revision state
+        * @param array
+        * @return boolean
+        */
+       protected function insertRevState( array $rows ) {
+               // Revision state
+               $stateRows = array();
+               foreach ( $rows as $i => $row ) {
+                       $stateRows += array_values( unserialize( 
$this->splitUpdate( $row, 'frs' ) ) );
+               }
+               if ( $stateRows ) {
+                       $dbw = $this->dbFactory->getDB( DB_MASTER );
+                       $res = $dbw->insert(
+                               'flow_revision_state',
+                               $this->preprocessSqlArray( $stateRows ),
+                               __METHOD__
+                       );
+                       if ( !$res ) {
+                               return false;
+                       }
+               }
+               return true;
+       }
+
+       /**
+        * Update revision state
+        * @param array
+        * @param array
+        * @return boolean
+        */
+       protected function updateRevState( array $old, array $new ) {
+               $insert = $delete = array();
+               $oldFrs = unserialize( $old['frs'] );
+               $newFrs = unserialize( $new['frs'] );
+               foreach ( $oldFrs as $state => $row ) {
+                       if ( !isset( $newFrs[$state] ) ) {
+                               $delete[] = $row;
+                       }
+               }
+               foreach ( $newFrs as $state => $row ) {
+                       if ( !isset( $oldFrs[$state] ) ) {
+                               $insert[] = $row;
+                       }
+               }
+
+               foreach ( $delete as $row ) {
+                       $res = $this->dbFactory->getDB( DB_MASTER )->delete(
+                               'flow_revision_state',
+                               $this->preprocessSqlArray( array( 'frs_rev_id' 
=> $row['frs_rev_id'], 'frs_rev_state' => $row['frs_rev_state'] ) ),
+                               __METHOD__
+                       );
+                       if ( !$res ) {
+                               return false;
+                       }
+               }
+
+               if ( $insert ) {
+                       $res = $this->dbFactory->getDB( DB_MASTER )->insert(
+                               'flow_revision_state',
+                               $this->preprocessSqlArray( $insert ),
+                               __METHOD__
+                       );
+                       if ( !$res ) {
+                               return false;
+                       }
+               }
+
+               return true;
+       }
+
+       /**
+        * Delete revision state
+        * @param array
+        * @return boolean
+        */
+       protected function removeRevState( array $row ) {
+               foreach ( unserialize( $row['frs'] ) as $stateRow ) {
+                       $res = $this->dbFactory->getDB( DB_MASTER )->delete(
+                               'flow_revision_state',
+                               $this->preprocessSqlArray( array( 'frs_rev_id' 
=> $row['frs_rev_id'], 'frs_rev_state' => $row['frs_rev_state'] ) ),
+                               __METHOD__
+                       );
+                       if ( !$res ) {
+                               return false;
+                       }
+               }
+               return true;
+       }
+
+       /**
         * @param DbFactory $dbFactory
         * @param array|false List of externel store servers available for 
insert
         *  or false to disable. See $wgFlowExternalStore.
@@ -149,12 +239,63 @@
                return $query;
        }
 
+       /**
+        * Find the state for revisions
+        */
+       protected function findMultiRevState( array $revIds ) {
+               $result = array();
+
+               if ( !$revIds ) {
+                       return $result;
+               }
+
+               $dbr = $this->dbFactory->getDB( DB_MASTER );
+               $res = $dbr->select(
+                       array( 'flow_revision_state' ),
+                       '*',
+                       $this->preprocessSqlArray( array( 'frs_rev_id' => 
$revIds ) ),
+                       __METHOD__
+               );
+               if ( !$res ) {
+                       // TODO: dont fail, but dont end up caching bad result 
either
+                       throw new DataModelException( 'query failure', 
'process-data' );
+               }
+
+               foreach ( $res as $row ) {
+                       $row = UUID::convertUUIDs( (array)$row, 'alphadecimal' 
);
+                       $result[$row['frs_rev_id']][$row['frs_rev_state']] = 
$row;
+               }
+
+               return $result;
+       }
+
        public function findMulti( array $queries, array $options = array() ) {
                if ( count( $queries ) < 3 ) {
                        $res = $this->fallbackFindMulti( $queries, $options );
                } else {
                        $res = $this->findMultiInternal( $queries, $options );
                }
+               $revIds = array();
+               foreach ( $res as $revs ) {
+                       foreach ( $revs as $rev ) {
+                               $revIds[] = $rev['rev_id'];
+                       }
+               }
+               $revState = $this->findMultiRevState( $revIds );
+               foreach ( $res as $key => $revs ) {
+                       foreach ( $revs as $revId => $data ) {
+                               if ( isset( $revState[$data['rev_id']] ) ) {
+                                       $frs = $revState[$data['rev_id']];
+                               } else {
+                                       $frs = array();
+                               }
+                               // Crap, need to serialize because FeatureIndex 
will throw
+                               // an error if the data is of composite data 
type. Maybe just
+                               // use a serialized field instead of a 
relational table
+                               $res[$key][$revId]['frs'] = serialize( $frs );
+                       }
+               }
+
                // Fetches content for all revisions flagged 'external'
                return self::mergeExternalContent( $res );
        }
@@ -377,6 +518,10 @@
                        return false;
                }
 
+               if ( !$this->insertRevState( $rows ) ) {
+                       return false;
+               }
+
                return $this->insertRelated( $rows );
        }
 
@@ -444,6 +589,11 @@
                                return false;
                        }
                }
+
+               if ( !$this->updateRevState( $old, $new ) ) {
+                       return false;
+               }
+
                return $this->updateRelated( $changeSet, $old );
        }
 
@@ -462,6 +612,10 @@
                if ( !$res ) {
                        return false;
                }
+
+               if ( !$this->removeRevState( $row ) ) {
+                       return false;
+               }
                return $this->removeRelated( $row );
        }
 
diff --git a/includes/Model/AbstractRevision.php 
b/includes/Model/AbstractRevision.php
index 825f5da..810533c 100644
--- a/includes/Model/AbstractRevision.php
+++ b/includes/Model/AbstractRevision.php
@@ -28,7 +28,7 @@
                self::MODERATED_HIDDEN,
                self::MODERATED_DELETED,
                self::MODERATED_SUPPRESSED,
-               self::MODERATED_CLOSED,
+               self::MODERATED_CLOSED
        );
 
        /**
@@ -155,11 +155,15 @@
         */
        protected $lastEditUserIp;
 
-
        /**
         * @var string|null The wiki of the user that most recently changed the 
content
         */
        protected $lastEditUserWiki;
+
+       /**
+        * @var RevisionState[]
+        */
+       protected $revisionState = array();
 
        /**
         * @param string[] $row
@@ -220,6 +224,13 @@
                $obj->lastEditUserIp = isset( $row['rev_edit_user_ip'] ) ? 
$row['rev_edit_user_ip'] : null;
                $obj->lastEditUserWiki = isset( $row['rev_edit_user_wiki'] ) ? 
$row['rev_edit_user_wiki'] : null;
 
+               // Revision state
+               if ( isset( $row['frs'] ) ) {
+                       foreach ( unserialize( $row['frs'] ) as $state ) {
+                               $this->revisionState[$state->getState()] = 
RevisionState::fromStorageRow( $state );
+                       }
+               }
+
                return $obj;
        }
 
@@ -228,6 +239,18 @@
         * @return string[]
         */
        static public function toStorageRow( $obj ) {
+               $state = array();
+               foreach ( $obj->revisionState as $state ) {
+                       $state[$state->getState()] = serialize( array(
+                               // Use getter method because PHP doesn't have 
package visibility
+                               'frs_rev_id' => 
$state->getRevId()->getAlphadecimal(),
+                               'frs_state' => $state->getState(),
+                               'frs_user_id' => $state->getUserId(),
+                               'frs_user_ip' => $state->getUserIp(),
+                               'frs_user_wiki' => $state->getUserWiki(),
+                               'frs_comment' => $state->getComment()
+                       ) );
+               }
                return array(
                        'rev_id' => $obj->revId->getAlphadecimal(),
                        'rev_user_id' => $obj->userId,
@@ -253,6 +276,7 @@
                        'rev_edit_user_id' => $obj->lastEditUserId,
                        'rev_edit_user_ip' => $obj->lastEditUserIp,
                        'rev_edit_user_wiki' => $obj->lastEditUserWiki,
+                       'frs' => $state
                );
        }
 
@@ -556,6 +580,13 @@
        }
 
        /**
+        * @param string
+        */
+       public function setChangeType( $changeType ) {
+               $this->changeType = $changeType;
+       }
+
+       /**
         * @return string
         */
        public function getModerationState() {
@@ -563,6 +594,20 @@
        }
 
        /**
+        * @return revisionState[]
+        */
+       public function getRevisionState() {
+               return $this->revisionState;
+       }
+
+       /**
+        * @param RevisionState[]
+        */
+       public function setRevisionState( array $revisionState ) {
+               $this->revisionState = $revisionState;
+       }
+
+       /**
         * @return string|null
         */
        public function getModeratedReason() {
diff --git a/includes/Model/RevisionState.php b/includes/Model/RevisionState.php
new file mode 100644
index 0000000..ea8d05d
--- /dev/null
+++ b/includes/Model/RevisionState.php
@@ -0,0 +1,137 @@
+<?php
+
+namespace Flow\Model;
+
+use Flow\Exception\DataModelException;
+use User;
+
+/**
+ * Model class mapping to a revision state row
+ */
+class RevisionState {
+
+       /**
+        * @var UUID revision id
+        */
+       protected $revId;
+
+       /**
+        * @var string Revision state
+        */
+       protected $state;
+
+       /**
+        * @var int|null User id setting the revision state
+        */
+       protected $userId;
+
+       /**
+        * @var string|null User ip setting the revision state
+        */
+       protected $userIp;
+
+       /**
+        * @var string User wiki setting the revision state
+        */
+       protected $userWiki;
+
+       /**
+        * @var string Comment for setting the revision state
+        */
+       protected $comment;
+
+       /**
+        * Create a RevisionState object
+        *
+        * @param User
+        * @param string
+        * @param string
+        * @return RevisionState
+        */
+       public function create( User $user, $state, $comment = '' ) {
+               $obj = new self();
+               $obj->revId = UUID::create();
+               list( $obj->userId, $obj->userIp, $obj->userWiki ) = 
AbstractRevision::userFields( $user );
+               $obj->comment = $comment;
+               return $obj;
+       }
+
+       /**
+        * @param array
+        * @param RevisionState|null
+        * @return RevisionState
+        * @throws DataModelException
+        */
+       public static function fromStorageRow( array $row, $obj = null ) {
+               if ( $obj === null ) {
+                       $obj = new self;
+               } elseif ( !$obj instanceof self ) {
+                       throw new DataModelException( 'Wrong obj type: ' . 
get_class( $obj ), 'process-data' );
+               }
+               $obj->revId = UUID::create( $row['frs_rev_id'] );
+               $obj->state = $row['frs_state'];
+               $obj->userId = $row['frs_user_id'];
+               $obj->userIp = $row['frs_user_ip'];
+               $obj->userWiki = $row['frs_user_wiki'];
+               $obj->comment = $row['frs_comment'];
+               return $obj;
+       }
+
+       /**
+        * @param RevisionState
+        * @return array
+        */
+       public static function toStorageRow( RevisionState $obj ) {
+               return array(
+                       'frs_rev_id' => $obj->revId->getBinary(),
+                       'frs_state' => $obj->state,
+                       'frs_user_id' => $obj->userId,
+                       'frs_user_ip' => $obj->userIp,
+                       'frs_user_wiki' => $obj->userWiki,
+                       'frs_comment' => $obj->comment,
+               );
+       }
+
+       /**
+        * @return UUID
+        */
+       public function getRevId() {
+               return $this->revId;
+       }
+
+       /**
+        * @return string
+        */
+       public function getState() {
+               return $this->state;
+       }
+
+       /**
+        * @return int
+        */
+       public function getUserId() {
+               return $this->userId;
+       }
+
+       /**
+        * @return string
+        */
+       public function getUserIp() {
+               return $this->userIp;
+       }
+
+       /**
+        * @return string
+        */
+       public function getUserWiki() {
+               return $this->userWiki;
+       }
+
+       /**
+        * @return string
+        */
+       public function getComment() {
+               return $this->comment;
+       }
+
+}

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

Gerrit-MessageType: newchange
Gerrit-Change-Id: I269b5cd143ea956b670d46483486b3cecaeebcd7
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/Flow
Gerrit-Branch: master
Gerrit-Owner: Bsitu <bs...@wikimedia.org>

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

Reply via email to