EBernhardson (WMF) has submitted this change and it was merged. Change subject: edit-post action for Topic block ......................................................................
edit-post action for Topic block Includes some refactoring of Parsoid, and a few other unrelated changes. Change-Id: Ie62ff67ae09ec131ae49acce3ffe779311203842 --- M Flow.i18n.php M Flow.php M includes/Block/Topic.php M includes/Block/TopicList.php M includes/Model/PostRevision.php M includes/Model/UUID.php M includes/Model/Workflow.php A includes/ParsoidUtils.php M includes/Templating.php M includes/UrlGenerator.php M includes/api/ApiQueryFlow.php M modules/base/ext.flow.base.js M modules/discussion/base.css M modules/discussion/discussion.js A templates/edit-post.html.php M templates/post.html.php M templates/topic.html.php 17 files changed, 502 insertions(+), 117 deletions(-) Approvals: EBernhardson (WMF): Verified; Looks good to me, approved diff --git a/Flow.i18n.php b/Flow.i18n.php index 38844e0..2914ba7 100644 --- a/Flow.i18n.php +++ b/Flow.i18n.php @@ -29,7 +29,13 @@ 'flow-reply-placeholder' => 'Click to reply to $1. Be nice!', 'flow-reply-submit' => 'Post Reply', + 'flow-edit-post-submit' => 'Submit changes', + + 'flow-post-action-view' => 'Permalink', + 'flow-post-action-post-history' => 'Post history', 'flow-post-action-delete-post' => 'Delete post', + 'flow-post-action-edit-post' => 'Edit post', + 'flow-post-action-edit' => 'Edit', 'flow-post-action-restore-post' => 'Restore post', 'flow-topic-action-edit-title' => 'Edit Title', diff --git a/Flow.php b/Flow.php index 2ba0fee..10508c5 100755 --- a/Flow.php +++ b/Flow.php @@ -40,7 +40,7 @@ 'descriptionmsg' => 'flow-desc', ); -$dir = dirname( __FILE__ ) . '/'; +$dir = __DIR__ . '/'; $wgExtensionMessagesFiles['Flow'] = $dir . 'Flow.i18n.php'; // Classes fulfilling the mediawiki extension architecture @@ -51,6 +51,7 @@ $wgAutoloadClasses['Pimple'] = $dir . 'vendor/Pimple.php'; $wgAutoloadClasses['Flow\Container'] = $dir . 'includes/Container.php'; $wgAutoloadClasses['Flow\DbFactory'] = $dir . 'includes/DbFactory.php'; +$wgAutoloadClasses['Flow\ParsoidUtils'] = $dir . 'includes/ParsoidUtils.php'; $wgAutoloadClasses['Flow\Templating'] = $dir . 'includes/Templating.php'; $wgAutoloadClasses['Flow\UrlGenerator'] = $dir . 'includes/UrlGenerator.php'; $wgAutoloadClasses['Flow\WorkflowLoader'] = $dir . 'includes/WorkflowLoader.php'; @@ -158,6 +159,7 @@ 'flow-error-external', 'flow-error-external-multi', 'flow-edit-title-submit', + 'flow-edit-post-submit', ), ), ); @@ -190,6 +192,6 @@ $wgFlowUseParsoid = false; $wgFlowParsoidURL = 'http://localhost:8000'; -$wgFlowParsoidPrefix = '_wikitext'; +$wgFlowParsoidPrefix = 'localhost'; $wgFlowParsoidTimeout = 100; diff --git a/includes/Block/Topic.php b/includes/Block/Topic.php index 59f5a9b..66ddb02 100644 --- a/includes/Block/Topic.php +++ b/includes/Block/Topic.php @@ -8,6 +8,7 @@ use Flow\Data\ManagerGroup; use Flow\Data\RootPostLoader; use Flow\DbFactory; +use Flow\ParsoidUtils; use Flow\Templating; use User; @@ -17,8 +18,12 @@ protected $topicTitle; protected $rootLoader; protected $newRevision; + protected $requestedPost; - protected $supportedActions = array( 'edit-title', 'reply', 'delete-topic', 'delete-post', 'restore-post' ); + protected $supportedActions = array( + 'edit-post', 'delete-post', 'restore-post', + 'reply', 'delete-topic', + ); public function __construct( Workflow $workflow, ManagerGroup $storage, $root ) { parent::__construct( $workflow, $storage ); @@ -56,6 +61,10 @@ $this->validateRestorePost(); break; + case 'edit-post': + $this->validateEditPost(); + break; + default: throw new \MWException( "Unexpected action: {$this->action}" ); } @@ -80,7 +89,7 @@ if ( empty( $this->submitted['content'] ) ) { $this->errors['content'] = wfMessage( 'flow-error-missing-content' ); } else { - $this->parsedContent = $this->convertWikitextToHtml5( $this->submitted['content'] ); + $this->parsedContent = ParsoidUtils::convertWikitextToHtml5( $this->submitted['content'], $this->workflow->getArticleTitle() ); if ( empty( $this->parsedContent ) ) { $this->errors['content'] = wfMessage( 'flow-error-parsoid-failure' ); } @@ -153,45 +162,25 @@ } } - // @todo: I assume not only topic reply, but also TopicListBlock & SummaryBlock's content need to be converted? - protected function convertWikitextToHtml5( $wikitext ) { - global $wgFlowUseParsoid; - - if ( $wgFlowUseParsoid ) { - global $wgFlowParsoidURL, $wgFlowParsoidPrefix, $wgFlowParsoidTimeout; - - $parsoidOutput = \Http::post( - $wgFlowParsoidURL . '/' . $wgFlowParsoidPrefix . '/', - array( - 'postData' => array( - 'content' => $wikitext, - 'format' => 'html', - ), - 'timeout' => $wgFlowParsoidTimeout - ) - ); - - // Strip out the Parsoid boilerplate - $dom = new \DOMDocument(); - $dom->loadHTML( $parsoidOutput ); - $body = $dom->getElementsByTagName( 'body' )->item(0); - $html = ''; - - foreach( $body->childNodes as $child ) { - $html .= $child->ownerDocument->saveXML( $child ); - } - - return $html; + protected function validateEditPost() { + if ( empty( $this->submitted['postId'] ) ) { + $this->errors['edit-post'] = wfMessage( 'flow-no-post-provided' ); + return; + } + if ( empty( $this->submitted['content'] ) ) { + $this->errors['content'] = wfMessage( 'flow-missing-post-content' ); } else { - global $wgParser; - - $title = \Title::newFromText( 'Flow', NS_SPECIAL ); - - $options = new \ParserOptions; - $options->setTidy( true ); - - $output = $wgParser->parse( $wikitext, $title, $options ); - return $output->getText(); + $this->parsedContent = ParsoidUtils::convertWikitextToHtml5( $this->submitted['content'], $this->workflow->getArticleTitle() ); + if ( empty( $this->parsedContent ) ) { + $this->errors['content'] = wfMessage( 'flow-empty-parsoid-result' ); + return; + } + } + $post = $this->loadRequestedPost( $this->submitted['postId'] ); + if ( $post ) { + $this->newRevision = $post->newNextRevision( $this->user, $this->parsedContent ); + } else { + $this->errors['edit-post'] = wfMessage( 'flow-post-not-found' ); } } @@ -201,6 +190,7 @@ case 'delete-post': case 'restore-post': case 'edit-title': + case 'edit-post': if ( $this->newRevision === null ) { throw new \MWException( 'Attempt to save null revision' ); } @@ -241,35 +231,80 @@ } public function render( Templating $templating, array $options, $return = false ) { - if ( $this->action === 'post-history' ) { - if ( empty( $options['postId'] ) ) { - var_dump( $this->getName() ); - var_dump( $options ); - throw new \Exception( 'No postId specified' ); - $history = array(); - } else { - $history = $this->getHistory( $options['postId'] ); - } - return $templating->render( "flow:post-history.html.php", array( - 'block' => $this, - 'topic' => $this->workflow, - 'history' => $history, - ), $return ); - } elseif ( $this->action === 'edit-title' ) { + $templating->getOutput()->addModules( 'ext.flow.discussion' ); + switch( $this->action ) { + case 'post-history': + return $this->renderPostHistory( $templating, $options, $return ); + + case 'edit-post': + return $this->renderEditPost( $templating, $options, $return ); + + case 'edit-title': return $templating->render( "flow:edit-title.html.php", array( 'block' => $this, - 'user' => $this->user, 'topic' => $this->workflow, 'topicTitle' => $this->loadTopicTitle(), ) ); + + default: + $root = $this->loadRootPost(); + + if ( isset( $options['postId'] ) ) { + $post = $root->findDescendant( $options['postId'] ); + + return $templating->renderPost( + $post, + $this, + $return + ); + } else { + return $templating->renderTopic( + $root, + $this, + $return + ); + } } - - $templating->getOutput()->addModules( 'ext.flow.base' ); - - return $templating->renderTopic( $this, $this->workflow, $this->loadRootPost(), $this->user ); } - public function renderAPI ( array $options ) { + protected function renderPostHistory( Templating $templating, array $options, $return = false ) { + if ( !isset( $options['postId'] ) ) { + throw new \Exception( 'No postId provided' ); + } + return $templating->render( "flow:post-history.html.php", array( + 'block' => $this, + 'topic' => $this->workflow, + 'history' => $$this->getHistory( $options['postId'] ), + ), $return ); + } + + protected function renderEditPost( Templating $templating, array $options, $return = false ) { + if ( !isset( $options['postId'] ) ) { + throw new \Exception( 'No postId provided' ); + } + return $templating->render( "flow:edit-post.html.php", array( + 'block' => $this, + 'topic' => $this->workflow, + 'post' => $this->loadRequestedPost( $options['postId'] ), + ), $return ); + } + + public function renderAPI( array $options ) { + if ( isset( $options['postId'] ) ) { + $rootPost = $this->loadRootPost(); + $post = $rootPost->findDescendant( $options['postId'] ); + + if ( ! $post ) { + throw new MWException( "Requested post could not be found" ); + } + + return $this->renderPostAPI( $post, $options ); + } else { + return $this->renderTopicAPI( $options ); + } + } + + public function renderTopicAPI ( array $options ) { $output = array(); $rootPost = $this->loadRootPost(); $topic = $this->workflow; @@ -313,17 +348,21 @@ $output['post-deleted'] = 'post-deleted'; } else { $output['content'] = array( '*' => $post->getContent() ); + $contentSource = ParsoidUtils::convertHtml5ToWikitext( $post->getContent() ); + $output['content-src'] = array( '*' => $contentSource ); $output['user'] = $post->getUserText(); } - $children = array( '_element' => 'post' ); + if ( ! isset( $options['no-children'] ) ) { + $children = array( '_element' => 'post' ); - foreach( $post->getChildren() as $child ) { - $children[] = $this->renderPostAPI( $child, $options ); - } + foreach( $post->getChildren() as $child ) { + $children[] = $this->renderPostAPI( $child, $options ); + } - if ( count($children) > 1 ) { - $output['replies'] = $children; + if ( count($children) > 1 ) { + $output['replies'] = $children; + } } $postId = $post->getPostId()->getHex(); @@ -421,6 +460,24 @@ return $this->topicTitle; } + protected function loadRequestedPost( $postId ) { + if ( !isset( $this->requestedPost[$postId] ) ) { + $found = $this->storage->find( + 'PostRevision', + array( 'tree_rev_descendant_id' => $postId ), + array( 'sort' => 'rev_id', 'order' => 'DESC', 'limit' => 1 ) + ); + if ( $found ) { + $this->requestedPost[$postId] = reset( $found ); + } else { + // meh, signals that its not found, dont look again + $this->requestedPost[$postId] = false; + } + } + // catches the === false and returns null as expected + return $this->requestedPost[$postId] ?: null; + } + // Somehow the template has to know which post the errors go with public function getRepliedTo() { return isset( $this->submitted['replyTo'] ) ? $this->submitted['replyTo'] : null; @@ -433,7 +490,7 @@ // The prefix used for form data public function getName() { - return 'topic_list'; + return 'topic'; } } diff --git a/includes/Block/TopicList.php b/includes/Block/TopicList.php index 9d302cb..142493f 100644 --- a/includes/Block/TopicList.php +++ b/includes/Block/TopicList.php @@ -66,7 +66,7 @@ 'created-post-id' => $firstPost->getRevisionId(), 'render-function' => function($templating) use ($topicWorkflow, $firstPost, $topicPost, $storage, $user) { $block = new TopicBlock( $topicWorkflow, $storage, $topicPost ); - return $templating->renderTopic( $block, $topicWorkflow, $topicPost, $user ); + return $templating->renderTopic( $topicPost, $block, $user ); }, ); diff --git a/includes/Model/PostRevision.php b/includes/Model/PostRevision.php index 359aa46..e91da62 100644 --- a/includes/Model/PostRevision.php +++ b/includes/Model/PostRevision.php @@ -106,6 +106,25 @@ return $this->children; } + public function findDescendant( $postId ) { + if ( ! $postId instanceof UUID ) { + $postId = UUID::create( $postId ); + } + + $stack = array( $this ); + while( $stack ) { + $post = array_pop( $stack ); + if ( $post->getPostId()->equals( $postId ) ) { + return $post; + } + foreach ( $post->getChildren() as $child ) { + $stack[] = $child; + } + } + + throw new \Exception( 'Requested postId is not available within post tree' ); + } + /** * Returns 1 if $this is newer than $rev, -1 is $rev is newer than * $this, and 0 if created at same moment. diff --git a/includes/Model/UUID.php b/includes/Model/UUID.php index 2aee92c..38553de 100644 --- a/includes/Model/UUID.php +++ b/includes/Model/UUID.php @@ -66,4 +66,8 @@ return $array; } + + public function equals( UUID $other ) { + return $other->getBinary() === $this->getBinary(); + } } diff --git a/includes/Model/Workflow.php b/includes/Model/Workflow.php index c16efa2..3f79665 100644 --- a/includes/Model/Workflow.php +++ b/includes/Model/Workflow.php @@ -90,7 +90,7 @@ if ( $this->wiki !== wfWikiId() ) { throw new \MWException( 'Interwiki not implemented' ); } - return Title::newFromText( $this->titleText, $this->namespace ); + return Title::makeTitleSafe( $this->namespace, $this->titleText ); } public function getId() { return $this->id; } diff --git a/includes/ParsoidUtils.php b/includes/ParsoidUtils.php new file mode 100644 index 0000000..3e4564c --- /dev/null +++ b/includes/ParsoidUtils.php @@ -0,0 +1,67 @@ +<?php + +namespace Flow; + +abstract class ParsoidUtils { + public static function convertWikitextToHtml5( $wikitext, $title ) { + global $wgFlowUseParsoid; + + if ( $wgFlowUseParsoid ) { + global $wgFlowParsoidURL, $wgFlowParsoidPrefix, $wgFlowParsoidTimeout; + + $parsoidOutput = \Http::post( + $wgFlowParsoidURL . '/_wikitext/' . $title->getPrefixedUrl(), + array( + 'postData' => array( + 'content' => $wikitext, + 'format' => 'html', + ), + 'timeout' => $wgFlowParsoidTimeout + ) + ); + + // Strip out the Parsoid boilerplate + $dom = new \DOMDocument(); + $dom->loadHTML( $parsoidOutput ); + $body = $dom->getElementsByTagName( 'body' )->item(0); + $html = ''; + + foreach( $body->childNodes as $child ) { + $html .= $child->ownerDocument->saveXML( $child ); + } + + return $html; + } else { + global $wgParser; + + $title = \Title::newFromText( 'Flow', NS_SPECIAL ); + + $options = new \ParserOptions; + $options->setTidy( true ); + $options->setEditSection( false ); + + $output = $wgParser->parse( $wikitext, $title, $options ); + return $output->getText(); + } + } + + public static function convertHtml5ToWikitext( $html ) { + global $wgFlowUseParsoid; + + if ( $wgFlowUseParsoid ) { + global $wgFlowParsoidURL, $wgFlowParsoidPrefix, $wgFlowParsoidTimeout; + + return \Http::post( + $wgFlowParsoidURL . '/' . $wgFlowParsoidPrefix . '/Flow', + array( + 'postData' => array( + 'content' => $html, + ), + 'timeout' => $wgFlowParsoidTimeout + ) + ); + } else { + throw new \MWException( "Editing posts is not supported without Parsoid" ); + } + } +} diff --git a/includes/Templating.php b/includes/Templating.php index 1bf457f..9be7e69 100644 --- a/includes/Templating.php +++ b/includes/Templating.php @@ -3,6 +3,7 @@ namespace Flow; use Flow\Block\Block; +use Flow\Block\TopicBlock; use Flow\Model\PostRevision; use Flow\Model\Workflow; use OutputPage; @@ -71,25 +72,23 @@ return $this->urlGenerator->generateUrl( $workflow, $action, $query ); } - public function renderPost( PostRevision $post, Block $block, PostRevision $root ) { + public function renderPost( PostRevision $post, Block $block, $return = true ) { return $this->render( 'flow:post.html.php', array( 'block' => $block, 'post' => $post, - 'root' => $root, ), - true + $return ); } - public function renderTopic( $block, $topic, $root, $user ) { + public function renderTopic( PostRevision $root, TopicBlock $block, $return = true ) { return $this->render( "flow:topic.html.php", array( 'block' => $block, - 'topic' => $topic, + 'topic' => $block->getWorkflow(), 'root' => $root, - 'user' => $user, - ), true ); + ), $return ); } } diff --git a/includes/UrlGenerator.php b/includes/UrlGenerator.php index 72fdd81..c476797 100644 --- a/includes/UrlGenerator.php +++ b/includes/UrlGenerator.php @@ -28,6 +28,6 @@ } return Title::newFromText( 'Flow/' . $workflow->getTitleFullText(), NS_SPECIAL ) - ->getFullURL( $query ); + ->getLinkURL( $query ); } } diff --git a/includes/api/ApiQueryFlow.php b/includes/api/ApiQueryFlow.php index 5059fd9..e8deac3 100644 --- a/includes/api/ApiQueryFlow.php +++ b/includes/api/ApiQueryFlow.php @@ -17,7 +17,7 @@ $passedParams = json_decode( $params['params'], true ); $pageTitle = Title::newFromText( $params['page'] ); - $id = $params['workflow'] ? new UUID( $params['workflow'] ) : null; + $id = $params['workflow'] ? UUID::create( $params['workflow'] ) : null; $this->loader = $this->container['factory.loader.workflow'] ->createWorkflowLoader( $pageTitle, $id ); diff --git a/modules/base/ext.flow.base.js b/modules/base/ext.flow.base.js index d224ff8..0ed82ef 100644 --- a/modules/base/ext.flow.base.js +++ b/modules/base/ext.flow.base.js @@ -59,6 +59,38 @@ ); }, + 'readTopic' : function( pageName, topicId, options ) { + var deferredObject = $.Deferred(); + + mw.flow.api.read( pageName, topicId, options ) + .done( function(output) { + // Immediate failure modes + if ( + ! output.query || + ! output.query.flow || + output.query.flow._element !== 'block' + ) { + deferredObject.fail( 'invalid-result', 'Unable to understand the API result' ); + return; + } + + $.each( output.query.flow, function( index, block ) { + // Looping through each block + if ( block['block-name'] === 'topic' ) { + // Return this block + deferredObject.resolve( block ); + } + } ); + + deferredObject.fail( 'invalid-result', 'Unable to find the topic block in the API result' ); + } ) + .fail( function() { + deferredObject.fail( arguments ); + } ); + + return deferredObject.promise(); + }, + 'generateTopicAction' : function( actionName, parameterList, promiseFilterCallback ) { return function( workflowId ) { var deferredObject = $.Deferred(); @@ -77,7 +109,7 @@ 'workflow' : workflowId }, actionName, - { 'topic_list' : + { 'topic' : requestParams }, true ).done( function(data) { @@ -90,12 +122,12 @@ ! data.flow || ! data.flow[actionName] || ! data.flow[actionName].result || - ! data.flow[actionName].result['topic_list'] + ! data.flow[actionName].result['topic'] ) { deferredObject.reject( 'invalid-result', 'Unable to find appropriate section in result' ); return; } - var output = data.flow[actionName].result['topic_list']; + var output = data.flow[actionName].result['topic']; deferredObject.resolve( output, data ); } ) @@ -169,5 +201,13 @@ 'content' ] ); + +mw.flow.api.editPost = mw.flow.api.generateTopicAction( + 'edit-post', + [ + 'postId', + 'content' + ] +); }); })( jQuery, mediaWiki ); \ No newline at end of file diff --git a/modules/discussion/base.css b/modules/discussion/base.css index af36a95..08de39f 100644 --- a/modules/discussion/base.css +++ b/modules/discussion/base.css @@ -913,4 +913,8 @@ .flow-edit-title-textbox { width: 70%; +} + +.flow-edit-post-form { + padding: 4px; } \ No newline at end of file diff --git a/modules/discussion/discussion.js b/modules/discussion/discussion.js index 8a9f681..42403eb 100644 --- a/modules/discussion/discussion.js +++ b/modules/discussion/discussion.js @@ -50,6 +50,20 @@ } }, + 'getTopicWorkflowId' : function( $element ) { + var $topicContainer = $element.closest( '.flow-topic-container' ); + var $container = $element.closest( '.flow-container' ); + + if ( $topicContainer.length ) { + return $topicContainer.data( 'topic-id' ); + } else if ( $container.length ) { + return $container.data( 'workflow-id' ); + } else { + console.dirxml( $element[0] ); + throw new Error( "Unable to get a workflow ID" ); + } + }, + 'setupFormHandler' : function( $container, submitSelector, @@ -259,9 +273,7 @@ function() { $form = $(this).closest( '.flow-reply-form' ); - var workflowId = $(this) - .closest( '.flow-topic-container' ) - .data( 'topic-id' ); + var workflowId = mw.flow.discussion.getTopicWorkflowId( $(this) ); var replyToId = $(this) .closest( '.flow-post-container' ) @@ -292,6 +304,128 @@ } ); + // Overload 'edit post' link. + $container.find( '.flow-action-edit-post a' ) + .click( function(e) { + e.preventDefault(); + var $postContainer = $(this).closest( '.flow-post' ); + var $contentContainer = $postContainer.find( '.flow-post-content' ); + var workflowId = mw.flow.discussion.getTopicWorkflowId( $(this) ); + var pageName = $(this).closest( '.flow-container' ).data( 'page-title' ); + var postId = $postContainer.data( 'post-id' ); + + if ( $postContainer.find( '.flow-edit-post-form' ).length ) { + return; + } + + mw.flow.api.readTopic( + pageName, + workflowId, + { + 'no-children' : true, + 'postId' : postId + } + ) + .done( function( data ) { + if ( ! data[0] || data[0]['post-id'] != postId ) { + console.dir( data ); + var $errorDiv = $( '<div/>' ) + .addClass( 'flow-error' ) + .text( mw.msg( 'flow-error-other') ) + .hide() + .insertAfter( $contentContainer ) + .slideDown(); + return; + } + + var originalContent = data[0]['content-src']['*']; + + var $postForm = $( '<form />' ) + .addClass( 'flow-edit-post-form' ); + + $postForm + .append( + $( '<textarea />' ) + .val( originalContent ) + .addClass( 'flow-edit-post-content' ) + ) + .append( + $( '<div/>' ) + .addClass( 'flow-post-controls' ) + .append( + $( '<a/>' ) + .text( mw.msg( 'flow-cancel' ) ) + .addClass( 'flow-cancel-link' ) + .addClass( 'mw-ui-destructive' ) + .attr( 'href', '#' ) + .click( function(e) { + e.preventDefault(); + $postForm.slideUp( 'fast', + function() { + $contentContainer.show(); + $postForm.remove(); + } + ); + } ) + ) + .append( + $( '<input />' ) + .attr( 'type', 'submit' ) + .addClass( 'mw-ui-button' ) + .addClass( 'mw-ui-primary' ) + .addClass( 'flow-edit-post-submit' ) + .val( mw.msg( 'flow-edit-post-submit' ) ) + ) + ) + .insertAfter( $contentContainer ); + + $contentContainer.hide(); + + mw.flow.discussion.setupFormHandler( + $postContainer, + '.flow-edit-post-submit', + mw.flow.api.editPost, + function() { + var content = $postForm.find( '.flow-edit-post-content' ).val(); + + return [ workflowId, postId, content ]; + }, + function( workflowId, postId, content ) { + return content; + }, + function( promise ) { + promise.done( function(output) { + $contentContainer + .empty() + .append( + $(output.rendered) + .find('.flow-post-content') + .children() + ); + } ); + } + ); + + mw.flow.discussion.setupEmptyDisabler( + 'form.flow-edit-post-form', + [ + '.flow-edit-post-content' + ], + '.flow-edit-post-submit' + ); + } ) + .fail( function() { + var $errorDiv = $( '<div/>' ) + .addClass( 'flow-error' ) + .hide(); + + mw.flow.discussion.handleError( $errorDiv, arguments ); + + $errorDiv.insertAfter( $contentContainer ) + .slideDown(); + } ); + } ); + // Overload 'edit title' link. $container.find( '.flow-action-edit-title a' ) .click( function(e) { diff --git a/templates/edit-post.html.php b/templates/edit-post.html.php new file mode 100644 index 0000000..e05b699 --- /dev/null +++ b/templates/edit-post.html.php @@ -0,0 +1,35 @@ +<?php + +echo Html::openElement( 'form', array( + 'method' => 'POST', + 'action' => $this->generateUrl( $topic->getId(), 'edit-post' ), +) ); +$editToken = $user->getEditToken( 'flow' ); +if ( $block->hasErrors() ) { + echo '<ul>'; + foreach ( $block->getErrors() as $error ) { + echo '<li>', $error->escaped() . '</li>'; // the pain ... + } + echo '</ul>'; +} + +$postWikitext = \Flow\ParsoidUtils::convertHtml5ToWikitext( $post->getContent() ); + +echo Html::element( 'input', array( + 'type' => 'hidden', + 'name' => 'wpEditToken', + 'value' => $user->getEditToken( 'flow' ), + ) ), + Html::element( 'input', array( + 'type' => 'hidden', + 'name' => $block->getName() . '[postId]', + 'value' => $post->getPostId()->getHex(), + ) ), + Html::textarea( $block->getName() . '[content]', $postWikitext ), + Html::element( 'input', array( + 'type' => 'submit', + 'class' => 'mw-ui-button mw-ui-primary', + 'value' => wfMessage( 'flow-edit-post-submit' )->plain() + ) ), + '</form>'; + diff --git a/templates/post.html.php b/templates/post.html.php index c5803fa..c4e925f 100644 --- a/templates/post.html.php +++ b/templates/post.html.php @@ -3,12 +3,12 @@ $editToken = $user->getEditToken( 'flow' ); $self = $this; -$postAction = function( $action, array $data = array(), $class = '' ) use( $self, $block, $root, $editToken ) { +$postAction = function( $action, array $data = array(), $class = '' ) use( $self, $block, $editToken ) { // actions that change things must be post requests $output = ''; $output .= Html::openElement( 'form', array( 'method' => 'POST', - 'action' => $self->generateUrl( $root->getPostId(), $action ) + 'action' => $self->generateUrl( $block->getWorkflowId(), $action ) ) ); $output .= Html::element( 'input', array( 'type' => 'hidden', 'name' => 'wpEditToken', 'value' => $editToken) ); foreach ( $data as $name => $value ) { @@ -27,8 +27,25 @@ return $output; }; -$renderPost = function( $post ) use( $self, $block, $root ) { - echo $self->renderPost( $post, $block, $root ); +$getAction = function( $action, $data = array(), $class = '' ) use ( $post, $self, $block ) { + $url = $self->generateUrl( + $block->getWorkflowId(), + $action, + array( + $block->getName() . '[postId]' => $post->getPostId()->getHex(), + ) + ); + return Html::element( 'a', + array( + 'href' => $url, + 'class' => $class, + ), + wfMessage( "flow-post-action-$action" )->plain() + ); +}; + +$renderPost = function( $post ) use( $self, $block ) { + echo $self->renderPost( $post, $block ); }; echo Html::openElement( 'div', array( @@ -57,7 +74,7 @@ $actions['restore'] = $postAction( 'restore-post', array( 'postId' => $post->getPostId()->getHex() ), 'mw-ui-constructive' ); $actions['history'] = Html::element( 'a', array( - 'href' => $self->generateUrl( $root->getPostId(), 'post-history', array( + 'href' => $self->generateUrl( $block->getWorkflowId(), 'post-history', array( $block->getName() . '[postId]' => $post->getPostId()->getHex(), ) ), ), wfMessage( 'flow-post-action-history' )->plain() ); @@ -65,15 +82,13 @@ $user = Html::element( 'span', null, $post->getUserText() ); $content = $post->getContent(); $actions['delete'] = $postAction( 'delete-post', array( 'postId' => $post->getPostId()->getHex() ), 'mw-ui-destructive' ); - $actions['history'] = Html::element( 'a', array( - 'href' => $self->generateUrl( $root->getPostId(), 'post-history', array( - $block->getName() . '[postId]' => $post->getPostId()->getHex(), - ) ), - ), wfMessage( 'flow-post-action-history' )->plain() ); + $actions['history'] = $getAction( 'post-history' ); + $actions['permalink'] = $getAction( 'view' ); + $actions['edit-post'] = $getAction( 'edit-post' ); $replyForm = Html::openElement( 'form', array( 'method' => 'POST', // root post id is same as topic workflow id - 'action' => $self->generateUrl( $root->getPostId(), 'reply' ), + 'action' => $self->generateUrl( $block->getWorkflowId(), 'reply' ), 'class' => 'flow-reply-form', ) ); $replyForm .= Html::element( 'input', array( 'type' => 'hidden', 'name' => 'wpEditToken', 'value' => $editToken) ); diff --git a/templates/topic.html.php b/templates/topic.html.php index 6b36bd8..2d24847 100644 --- a/templates/topic.html.php +++ b/templates/topic.html.php @@ -1,16 +1,4 @@ <?php -// yes, this is a horrible quick hack -// probably be better off if the templates were classes that were called as -// $template->render( $options ); -// or some such - -$self = $this; - -$editToken = $user->getEditToken( 'flow' ); - -$renderPost = function( $post ) use( $self, $block, $root, $user ) { - echo $self->renderPost( $post, $block, $root ); -}; $title = $root->getContent(); @@ -26,7 +14,7 @@ <div class="flow-titlebar"> <div class="flow-topic-title"> <div class="flow-realtitle"> -<?php echo htmlspecialchars( $title ); ?> + <?php echo htmlspecialchars( $title ); ?> </div> </div> <div class="flow-topiccontrols"> @@ -38,11 +26,26 @@ <div class="flow-actionbox-pokey"> </div> <div class="flow-topic-actionbox"> <ul> -<?php -echo Html::rawElement( 'li', array( 'class' => 'flow-action-edit-title' ), Html::rawElement( 'a', array( - 'href' => $this->generateUrl( $root->getPostId(), 'edit-title' ) - ), wfMessage( 'flow-topic-action-edit-title' ) ) ); -?> + <li class="flow-action-edit-title"> + <?php + echo Html::rawElement( 'a', + array( + 'href' => $this->generateUrl( $root->getPostId(), 'edit-title' ) + ), + wfMessage( 'flow-topic-action-edit-title' ) + ); + ?> + </li> + <li class="flow-action-topic-history"> + <?php + echo Html::rawElement( 'a', + array( + 'href' => $this->generateUrl( $root->getPostId(), 'topic-history' ) + ), + wfMessage( 'flow-topic-action-history' ) + ); + ?> + </li> </ul> </div> </div> @@ -54,7 +57,7 @@ <?php foreach( $root->getChildren() as $child ) { - $renderPost( $child ); + echo $this->renderPost( $child, $block, $root ); } ?> </div> -- To view, visit https://gerrit.wikimedia.org/r/78933 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: merged Gerrit-Change-Id: Ie62ff67ae09ec131ae49acce3ffe779311203842 Gerrit-PatchSet: 7 Gerrit-Project: mediawiki/extensions/Flow Gerrit-Branch: master Gerrit-Owner: EBernhardson (WMF) <ebernhard...@wikimedia.org> Gerrit-Reviewer: EBernhardson (WMF) <ebernhard...@wikimedia.org> Gerrit-Reviewer: Matthias Mullie <mmul...@wikimedia.org> Gerrit-Reviewer: Werdna <agarr...@wikimedia.org> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits