EBernhardson (WMF) has uploaded a new change for review.

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


Change subject: New topic action: thread-history
......................................................................

New topic action: thread-history

New index on PostRevision indexes revisions by the root post of their topic
tree. Creates a query-only 'topic_root' field which when queried with
options matching the index will return the sorted history of that topic.

Change-Id: I6f0553f06a98e42f739bcad388cbec340db3718d
---
M Flow.php
M container.php
M includes/Block/Summary.php
M includes/Block/Topic.php
M includes/Data/ObjectManager.php
M includes/Data/RevisionStorage.php
M includes/Model/AbstractRevision.php
M includes/Repository/TreeRepository.php
A templates/topic-history.html.php
M templates/topic.html.php
10 files changed, 158 insertions(+), 12 deletions(-)


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

diff --git a/Flow.php b/Flow.php
index f5be5aa..b22ced1 100755
--- a/Flow.php
+++ b/Flow.php
@@ -74,8 +74,9 @@
 $wgAutoloadClasses['Flow\Data\ObjectManager'] = $dir . 
'includes/Data/ObjectManager.php';
 $wgAutoloadClasses['Flow\Data\LifecycleHandler'] = $dir . 
'includes/Data/ObjectManager.php';
 $wgAutoloadClasses['Flow\Data\Index'] = $dir . 
'includes/Data/ObjectManager.php';
-$wgAutoloadClasses['Flow\Data\UniqueIndex'] = $dir . 
'includes/Data/ObjectManager.php';
-$wgAutoloadClasses['Flow\Data\SecondaryIndex'] = $dir . 
'includes/Data/ObjectManager.php';
+$wgAutoloadClasses['Flow\Data\UniqueFeatureIndex'] = $dir . 
'includes/Data/ObjectManager.php';
+$wgAutoloadClasses['Flow\Data\TopKIndex'] = $dir . 
'includes/Data/ObjectManager.php';
+$wgAutoloadClasses['Flow\Data\TopicHistoryIndex'] = $dir . 
'includes/Data/ObjectManager.php';
 $wgAutoloadClasses['Flow\Data\ObjectStorage'] = $dir . 
'includes/Data/ObjectManager.php';
 $wgAutoloadClasses['Flow\Data\BasicDbStorage'] = $dir . 
'includes/Data/ObjectManager.php';
 $wgAutoloadClasses['Flow\Data\ObjectMapper'] = $dir . 
'includes/Data/ObjectManager.php';
diff --git a/container.php b/container.php
index 401f24f..1e9a0f3 100644
--- a/container.php
+++ b/container.php
@@ -56,6 +56,7 @@
 use Flow\Data\SummaryRevisionStorage;
 use Flow\Data\UniqueFeatureIndex;
 use Flow\Data\TopKIndex;
+use Flow\Data\TopicHistoryIndex;
 use Flow\Data\ObjectMapper;
 use Flow\Data\ObjectManager;
 
@@ -182,6 +183,7 @@
        $pk = new UniqueFeatureIndex( $cache, $storage, 'flow_revision:pk', 
array( 'rev_id' ) );
        $indexes = array(
                $pk,
+               // revision history
                new TopKIndex( $cache, $storage, 'flow_revision:descendant',
                        array( 'tree_rev_descendant_id' ),
                        array(
@@ -194,6 +196,7 @@
                                        return $row['rev_parent_id'] === null;
                                },
                ) ),
+               // most recent revision
                new TopKIndex( $cache, $storage, 'flow_revision:latest_post',
                        array( 'tree_rev_descendant_id' ),
                        array(
@@ -205,6 +208,21 @@
                                        return $row['rev_parent_id'] === null;
                                },
                ) ),
+               // thread history -- to keep a history by thread we have to 
know what thread every post
+               // belongs to, not just its parent. TopicHistoryIndex is a 
slight tweak to TopKIndex
+               // using TreeRepository for extra information and stuffing it 
into thread_root while indexing
+               new TopicHistoryIndex( $cache, $storage, $c['repository.tree'], 
'flow_revision:thread',
+                       array( 'thread_root' ),
+                       array(
+                               'limit' => 500,
+                               'sort' => 'rev_id',
+                               'order' => 'DESC',
+                               'shallow' => $pk,
+                               'create' => function( array $row ) {
+                                       // if the post has no parent it is a 
root of a new thread
+                                       return $row['tree_parent_id'] === null;
+                               },
+               ) )
        );
 
        return new ObjectManager( $mapper, $storage, $indexes );
diff --git a/includes/Block/Summary.php b/includes/Block/Summary.php
index 9080b6e..5496e72 100644
--- a/includes/Block/Summary.php
+++ b/includes/Block/Summary.php
@@ -47,7 +47,7 @@
                                $this->errors['prev_revision'] = wfMessage( 
'flow-prev-revision-mismatch' )->params( $this->submitted['prev_revision'], 
$this->summary->getRevisionId()->getHex() );
                        }
                        // this isnt really part of validate, but we want the 
error-rendering template to see the users edited summary
-                       $this->summary = $this->summary->newNextRevision( 
$this->user, $this->submitted['content'] );
+                       $this->summary = $this->summary->newNextRevision( 
$this->user, $this->submitted['content'], 'flow-edit-summary' );
                } else {
                        if ( empty( $this->submitted['prev_revision'] ) ) {
                                // this isnt really part of validate either, 
should validate be renamed or should this logic be redone?
diff --git a/includes/Block/Topic.php b/includes/Block/Topic.php
index b40622d..9f67572 100644
--- a/includes/Block/Topic.php
+++ b/includes/Block/Topic.php
@@ -19,6 +19,8 @@
        protected $newRevision;
        protected $requestedPost;
 
+       // POST actions, GET do not need to be listed
+       // unrecognized GET actions fallback to 'view'
        protected $supportedActions = array(
                'edit-post', 'delete-post', 'restore-post',
                'reply', 'delete-topic',
@@ -80,7 +82,7 @@
                                throw new \Exception( 'No revision associated 
with workflow?' );
                        }
 
-                       $this->topicTitle = $topicTitle->newNextRevision( 
$this->user, $this->submitted['content'] );
+                       $this->topicTitle = $topicTitle->newNextRevision( 
$this->user, $this->submitted['content'], 'flow-edit-title' );
                }
        }
 
@@ -177,7 +179,7 @@
                }
                $post = $this->loadRequestedPost( $this->submitted['postId'] );
                if ( $post ) {
-                       $this->newRevision = $post->newNextRevision( 
$this->user, $this->parsedContent );
+                       $this->newRevision = $post->newNextRevision( 
$this->user, $this->parsedContent, 'flow-edit-post' );
                } else {
                        $this->errors['edit-post'] = wfMessage( 
'flow-post-not-found' );
                }
@@ -256,6 +258,13 @@
                case 'post-history':
                        return $this->renderPostHistory( $templating, $options, 
$return );
 
+               case 'topic-history':
+                       return $templating->render( 
"flow:topic-history.html.php", array(
+                               'block' => $this,
+                               'topic' => $this->workflow,
+                               'history' => $this->loadThreadHistory(),
+                       ) );
+
                case 'edit-post':
                        return $this->renderEditPost( $templating, $options, 
$return );
 
@@ -282,7 +291,7 @@
                return $templating->render( "flow:post-history.html.php", array(
                        'block' => $this,
                        'topic' => $this->workflow,
-                       'history' => $$this->getHistory( $options['postId'] ),
+                       'history' => $this->getHistory( $options['postId'] ),
                ), $return );
        }
 
@@ -449,6 +458,19 @@
                return $this->topicTitle;
        }
 
