Sbisson has uploaded a new change for review.
https://gerrit.wikimedia.org/r/234603
Change subject: [WIP] Edit post widget
......................................................................
[WIP] Edit post widget
Bug: T109000
Change-Id: Id429472cc45224ba1758989d68a2cd4c917d1b1b
---
M Resources.php
M handlebars/compiled/flow_block_topic.handlebars.php
M handlebars/compiled/flow_block_topiclist.handlebars.php
M handlebars/compiled/flow_post.handlebars.php
M handlebars/flow_moderation_actions_list.partial.handlebars
M modules/engine/components/board/base/flow-board-interactive-events.js
M modules/flow-initialize.js
M modules/flow/dm/api/mw.flow.dm.APIHandler.js
A modules/flow/ui/widgets/mw.flow.ui.EditPostWidget.js
M tests/browser/features/edit_existing.feature
M tests/browser/features/step_definitions/edit_existing_steps.rb
M tests/browser/features/support/pages/abstract_flow_page.rb
12 files changed, 273 insertions(+), 28 deletions(-)
git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/Flow
refs/changes/03/234603/1
diff --git a/Resources.php b/Resources.php
index fdbaa46..c6202bf 100644
--- a/Resources.php
+++ b/Resources.php
@@ -381,6 +381,7 @@
'flow/ui/widgets/mw.flow.ui.ReorderTopicsWidget.js',
'flow/ui/widgets/mw.flow.ui.NavigationWidget.js',
'flow/ui/widgets/mw.flow.ui.ReplyWidget.js',
+ 'flow/ui/widgets/mw.flow.ui.EditPostWidget.js',
'flow/ui/widgets/mw.flow.ui.SidebarExpandWidget.js',
'flow/ui/widgets/mw.flow.ui.NewTopicWidget.js',
diff --git a/handlebars/compiled/flow_block_topic.handlebars.php
b/handlebars/compiled/flow_block_topic.handlebars.php
index a0bfd9d..bb6cf6e 100644
--- a/handlebars/compiled/flow_block_topic.handlebars.php
+++ b/handlebars/compiled/flow_block_topic.handlebars.php
@@ -107,9 +107,7 @@
'.$sp.' <a
class="'.htmlentities((string)((isset($in['moderationMwUiClass']) &&
is_array($in)) ? $in['moderationMwUiClass'] : null), ENT_QUOTES, 'UTF-8').'
mw-ui-progressive mw-ui-quiet mw-ui-hovericon"
'.$sp.'
href="'.htmlentities((string)((isset($in['actions']['edit']['url']) &&
is_array($in['actions']['edit'])) ? $in['actions']['edit']['url'] : null),
ENT_QUOTES, 'UTF-8').'"
'.$sp.'
title="'.htmlentities((string)((isset($in['actions']['edit']['title']) &&
is_array($in['actions']['edit'])) ? $in['actions']['edit']['title'] : null),
ENT_QUOTES, 'UTF-8').'"
-'.$sp.'
data-flow-api-handler="activateEditPost"
-'.$sp.' data-flow-api-target="<
.flow-post-main"
-'.$sp.'
data-flow-interactive-handler="apiRequest"
+'.$sp.'
data-flow-interactive-handler="editPost"
'.$sp.' >'.((LCRun3::ifvar($cx,
((isset($in['moderationIcons']) && is_array($in)) ? $in['moderationIcons'] :
null))) ? '<span class="mw-ui-icon mw-ui-icon-before mw-ui-icon-edit
mw-ui-icon-edit-progressive-hover"></span> ' : '').''.LCRun3::ch($cx, 'l10n',
array(array('flow-post-action-edit-post'),array()), 'encq').'</a>
'.$sp.' </li>' : '').''.((LCRun3::ifvar($cx,
((isset($in['links']['post']) && is_array($in['links'])) ? $in['links']['post']
: null))) ? '<li>'.htmlentities((string)((isset($in['noop']) && is_array($in))
? $in['noop'] : null), ENT_QUOTES, 'UTF-8').'<a
class="'.htmlentities((string)((isset($in['moderationMwUiClass']) &&
is_array($in)) ? $in['moderationMwUiClass'] : null), ENT_QUOTES, 'UTF-8').'
mw-ui-quiet"
'.$sp.'
href="'.htmlentities((string)((isset($in['links']['post']['url']) &&
is_array($in['links']['post'])) ? $in['links']['post']['url'] : null),
ENT_QUOTES, 'UTF-8').'"
diff --git a/handlebars/compiled/flow_block_topiclist.handlebars.php
b/handlebars/compiled/flow_block_topiclist.handlebars.php
index 7803995..690d0d3 100644
--- a/handlebars/compiled/flow_block_topiclist.handlebars.php
+++ b/handlebars/compiled/flow_block_topiclist.handlebars.php
@@ -157,9 +157,7 @@
'.$sp.' <a
class="'.htmlentities((string)((isset($in['moderationMwUiClass']) &&
is_array($in)) ? $in['moderationMwUiClass'] : null), ENT_QUOTES, 'UTF-8').'
mw-ui-progressive mw-ui-quiet mw-ui-hovericon"
'.$sp.'
href="'.htmlentities((string)((isset($in['actions']['edit']['url']) &&
is_array($in['actions']['edit'])) ? $in['actions']['edit']['url'] : null),
ENT_QUOTES, 'UTF-8').'"
'.$sp.'
title="'.htmlentities((string)((isset($in['actions']['edit']['title']) &&
is_array($in['actions']['edit'])) ? $in['actions']['edit']['title'] : null),
ENT_QUOTES, 'UTF-8').'"
-'.$sp.'
data-flow-api-handler="activateEditPost"
-'.$sp.' data-flow-api-target="<
.flow-post-main"
-'.$sp.'
data-flow-interactive-handler="apiRequest"
+'.$sp.'
data-flow-interactive-handler="editPost"
'.$sp.' >'.((LCRun3::ifvar($cx,
((isset($in['moderationIcons']) && is_array($in)) ? $in['moderationIcons'] :
null))) ? '<span class="mw-ui-icon mw-ui-icon-before mw-ui-icon-edit
mw-ui-icon-edit-progressive-hover"></span> ' : '').''.LCRun3::ch($cx, 'l10n',
array(array('flow-post-action-edit-post'),array()), 'encq').'</a>
'.$sp.' </li>' : '').''.((LCRun3::ifvar($cx,
((isset($in['links']['post']) && is_array($in['links'])) ? $in['links']['post']
: null))) ? '<li>'.htmlentities((string)((isset($in['noop']) && is_array($in))
? $in['noop'] : null), ENT_QUOTES, 'UTF-8').'<a
class="'.htmlentities((string)((isset($in['moderationMwUiClass']) &&
is_array($in)) ? $in['moderationMwUiClass'] : null), ENT_QUOTES, 'UTF-8').'
mw-ui-quiet"
'.$sp.'
href="'.htmlentities((string)((isset($in['links']['post']['url']) &&
is_array($in['links']['post'])) ? $in['links']['post']['url'] : null),
ENT_QUOTES, 'UTF-8').'"
diff --git a/handlebars/compiled/flow_post.handlebars.php
b/handlebars/compiled/flow_post.handlebars.php
index a0ac9d0..bce3afe 100644
--- a/handlebars/compiled/flow_post.handlebars.php
+++ b/handlebars/compiled/flow_post.handlebars.php
@@ -97,9 +97,7 @@
'.$sp.' <a
class="'.htmlentities((string)((isset($in['moderationMwUiClass']) &&
is_array($in)) ? $in['moderationMwUiClass'] : null), ENT_QUOTES, 'UTF-8').'
mw-ui-progressive mw-ui-quiet mw-ui-hovericon"
'.$sp.'
href="'.htmlentities((string)((isset($in['actions']['edit']['url']) &&
is_array($in['actions']['edit'])) ? $in['actions']['edit']['url'] : null),
ENT_QUOTES, 'UTF-8').'"
'.$sp.'
title="'.htmlentities((string)((isset($in['actions']['edit']['title']) &&
is_array($in['actions']['edit'])) ? $in['actions']['edit']['title'] : null),
ENT_QUOTES, 'UTF-8').'"
-'.$sp.'
data-flow-api-handler="activateEditPost"
-'.$sp.' data-flow-api-target="<
.flow-post-main"
-'.$sp.'
data-flow-interactive-handler="apiRequest"
+'.$sp.'
data-flow-interactive-handler="editPost"
'.$sp.' >'.((LCRun3::ifvar($cx,
((isset($in['moderationIcons']) && is_array($in)) ? $in['moderationIcons'] :
null))) ? '<span class="mw-ui-icon mw-ui-icon-before mw-ui-icon-edit
mw-ui-icon-edit-progressive-hover"></span> ' : '').''.LCRun3::ch($cx, 'l10n',
array(array('flow-post-action-edit-post'),array()), 'encq').'</a>
'.$sp.' </li>' : '').''.((LCRun3::ifvar($cx,
((isset($in['links']['post']) && is_array($in['links'])) ? $in['links']['post']
: null))) ? '<li>'.htmlentities((string)((isset($in['noop']) && is_array($in))
? $in['noop'] : null), ENT_QUOTES, 'UTF-8').'<a
class="'.htmlentities((string)((isset($in['moderationMwUiClass']) &&
is_array($in)) ? $in['moderationMwUiClass'] : null), ENT_QUOTES, 'UTF-8').'
mw-ui-quiet"
'.$sp.'
href="'.htmlentities((string)((isset($in['links']['post']['url']) &&
is_array($in['links']['post'])) ? $in['links']['post']['url'] : null),
ENT_QUOTES, 'UTF-8').'"
diff --git a/handlebars/flow_moderation_actions_list.partial.handlebars
b/handlebars/flow_moderation_actions_list.partial.handlebars
index a592c3b..ca7f82f 100644
--- a/handlebars/flow_moderation_actions_list.partial.handlebars
+++ b/handlebars/flow_moderation_actions_list.partial.handlebars
@@ -69,9 +69,7 @@
<a class="{{moderationMwUiClass}}
mw-ui-progressive mw-ui-quiet mw-ui-hovericon"
href="{{actions.edit.url}}"
title="{{actions.edit.title}}"
- data-flow-api-handler="activateEditPost"
- data-flow-api-target="< .flow-post-main"
- data-flow-interactive-handler="apiRequest"
+ data-flow-interactive-handler="editPost"
>
{{~#if moderationIcons}}<span
class="mw-ui-icon mw-ui-icon-before mw-ui-icon-edit
mw-ui-icon-edit-progressive-hover"></span> {{/if~}}
{{~l10n "flow-post-action-edit-post"~}}
diff --git
a/modules/engine/components/board/base/flow-board-interactive-events.js
b/modules/engine/components/board/base/flow-board-interactive-events.js
index 894b9bb..d108e12 100644
--- a/modules/engine/components/board/base/flow-board-interactive-events.js
+++ b/modules/engine/components/board/base/flow-board-interactive-events.js
@@ -24,6 +24,42 @@
// interactive handlers
//
+
FlowBoardComponentInteractiveEventsMixin.UI.events.interactiveHandlers.editPost
= function ( event ) {
+ var editPostWidget,
+ $topic = $( this ).closest( '.flow-topic' ),
+ topicId = $topic.data( 'flow-id' ),
+ $post = $( this ).closest( '.flow-post' ),
+ $postMain = $post.find( '.flow-post-main' ),
+ postId = $post.data( 'flow-id' ),
+ $board = $( '.flow-board' ),
+ flowBoard = mw.flow.getPrototypeMethod( 'component',
'getInstanceByElement' )( $board );
+
+ editPostWidget = new mw.flow.ui.EditPostWidget( topicId, postId
);
+ editPostWidget
+ .on( 'saveContent', function ( workflow ) {
+ editPostWidget.destroy();
+ editPostWidget.$element.remove();
+
+ $topic.addClass( 'flow-api-inprogress' );
+ // HACK get the old system to rerender the topic
+ return flowBoard.flowBoardComponentRefreshTopic(
+ $topic,
+ topicId
+ )
+ .always( function () {
+ $topic.removeClass(
'flow-api-inprogress' );
+ } );
+ } )
+ .on( 'cancel', function () {
+ editPostWidget.$element.replaceWith( $postMain
);
+ editPostWidget.destroy();
+ } );
+
+ $postMain.replaceWith( editPostWidget.$element );
+
+ event.preventDefault();
+ };
+
/**
* Toggles collapse state
*
diff --git a/modules/flow-initialize.js b/modules/flow-initialize.js
index 7ba7515..4712898 100644
--- a/modules/flow-initialize.js
+++ b/modules/flow-initialize.js
@@ -94,12 +94,6 @@
// We shouldn't have to worry about 'remove', since by the time
we have filtering,
// orderedTopicIds should be gone.
- // HACK: On load more, populate the board dm
- flowBoard.on( 'loadmore', function ( topiclist ) {
- // Add to the DM board
- mw.flow.system.populateBoardTopicsFromJson( topiclist );
- } );
-
// Initialize the old system to accept the default
// 'newest' order for the topic order widget
// Get the current default sort
diff --git a/modules/flow/dm/api/mw.flow.dm.APIHandler.js
b/modules/flow/dm/api/mw.flow.dm.APIHandler.js
index 13b4bbc..1c80ec6 100644
--- a/modules/flow/dm/api/mw.flow.dm.APIHandler.js
+++ b/modules/flow/dm/api/mw.flow.dm.APIHandler.js
@@ -227,4 +227,54 @@
return xhr.promise( { abort: xhr.abort } );
};
+
+ /**
+ * Get a post.
+ *
+ * @param {string} postId
+ * @param {string} format
+ * @return {jQuery.Promise} Promise that is resolved with the post
revision data
+ */
+ mw.flow.dm.APIHandler.prototype.getPost = function ( postId, format ) {
+ var params = {
+ page: this.page,
+ vppostId: postId,
+ vpformat: format
+ };
+
+ return this.get( 'view-post', params )
+ .then( function ( data ) {
+ return data.topic.revisions[postId];
+ } );
+ };
+
+ /**
+ * Save a post.
+ *
+ * @param {string} postId
+ * @param {string} content
+ * @param {string} format
+ * @param {string} [captcha] CAPTCHA information
+ * @return {jQuery.Promise} Promise that is resolved with the saved
post revision id
+ */
+ mw.flow.dm.APIHandler.prototype.savePost = function ( postId, content,
format, captcha ) {
+ var xhr,
+ params = {
+ page: this.page,
+ epcontent: content,
+ epformat: format,
+ epprev_revision: this.currentRevision,
+ eppostId: postId
+ };
+
+ this.addCaptcha( params, captcha );
+
+ xhr = this.postEdit( 'edit-post', params )
+ .then( function ( data ) {
+ return OO.getProp( data.flow, 'edit-post',
'committed', 'topic', 'post-revision-id' );
+ } );
+
+ return xhr.promise( { abort: xhr.abort } );
+ };
+
}( jQuery ) );
diff --git a/modules/flow/ui/widgets/mw.flow.ui.EditPostWidget.js
b/modules/flow/ui/widgets/mw.flow.ui.EditPostWidget.js
new file mode 100644
index 0000000..6f24a14
--- /dev/null
+++ b/modules/flow/ui/widgets/mw.flow.ui.EditPostWidget.js
@@ -0,0 +1,167 @@
+( function ( $ ) {
+ /**
+ * Flow reply widget
+ *
+ * @class
+ * @extends OO.ui.Widget
+ *
+ * @constructor
+ * @param {string} postId The id of the post to edit
+ * @param {Object} [config] Configuration object
+ */
+ mw.flow.ui.EditPostWidget = function mwFlowUiEditPostWidget( topicId,
postId, config ) {
+ config = config || {};
+
+ this.topicId = topicId;
+ this.postId = postId;
+
+ // Parent constructor
+ mw.flow.ui.EditPostWidget.parent.call( this, config );
+
+ this.editor = new mw.flow.ui.EditorWidget( {
+ saveMsgKey: 'flow-post-action-edit-post-submit',
+ classes: [ 'flow-ui-editPostWidget-editor' ]
+ } );
+ this.editor.toggle( true );
+
+ this.anonWarning = new mw.flow.ui.AnonWarningWidget();
+ this.anonWarning.toggle( true );
+
+ this.error = new OO.ui.LabelWidget( {
+ classes: [ 'flow-ui-editPostWidget-error flow-errors
errorbox' ]
+ } );
+ this.error.toggle( false );
+
+ this.api = new mw.flow.dm.APIHandler(
+ 'Topic:' + topicId
+ );
+
+ // Events
+ this.editor.connect( this, {
+ saveContent: 'onEditorSaveContent',
+ cancel: 'onEditorCancel'
+ } );
+
+ this.$element
+ .addClass( 'flow-ui-editPostWidget' )
+ .append(
+ this.anonWarning.$element,
+ this.error.$element,
+ this.editor.$element
+ );
+
+ // Load the editor
+ this.editor.pushPending();
+ this.editor.activate();
+
+ // Get the post from the API
+ var widget = this,
+ contentFormat = this.editor.getContentFormat() ||
'wikitext';
+
+ this.api.getPost( postId, contentFormat ).then(
+ function ( post ) {
+ var content = OO.getProp( post, 'content',
'content' ),
+ format = OO.getProp( post, 'content',
'format' );
+
+ if ( content !== undefined && format !==
undefined ) {
+ // Give it to the editor
+ widget.editor.setContent( content,
format );
+
+ // Update revisionId in the API
+ widget.api.setCurrentRevision(
post.revisionId );
+ }
+
+ },
+ // Error fetching description
+ function ( error ) {
+ // Display error
+ widget.error.setLabel( mw.msg(
'flow-error-external', error ) );
+ widget.error.toggle( true );
+ }
+ ).always( function () {
+ // Unset pending editor
+ widget.editor.popPending();
+ // Focus again: pending editors are disabled and can't
be focused
+ widget.editor.focus();
+ } );
+
+ };
+
+ /* Initialization */
+
+ OO.inheritClass( mw.flow.ui.EditPostWidget, OO.ui.Widget );
+
+ /* Events */
+
+ /**
+ * Save the content of the reply
+ * @event saveContent
+ * @param {string} workflow The workflow this reply was saved under
+ * @param {string} content The content of the reply
+ * @param {string} contentFormat The format of the content of this reply
+ */
+
+ /* Methods */
+
+ /**
+ * Respond to editor cancel
+ */
+ mw.flow.ui.EditPostWidget.prototype.onEditorCancel = function () {
+ this.emit( 'cancel' );
+ };
+
+ /**
+ * Respond to editor save
+ */
+ mw.flow.ui.EditPostWidget.prototype.onEditorSaveContent = function (
content, format ) {
+ var widget = this,
+ $captchaField, captcha;
+
+ $captchaField = this.error.$label.find(
'[name="wpCaptchaWord"]' );
+ if ( $captchaField.length > 0 ) {
+ captcha = {
+ id: this.error.$label.find(
'[name="wpCaptchaId"]' ).val(),
+ answer: $captchaField.val()
+ };
+ }
+
+ this.error.setLabel( '' );
+ this.error.toggle( false );
+
+ this.editor.pushPending();
+ this.api.savePost( this.postId, content, format, captcha )
+ .then( function ( workflow ) {
+ widget.emit( 'saveContent', workflow, content,
format );
+ } )
+ .then( null, function ( errorCode, errorObj ) {
+ if ( /spamfilter$/.test( errorCode ) &&
errorObj.error.spamfilter === 'flow-spam-confirmedit-form' ) {
+ widget.error.setLabel(
+ // CAPTCHA form
+ new OO.ui.HtmlSnippet(
errorObj.error.info )
+ );
+ } else {
+ widget.error.setLabel( errorObj.error
&& errorObj.error.info || errorObj.exception );
+ }
+
+ widget.error.toggle( true );
+ } )
+ .always( function () {
+ widget.editor.popPending();
+ } );
+ };
+
+ /**
+ * Focus the reply widget on the editor
+ */
+ mw.flow.ui.EditPostWidget.prototype.focus = function () {
+ this.editor.focus();
+ };
+
+ /**
+ * Destroy the widget
+ */
+ mw.flow.ui.EditPostWidget.prototype.destroy = function () {
+ this.editor.destroy();
+ };
+
+}( jQuery ) );
diff --git a/tests/browser/features/edit_existing.feature
b/tests/browser/features/edit_existing.feature
index 9c62ef7..5c1282a 100644
--- a/tests/browser/features/edit_existing.feature
+++ b/tests/browser/features/edit_existing.feature
@@ -9,7 +9,7 @@
Scenario: Edit an existing title
Given I am on Flow page
When I select the Edit title action
- And I edit the title field with Title edited
+ And I edit the title field with "Title edited"
And I save the new title
Then the top post should have a heading which contains "Title edited"
@@ -17,6 +17,6 @@
Given I am logged in
And I am on Flow page
When I select Edit post
- And I edit the post field with Post edited
+ And I edit the post field with "Post edited"
And I save the new post
- Then the saved post should contain Post edited
+ Then the saved post should contain "Post edited"
diff --git a/tests/browser/features/step_definitions/edit_existing_steps.rb
b/tests/browser/features/step_definitions/edit_existing_steps.rb
index eaaee3b..a9d9bc8 100644
--- a/tests/browser/features/step_definitions/edit_existing_steps.rb
+++ b/tests/browser/features/step_definitions/edit_existing_steps.rb
@@ -14,7 +14,7 @@
end
end
-When(/^I edit the post field with (.+)$/) do |edited_post|
+When(/^I edit the post field with "(.+)"$/) do |edited_post|
on(FlowPage) do |page|
# Take focus away from menu
page.post_edit_element.when_present.click
@@ -22,7 +22,7 @@
end
end
-When(/^I edit the title field with (.+)$/) do |edited_title|
+When(/^I edit the title field with "(.+)"$/) do |edited_title|
on(FlowPage) do |page|
@edited_topic_string = edited_title + @random_string
# Take focus away from menu
@@ -45,6 +45,6 @@
end
end
-Then(/^the saved post should contain (.+)$/) do |edited_post|
- expect(on(FlowPage).flow_first_topic_body).to match(edited_post +
@random_string)
+Then(/^the saved post should contain "(.+)"$/) do |edited_post|
+ expect(on(FlowPage).flow_first_topic_body_element.when_present.text).to
match(edited_post + @random_string)
end
diff --git a/tests/browser/features/support/pages/abstract_flow_page.rb
b/tests/browser/features/support/pages/abstract_flow_page.rb
index 6a5ad0d..262412f 100644
--- a/tests/browser/features/support/pages/abstract_flow_page.rb
+++ b/tests/browser/features/support/pages/abstract_flow_page.rb
@@ -46,7 +46,9 @@
div(:flow_first_topic, css: ".flow-topic", index: 0)
h2(:flow_first_topic_heading, css: ".flow-topic h2", index: 0)
# todo this is poor naming, it's really the first_topic_first_post_content
- div(:flow_first_topic_body, css: ".flow-topic .flow-post-content", index: 0)
+ div(:flow_first_topic_body) do
+ div_element(class: "flow-topic", index: 0).div_element(class:
'flow-post-content')
+ end
div(:flow_first_topic_moderation_msg) do |page|
page.flow_first_topic_element.div_element(css: "div.flow-topic-titlebar
div.flow-moderated-topic-title")
end
@@ -271,11 +273,14 @@
# Editing post workflow
+ div(:edit_post_widget, class: 'flow-ui-editPostWidget')
def post_edit_element
- visualeditor_or_textarea '.flow-edit-post-form'
+ visualeditor_or_textarea edit_post_widget_element
end
- button(:change_post_save, css: ".flow-edit-post-form .mw-ui-constructive")
+ button(:change_post_save) do
+ edit_post_widget_element.link_element(text: 'Save changes')
+ end
div(:small_spinner, class: "mw-spinner mw-spinner-small mw-spinner-inline")
--
To view, visit https://gerrit.wikimedia.org/r/234603
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: Id429472cc45224ba1758989d68a2cd4c917d1b1b
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/Flow
Gerrit-Branch: master
Gerrit-Owner: Sbisson <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits