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