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">&nbsp;</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

Reply via email to