+       protected function loadThreadHistory() {
+               $found = $this->storage->find(
+                       'PostRevision',
+                       array( 'topic_root' => $this->workflow->getId() ),
+                       array( 'sort' => 'rev_id', 'order' => 'DESC', 'limit' 
=> 100 )
+               );
+               if ( $found ) {
+                       return $found;
+               } else {
+                       die( 'kersplat' );
+               }
+       }
+
        protected function loadRequestedPost( $postId ) {
                if ( !isset( $this->requestedPost[$postId] ) ) {
                        $found = $this->storage->find(
diff --git a/includes/Data/ObjectManager.php b/includes/Data/ObjectManager.php
index b11f91c..b460160 100644
--- a/includes/Data/ObjectManager.php
+++ b/includes/Data/ObjectManager.php
@@ -765,6 +765,11 @@
                if ( count( $queries ) === 0 ) {
                        return $results;
                }
+
+               return $this->backingStoreFindMulti( $queries, $idxToKey, 
$results );
+       }
+
+       protected function backingStoreFindMulti( array $queries, array 
$idxToKey, array $results = array() ) {
                // query backing store
                $stored = $this->storage->findMulti( $queries, 
$this->queryOptions() );
                // map store results to cache key
diff --git a/includes/Data/RevisionStorage.php 
b/includes/Data/RevisionStorage.php
index 334e4fc..f628cb1 100644
--- a/includes/Data/RevisionStorage.php
+++ b/includes/Data/RevisionStorage.php
@@ -52,7 +52,7 @@
                }
                $retval = array();
                foreach ( $res as $row ) {
-                       $retval[$row->rev_id] = (array) $row;
+                       $retval[UUID::create( $row->rev_id )->getHex()] = 
(array) $row;
                }
                return $retval;
        }
@@ -441,6 +441,75 @@
 }
 
 /**
+ * Slight tweak to the TopKIndex uses additional info from TreeRepository to 
build the cache
+ */
+class TopicHistoryIndex extends TopKIndex {
+
+       protected $treeRepository;
+
+       public function __construct( BufferedCache $cache, PostRevisionStorage 
$storage, TreeRepository $treeRepo, $prefix, array $indexed, array $options = 
array() ) {
+               if ( $indexed !== array( 'topic_root' ) ) {
+                       throw new \Exception( __CLASS__ . ' is hardcoded to 
only index topic_root' );
+               }
+               parent::__construct( $cache, $storage, $prefix, $indexed, 
$options );
+               $this->treeRepository = $treeRepo;
+       }
+
+       public function onAfterInsert( $object, array $new ) {
+               $new['topic_root'] = $this->treeRepository->findRoot( 
UUID::create( $new['tree_rev_descendant_id'] ) );
+               parent::onAfterInsert( $object, $new );
+       }
+
+       public function onAfterUpdate( $object, array $old, array $new ) {
+               $old['topic_root'] = $new['thread_root'] = 
$this->treeRepository->findRoot( UUID::create( $old['tree_rev_descendant_id'] ) 
);
+               parent::onAfterUpdate( $object, $old, $new );
+       }
+
+       public function onAfterRemove( $object, array $old ) {
+               $old['topic_root'] = $this->treeRepository->findRoot( 
UUID::create( $old['tree_rev_descendant_id'] ) );
+               parent::onAfterRemove( $object, $old );
+       }
+
+       public function backingStoreFindMulti( array $queries, array $idxToKey, 
array $retval = array() ) {
+               // all queries are for roots( guaranteed by constructor), so 
anything that falls
+               // through and has to be queried from storage will actually 
need to be doing a
+               // special condition either joining against flow_tree_node or 
first collecting the
+               // subtree node lists and then doing a big IN condition
+
+               // This isn't a hot path(should be pre-populated into index) 
but we still dont want
+               // horrible performance
+
+               $roots = array();
+               foreach ( $queries as $idx => $features ) {
+                       $roots[] = UUID::create( $features['topic_root'] );
+               }
+               $nodeList = $this->treeRepository->fetchSubtreeNodeList( $roots 
);
+
+               $descendantQueries = array();
+               foreach ( $queries as $idx => $features ) {
+                       $list = array();
+                       $nodes = $nodeList[$features['topic_root']->getHex()];
+                       $descendantQueries[$idx] = array(
+                               'tree_rev_descendant_id' => UUID::convertUUIDs( 
$nodes ),
+                       );
+               }
+
+               $res = $this->storage->findMulti( $descendantQueries, 
$this->queryOptions() );
+               if  ( !$res ) {
+                       return false;
+               }
+               foreach ( $res as $idx => $rows ) {
+                       $retval[$idx] = $rows;
+                       $this->cache->add( $idxToKey[$idx], 
$this->rowCompactor->compactRows( $rows ) );
+                       unset( $queries[$idx] );
+               }
+               if ( $queries ) {
+                       // Log something about not finding everything?
+               }
+               return $retval;
+       }
+}
+/**
  * This assists in performing client-side 1-to-1 joins.  It collects the 
foreign key
  * from a multi-dimensional array, queries a callable for the foreign key 
values and
  * then returns the source data with related data merged in.
diff --git a/includes/Model/AbstractRevision.php 
b/includes/Model/AbstractRevision.php
index 1e1f341..7b87cfc 100644
--- a/includes/Model/AbstractRevision.php
+++ b/includes/Model/AbstractRevision.php
@@ -70,9 +70,10 @@
                return $obj;
        }
 
-       public function newNextRevision( User $user, $content ) {
+       public function newNextRevision( User $user, $content, $comment ) {
                $obj = $this->newNullRevision( $user );
                $obj->setContent( $content );
+               $obj->comment = $comment;
                return $obj;
        }
 
diff --git a/includes/Repository/TreeRepository.php 
b/includes/Repository/TreeRepository.php
index 6c91115..105233f 100644
--- a/includes/Repository/TreeRepository.php
+++ b/includes/Repository/TreeRepository.php
@@ -142,8 +142,12 @@
                if ( !$res ) {
                        return null;
                }
+               $path = array();
                foreach ( $res as $row ) {
                        $path[$row->tree_depth] = UUID::create( 
$row->tree_ancestor_id );
+               }
+               if ( !$path ) {
+                       throw new \Exception( 'No root path found? Is this a 
root already? ' . $descendant->getHex() );
                }
                ksort( $path );
                $path = array_reverse( $path );
@@ -211,7 +215,13 @@
                        $roots,
                        array( $this, 'fetchSubtreeNodeListFromDb' )
                );
-               return $res;
+
+               // $idx is a binary UUID
+               $retval = array();
+               foreach ( $res as $idx => $val ) {
+                       $retval[UUID::create( $idx )->getHex()] = $val;
+               }
+               return $retval;
        }
 
        public function fetchSubtreeNodeListFromDb( array $roots ) {
diff --git a/templates/topic-history.html.php b/templates/topic-history.html.php
new file mode 100644
index 0000000..f741204
--- /dev/null
+++ b/templates/topic-history.html.php
@@ -0,0 +1,12 @@
+<?php
+
+echo wfMessage( 'flow-topic-history' )->escaped();
+echo '<ul>';
+foreach ( $history as $revision ) {
+       echo '<li>'
+               . $revision->getRevisionId()->getHex() . ' : '
+               . $revision->getUserText() . ' : '
+               . $revision->getComment()
+               . '</li>';
+}
+echo '</ul>';
diff --git a/templates/topic.html.php b/templates/topic.html.php
index fd18ce3..106f0b2 100644
--- a/templates/topic.html.php
+++ b/templates/topic.html.php
@@ -91,9 +91,17 @@
 };
 
 echo Html::element( 'h4', array(), $root->getContent() ),
-       Html::rawElement( 'a', array(
-               'href' => $this->generateUrl( $root->getPostId(), 'edit-title' )
-       ), wfMessage( 'flow-action-edit-title' ) );
+       '<ul>',
+               '<li>',
+                       Html::rawElement( 'a', array(
+                               'href' => $this->generateUrl( 
$root->getPostId(), 'topic-history' )
+                       ), wfMessage( 'flow-action-topic-history' ) ),
+               '</li><li>',
+                       Html::rawElement( 'a', array(
+                               'href' => $this->generateUrl( 
$root->getPostId(), 'edit-title' )
+                       ), wfMessage( 'flow-action-edit-title' ) ),
+               '</li>',
+       '<ul>';
 
 foreach( $root->getChildren() as $child ) {
        $renderPost( $child );

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

Gerrit-MessageType: newchange
Gerrit-Change-Id: I6f0553f06a98e42f739bcad388cbec340db3718d
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/Flow
Gerrit-Branch: master
Gerrit-Owner: EBernhardson (WMF) <ebernhard...@wikimedia.org>

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

Reply via email to