Werdna has uploaded a new change for review.

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


Change subject: Paging for topics on TopicList objects.
......................................................................

Paging for topics on TopicList objects.

TODO:
* API offset/limit support
* JavaScript infinite scrolling.

Change-Id: Id94af9cf71c4ca0fd89bce7fc58c3bb756742777
---
M Flow.i18n.php
M Flow.php
M includes/Block/TopicList.php
M includes/Data/ObjectManager.php
M includes/Templating.php
M modules/discussion/base.css
M templates/topiclist.html.php
7 files changed, 253 insertions(+), 24 deletions(-)


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

diff --git a/Flow.i18n.php b/Flow.i18n.php
index 8eb27ae..798540d 100644
--- a/Flow.i18n.php
+++ b/Flow.i18n.php
@@ -67,6 +67,9 @@
 
        'flow-comment-restored' => 'Restored comment',
        'flow-comment-deleted' => 'Deleted comment',
+
+       'flow-paging-rev' => 'More recent topics',
+       'flow-paging-fwd' => 'Older topics',
 );
 
 /** Message documentation (Message documentation)
@@ -142,6 +145,9 @@
 "this item" seems to refer "this post".',
        'flow-summaryedit-submit' => 'Used as label for the Submit button.',
        'flow-edit-title-submit' => 'Used as label for the Submit button.',
+
+       'flow-paging-rev' => 'Label for paging link that shows more recently 
modified topics',
+       'flow-paging-fwd' => 'Label for paging link that shows less recently 
modified topics',
 );
 
 /** Asturian (asturianu)
diff --git a/Flow.php b/Flow.php
index 7ff4339..e880e36 100755
--- a/Flow.php
+++ b/Flow.php
@@ -190,6 +190,8 @@
 // When visiting the flow for an article but not specifying what type of 
workflow should be viewed,
 // use this workflow
 $wgFlowDefaultWorkflow = 'discussion';
+$wgFlowDefaultLimit = 5;
+$wgFlowMaxLimit = 50;
 
 $wgFlowUseParsoid = false;
 $wgFlowParsoidURL = 'http://localhost:8000';
diff --git a/includes/Block/TopicList.php b/includes/Block/TopicList.php
index 142493f..1f8b876 100644
--- a/includes/Block/TopicList.php
+++ b/includes/Block/TopicList.php
@@ -4,6 +4,7 @@
 
 use Flow\Model\PostRevision;
 use Flow\Model\TopicListEntry;
+use Flow\Model\UUID;
 use Flow\Model\Workflow;
 use Flow\Data\ManagerGroup;
 use Flow\Data\ObjectManager;
@@ -75,10 +76,50 @@
 
        public function render( Templating $templating, array $options ) {
                $templating->getOutput()->addModules( array( 
'ext.flow.discussion' ) );
+               $topics = $this->getTopics( $options );
+
+               $requestedDirection = isset( $options['offset-dir'] )
+                       ? $options['offset-dir']
+                       : 'fwd';
+
+               $requestedOffset = isset( $options['offset-id'] )
+                       ? $options['offset-id']
+                       : false;
+
+               $requestedLimit = $this->getLimit( $options );
+
+               $nextPage = end( $topics )->getWorkflowId()->getHex();
+
+               if (
+                       $requestedDirection == 'rev' &&
+                       count( $topics ) > $requestedLimit &&
+                       $nextPage === $requestedOffset
+               ) {
+                       // array_shift() shifts the first value of the array 
off and returns it
+                       $prevTopic = array_shift( $topics );
+                       $prevPage = reset($topics)->getWorkflowId()->getHex();
+               } elseif (
+                       $requestedDirection == 'fwd' &&
+                       $requestedOffset
+               ) {
+                       $prevPage = reset($topics)->getWorkflowId()->getHex();
+               } else {
+                       $prevPage = false;
+               }
+
+               if ( count( $topics ) > $requestedLimit ) {
+                       $topics = array_slice( $topics, 0, $requestedLimit + 1 
);
+                       $nextPage = array_pop( $topics 
)->getWorkflowId()->getHex();
+               } else {
+                       $nextPage = false;
+               }
+
                $templating->render( "flow:topiclist.html.php", array(
                        'topicList' => $this,
-                       'topics' => $this->getTopics(),
+                       'topics' => $topics,
                        'user' => $this->user,
+                       'prevPage' => $prevPage,
+                       'nextPage' => $nextPage,
                ) );
        }
 
@@ -93,12 +134,13 @@
                return $output;
        }
 
-       protected function getTopics() {
+       protected function getTopics( $options = array() ) {
                // New workflows cant have content yet
                if ( $this->workflow->isNew() ) {
                        return array();
                } else {
-                       return $this->loadAllRelatedTopics();
+                       $findOptions = $this->getFindOptions( $options );
+                       return $this->loadAllRelatedTopics( $findOptions );
                }
        }
 
@@ -106,10 +148,53 @@
                return 'topic_list';
        }
 
-       protected function loadAllRelatedTopics() {
-               $found = $this->storage->find( 'TopicListEntry', array(
-                       'topic_list_id' => $this->workflow->getId(),
-               ) );
+       protected function getLimit( $options ) {
+               global $wgFlowDefaultLimit;
+               $limit = $wgFlowDefaultLimit;
+               if ( isset( $requestOptions['limit'] ) ) {
+                       $requestedLimit = intval( $requestOptions['limit'] );
+                       if ( $requestedLimit > 0 && $requestedLimit < 
$wgFlowMaxLimit ) {
+                               $limit = $requestedLimit;
+                       }
+               }
+
+               return $limit;
+       }
+
+       protected function getFindOptions( $requestOptions ) {
+               global $wgFlowDefaultLimit, $wgFlowMaxLimit;
+               $findOptions = array();
+
+               // Compute offset/limit
+               $limit = $this->getLimit( $requestOptions );
+
+               $findOptions['limit'] = $limit + 1;
+
+               if ( isset( $requestOptions['offset-id'] ) ) {
+                       $findOptions['offset-key'] = UUID::create( 
$requestOptions['offset-id'] );
+               } elseif ( isset( $requestOptions['offset'] ) ) {
+                       $findOptions['offset'] = intval( 
$requestOptions['offset'] );
+               }
+
+               if ( isset( $requestOptions['offset-dir'] ) ) {
+                       $findOptions['offset-dir'] = 
$requestOptions['offset-dir'];
+
+                       if ( $findOptions['offset-dir'] == 'rev' ) {
+                               $findOptions['limit']++;
+                       }
+               }
+
+               return $findOptions;
+       }
+
+       protected function loadAllRelatedTopics( $findOptions = array() ) {
+               $found = $this->storage->find(
+                       'TopicListEntry',
+                       array(
+                               'topic_list_id' => $this->workflow->getId(),
+                       ),
+                       $findOptions
+               );
                if ( !$found ) {
                        return array();
                }
diff --git a/includes/Data/ObjectManager.php b/includes/Data/ObjectManager.php
index b460160..9718c2b 100644
--- a/includes/Data/ObjectManager.php
+++ b/includes/Data/ObjectManager.php
@@ -79,11 +79,9 @@
 // An Index is just a store that receives updates via handler.
 // backing store's can be passed via constructor
 interface Index extends LifecycleHandler {
-       // Indexes accept no query options
-       function find( array $keys );
+       function find( array $keys, array $options = array() );
 
-       // Indexes accept no query options
-       function findMulti( array $queries );
+       function findMulti( array $queries, array $options = array() );
 
        // Maximum number of items in a single index value
        function getLimit();
@@ -193,22 +191,16 @@
                if ( isset( $options['sort'] ) && !is_array( $options['sort'] ) 
) {
                        $options['sort'] = ObjectManager::makeArray( 
$options['sort'] );
                }
-               if ( isset( $options['limit'] ) ) {
-                       $limit = $options['limit'];
-                       $offset = isset( $options['offset'] ) ? 
$options['offset'] : 0;
-               } else {
-                       $limit = false;
-               }
 
-               $res = $this->getIndexFor( $keys, $options )->findMulti( 
$queries );
+               $index = $this->getIndexFor( $keys, $options );
+               $res = $index->findMulti( $queries, $options );
+
                if ( $res === null ) {
                        return null;
                }
+
                $retval = array();
                foreach ( $res as $i => $rows ) {
-                       if ( $limit ) {
-                               $rows = array_slice( $rows, $offset, $limit, 
true );
-                       }
                        foreach ( $rows as $j => $row ) {
                                $retval[$i][$j] = $this->load( $row );
                        }
@@ -722,12 +714,12 @@
                // nothing to do
        }
 
-       public function find( array $attributes ) {
-               $results = $this->findMulti( array( $attributes ) );
+       public function find( array $attributes, array $options = array() ) {
+               $results = $this->findMulti( array( $attributes ), $options );
                return reset( $results );
        }
 
-       public function findMulti( array $queries ) {
+       public function findMulti( array $queries, array $options = array() ) {
                if ( !$queries ) {
                        return array();
                }
@@ -907,6 +899,73 @@
                return $this->options['limit'];
        }
 
+       public function getSort() {
+               return $this->options['sort'];
+       }
+
+       public function findMulti( array $queries, array $options = array() ) {
+               $result = parent::findMulti( $queries, $options );
+
+               // Paging related stuff
+               // Bail out if no offset is given
+               $limit = isset( $options['limit'] ) ? $options['limit'] : 
$this->getLimit();
+               if ( ! isset( $options['offset-key'] ) ) {
+                       $output = array();
+
+                       foreach( $result as $i => $rows ) {
+                               $output[$i] = array_slice( $rows, 0, $limit );
+                       }
+
+                       return $output;
+               }
+               $offsetKey = $options['offset-key'];
+               if ( $offsetKey instanceof UUID ) {
+                       $offsetKey = $offsetKey->getBinary();
+               }
+
+               $dir = 'fwd';
+               if (
+                       isset( $options['offset-dir'] ) &&
+                       $options['offset-dir'] === 'rev'
+               ) {
+                       $dir = 'rev';
+               }
+
+               // Find offset in results
+               $output = array();
+               foreach( $result as $i => $rows ) {
+                       $offset = false;
+                       for( $rowIndex = 0; $rowIndex < count( $rows ); 
++$rowIndex ) {
+                               $row = $rows[$rowIndex];
+                               $comparisonValue = $this->compareRowToOffset( 
$row, $offsetKey );
+                               if ( $comparisonValue <= 0 ) {
+                                       $offset = $rowIndex;
+                                       break;
+                               }
+                       }
+
+                       if ( $offset === false ) {
+                               throw new \MWException( "Unable to find 
specified offset in query results" );
+                       }
+
+                       if ( $dir === 'fwd' ) {
+                               $startPos = $offset;
+                       } else {
+                               $startPos = $offset - $limit + 1;
+
+                               if ( $startPos < 0 ) {
+                                       $startPos = 0;
+                               }
+                       }
+
+                       $rows = array_slice( $rows, $startPos, $limit );
+
+                       $output[$i] = $rows;
+               }
+
+               return $output;
+       }
+
        protected function maybeCreateIndex( array $indexed, array $sourceRow, 
array $compacted ) {
                if ( call_user_func( $this->options['create'], $sourceRow ) ) {
                        $this->cache->set( $this->cacheKey( $indexed ), array( 
$compacted ) );
@@ -985,6 +1044,37 @@
                );
        }
 
+       protected function compareRowToOffset( $row, $offset ) {
+               $sortFields = $this->getSort();
+               $splitOffset = explode( '|', $offset );
+               $fieldIndex = 0;
+
+               foreach( $sortFields as $field ) {
+                       $valueInRow = $row[$field];
+                       $valueInOffset = $splitOffset[$fieldIndex];
+
+                       if ( $valueInRow > $valueInOffset ) {
+                               return 1;
+                       } elseif ( $valueInRow < $valueInOffset ) {
+                               return -1;
+                       }
+                       ++$fieldIndex;
+               }
+
+               return 0;
+       }
+
+       public function serializeOffset( $row ) {
+               $sortFields = $this->getSort();
+               $offsetFields = array();
+
+               foreach( $sortFields as $field ) {
+                       $offsetFields[] = $row->$field;
+               }
+
+               return implode( '|', $offsetFields );
+       }
+
        // INTERNAL: in 5.4 it can be protected
        public function sortIndex( array $values ) {
                // I dont think this is a valid way to sort a 128bit integer 
string
diff --git a/includes/Templating.php b/includes/Templating.php
index 9be7e69..ebb424a 100644
--- a/includes/Templating.php
+++ b/includes/Templating.php
@@ -90,5 +90,36 @@
                        'root' => $root,
                ), $return );
        }
+
+       public function getPagingLink( $block, $direction, $offset ) {
+               $output = '';
+
+               // Use the message/class flow-paging-fwd or flow-paging-rev
+               //  depending on direction
+               $output .= \Html::element(
+                       'a',
+                       array(
+                               'href' => $this->generateUrl(
+                                       $block->getWorkflowId(),
+                                       'view',
+                                       array(
+                                               $block->getName().'[offset-id]' 
=> $offset,
+                                               
$block->getName().'[offset-dir]' => $direction,
+                                       )
+                               ),
+                       ),
+                       wfMessage( 'flow-paging-'.$direction )->parse()
+               );
+
+               $output = \Html::rawElement(
+                       'div',
+                       array(
+                               'class' => 'flow-paging 
flow-paging-'.$direction,
+                       ),
+                       $output
+               );
+
+               return $output;
+       }
 }
 
diff --git a/modules/discussion/base.css b/modules/discussion/base.css
index 08de39f..abc32c1 100644
--- a/modules/discussion/base.css
+++ b/modules/discussion/base.css
@@ -917,4 +917,11 @@
 
 .flow-edit-post-form {
        padding: 4px;
+}
+
+.flow-paging {
+       padding: 4px;
+       font-size: large;
+       border: 1px solid #ddddff;
+       text-align: center;
 }
\ No newline at end of file
diff --git a/templates/topiclist.html.php b/templates/topiclist.html.php
index b704f9b..51063cc 100644
--- a/templates/topiclist.html.php
+++ b/templates/topiclist.html.php
@@ -37,6 +37,14 @@
 ), wfMessage( 'flow-disclaimer' )->parse() );
 echo '</form>';
 
+if ( $prevPage ) {
+       echo $this->getPagingLink( $topicList, 'rev', $prevPage );
+}
+
 foreach ( $topics as $topic ) {
        echo $topic->render( $this, array(), true );
 }
+
+if ( $nextPage ) {
+       echo $this->getPagingLink( $topicList, 'fwd', $nextPage );
+}
\ No newline at end of file

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

Gerrit-MessageType: newchange
Gerrit-Change-Id: Id94af9cf71c4ca0fd89bce7fc58c3bb756742777
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/Flow
Gerrit-Branch: master
Gerrit-Owner: Werdna <[email protected]>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to