jenkins-bot has submitted this change and it was merged. Change subject: Refactor collapsing to handle moderated posts ......................................................................
Refactor collapsing to handle moderated posts * Get rid of flow-topic-collapsed-invert * Use flow-topic-expanded and flow-topic-collapsed * Save + (expanded) or - (collapsed) to storage, since there is no general toggle class any more * Remove flow-topic-collapsed when explicitly overriden, either by a user click or session storage (essentially a past click) * Don't remove collapsed class of moderated posts when switching the board's overall collapse mode * Add browser tests for the above See https://trello.com/c/ZR3a5BAF/282-b-171-issues-with-closed-topics-in-collapsed-views Change-Id: I1d8274d7306bc318f693a6a10b712e56219ea25c --- M handlebars/compiled/flow_block_topic.handlebars.php M handlebars/compiled/flow_block_topiclist.handlebars.php M handlebars/flow_topic.handlebars M modules/new/components/flow-board.js M modules/new/styles/interactive.less M tests/browser/features/collapse.feature M tests/browser/features/step_definitions/collapse_steps.rb M tests/browser/features/step_definitions/flow_steps.rb M tests/browser/features/step_definitions/moderation_steps.rb M tests/browser/features/support/pages/flow_page.rb 10 files changed, 240 insertions(+), 67 deletions(-) Approvals: SG: Looks good to me, approved jenkins-bot: Verified diff --git a/handlebars/compiled/flow_block_topic.handlebars.php b/handlebars/compiled/flow_block_topic.handlebars.php index 7803666..71f715d 100644 --- a/handlebars/compiled/flow_block_topic.handlebars.php +++ b/handlebars/compiled/flow_block_topic.handlebars.php @@ -46,7 +46,7 @@ '.LCRun3::sec($cx, ((is_array($in) && isset($in['roots'])) ? $in['roots'] : null), $in, true, function($cx, $in) {return ' '.LCRun3::hbch($cx, 'eachPost', Array($cx['scopes'][0],$in), $in, function($cx, $in) {return ' - <div class="flow-topic '.((!LCRun3::ifvar($cx, ((is_array($in) && isset($in['isPreview'])) ? $in['isPreview'] : null))) ? 'flow-load-interactive' : '').' '.((LCRun3::ifvar($cx, ((is_array($in) && isset($in['isModerated'])) ? $in['isModerated'] : null))) ? 'flow-topic-moderated flow-topic-collapsed-invert' : '').'" id="flow-topic-'.htmlentities(((is_array($in) && isset($in['postId'])) ? $in['postId'] : null), ENT_QUOTES, 'UTF-8').'" data-flow-id="'.htmlentities(((is_array($in) && isset($in['postId'])) ? $in['postId'] : null), ENT_QUOTES, 'UTF-8').'" data-flow-load-handler="topicElement"> + <div class="flow-topic '.((!LCRun3::ifvar($cx, ((is_array($in) && isset($in['isPreview'])) ? $in['isPreview'] : null))) ? 'flow-load-interactive' : '').' '.((LCRun3::ifvar($cx, ((is_array($in) && isset($in['isModerated'])) ? $in['isModerated'] : null))) ? 'flow-topic-moderated flow-topic-collapsed' : '').'" id="flow-topic-'.htmlentities(((is_array($in) && isset($in['postId'])) ? $in['postId'] : null), ENT_QUOTES, 'UTF-8').'" data-flow-id="'.htmlentities(((is_array($in) && isset($in['postId'])) ? $in['postId'] : null), ENT_QUOTES, 'UTF-8').'" data-flow-load-handler="topicElement"> <div class="flow-topic-titlebar flow-click-interactive" data-flow-interactive-handler="topicCollapserToggle" tabindex="0"> <h2 class="flow-topic-title" data-title="'.htmlentities(((is_array($in) && isset($in['content'])) ? $in['content'] : null), ENT_QUOTES, 'UTF-8').'">'.htmlentities(((is_array($in) && isset($in['content'])) ? $in['content'] : null), ENT_QUOTES, 'UTF-8').'</h2> '.((LCRun3::ifvar($cx, ((is_array($in) && isset($in['isModerated'])) ? $in['isModerated'] : null))) ? ' @@ -300,4 +300,4 @@ </div> '; } -?> \ No newline at end of file +?> diff --git a/handlebars/compiled/flow_block_topiclist.handlebars.php b/handlebars/compiled/flow_block_topiclist.handlebars.php index f6cc1ed..8596e78 100644 --- a/handlebars/compiled/flow_block_topiclist.handlebars.php +++ b/handlebars/compiled/flow_block_topiclist.handlebars.php @@ -102,7 +102,7 @@ '.LCRun3::sec($cx, ((is_array($in) && isset($in['roots'])) ? $in['roots'] : null), $in, true, function($cx, $in) {return ' '.LCRun3::hbch($cx, 'eachPost', Array($cx['scopes'][0],$in), $in, function($cx, $in) {return ' - <div class="flow-topic '.((!LCRun3::ifvar($cx, ((is_array($in) && isset($in['isPreview'])) ? $in['isPreview'] : null))) ? 'flow-load-interactive' : '').' '.((LCRun3::ifvar($cx, ((is_array($in) && isset($in['isModerated'])) ? $in['isModerated'] : null))) ? 'flow-topic-moderated flow-topic-collapsed-invert' : '').'" id="flow-topic-'.htmlentities(((is_array($in) && isset($in['postId'])) ? $in['postId'] : null), ENT_QUOTES, 'UTF-8').'" data-flow-id="'.htmlentities(((is_array($in) && isset($in['postId'])) ? $in['postId'] : null), ENT_QUOTES, 'UTF-8').'" data-flow-load-handler="topicElement"> + <div class="flow-topic '.((!LCRun3::ifvar($cx, ((is_array($in) && isset($in['isPreview'])) ? $in['isPreview'] : null))) ? 'flow-load-interactive' : '').' '.((LCRun3::ifvar($cx, ((is_array($in) && isset($in['isModerated'])) ? $in['isModerated'] : null))) ? 'flow-topic-moderated flow-topic-collapsed' : '').'" id="flow-topic-'.htmlentities(((is_array($in) && isset($in['postId'])) ? $in['postId'] : null), ENT_QUOTES, 'UTF-8').'" data-flow-id="'.htmlentities(((is_array($in) && isset($in['postId'])) ? $in['postId'] : null), ENT_QUOTES, 'UTF-8').'" data-flow-load-handler="topicElement"> <div class="flow-topic-titlebar flow-click-interactive" data-flow-interactive-handler="topicCollapserToggle" tabindex="0"> <h2 class="flow-topic-title" data-title="'.htmlentities(((is_array($in) && isset($in['content'])) ? $in['content'] : null), ENT_QUOTES, 'UTF-8').'">'.htmlentities(((is_array($in) && isset($in['content'])) ? $in['content'] : null), ENT_QUOTES, 'UTF-8').'</h2> '.((LCRun3::ifvar($cx, ((is_array($in) && isset($in['isModerated'])) ? $in['isModerated'] : null))) ? ' @@ -365,4 +365,4 @@ </div> '; } -?> \ No newline at end of file +?> diff --git a/handlebars/flow_topic.handlebars b/handlebars/flow_topic.handlebars index e045a41..4bf6f92 100644 --- a/handlebars/flow_topic.handlebars +++ b/handlebars/flow_topic.handlebars @@ -1,4 +1,4 @@ -<div class="flow-topic {{#unless isPreview}}flow-load-interactive{{/unless}} {{#if isModerated}}flow-topic-moderated flow-topic-collapsed-invert{{/if}}" id="flow-topic-{{postId}}" data-flow-id="{{postId}}" data-flow-load-handler="topicElement"> +<div class="flow-topic {{#unless isPreview}}flow-load-interactive{{/unless}} {{#if isModerated}}flow-topic-moderated flow-topic-collapsed{{/if}}" id="flow-topic-{{postId}}" data-flow-id="{{postId}}" data-flow-load-handler="topicElement"> {{>flow_topic_titlebar}} {{#if @root.posts}} diff --git a/modules/new/components/flow-board.js b/modules/new/components/flow-board.js index 3fd4a55..be1ae42 100644 --- a/modules/new/components/flow-board.js +++ b/modules/new/components/flow-board.js @@ -969,12 +969,28 @@ */ FlowBoardComponent.UI.events.loadHandlers.topicElement = function ( $topic ) { // Get last collapse state from sessionStorage - var states = mw.flow.StorageEngine.sessionStorage.getItem( 'topicCollapserStates' ) || {}, - topicId = $topic.data('flow-id'); + var stateForTopic, classForTopic, + states = mw.flow.StorageEngine.sessionStorage.getItem( 'topicCollapserStates' ) || {}, + topicId = $topic.data('flow-id'), + STORAGE_TO_CLASS = { + // Conserve space in browser storage + '+': 'flow-topic-expanded', + '-': 'flow-topic-collapsed' + }; - if ( states[ topicId ] ) { - // This item was previously collapse-inverted, so reapply the class - $topic.addClass( 'flow-topic-collapsed-invert' ); + stateForTopic = states[ topicId ]; + if ( stateForTopic ) { + // This item has an visibility override previously, so reapply the class + + classForTopic = STORAGE_TO_CLASS[stateForTopic]; + + if ( classForTopic === 'flow-topic-expanded' ) { + // Remove flow-topic-collapsed first (can be set on server for moderated), so it + // doesn't clash. + $topic.removeClass( 'flow-topic-collapsed' ); + } + + $topic.addClass( classForTopic ); } }; @@ -1113,23 +1129,42 @@ }; /** - * Toggles the flow-topic-expanded class on .flow-topic. + * Sets the visibility class based on the user toggle action. * @param {Event} event */ FlowBoardComponent.UI.events.interactiveHandlers.topicCollapserToggle = function ( event ) { var $target = $( event.target ), - $topic, topicId, states; + $topic, topicId, states, + $component = $( this ).closest( '.flow-component' ), + overrideClass; // Make sure we didn't click on any interactive elements if ( $target.not( '.flow-menu-js-drop' ) && !$target.closest( 'a, button, input, textarea, select, ul, ol' ).length ) { - // Toggle the class - $topic = $( this ).closest( '.flow-topic' ).toggleClass( 'flow-topic-collapsed-invert' ); + $topic = $( this ).closest( '.flow-topic' ); + if ( $component.is( '.flow-board-collapsed-compact, .flow-board-collapsed-topics' ) ) { + // Board default is collapsed; topic can be overridden to + // expanded, or not. + + // We also remove flow-topic-collapsed. That is set on the + // server for moderated posts, but an explicit user action + // overrides that. + $topic.removeClass( 'flow-topic-collapsed' ).toggleClass( 'flow-topic-expanded' ); + + } else { + // .flow-board-collapsed-full; Board default is expanded; + // topic can be overridden to collapsed, or not. + $topic.toggleClass( 'flow-topic-collapsed' ); + } + topicId = $topic.data('flow-id'); // Save in sessionStorage states = mw.flow.StorageEngine.sessionStorage.getItem( 'topicCollapserStates' ) || {}; - if ( $topic.hasClass( 'flow-topic-collapsed-invert' ) ) { - states[ topicId ] = 1; + // Opposite of STORAGE_TO_CLASS + if ( $topic.hasClass( 'flow-topic-expanded' ) ) { + states[ topicId ] = '+'; + } else if ( $topic.hasClass( 'flow-topic-collapsed' ) ) { + states[ topicId ] = '-'; } else { delete states[ topicId ]; } @@ -1860,7 +1895,10 @@ } else { // Save mw.flow.StorageEngine.localStorage.setItem( 'collapserState', newState ); - flowBoard.$board.find( '.flow-topic-collapsed-invert' ).removeClass( 'flow-topic-collapsed-invert' ); + flowBoard.$board.find( '.flow-topic-expanded, .flow-topic-collapsed' ) + // If moderated topics are currently collapsed, leave them that way + .not( '.flow-topic-moderated.flow-topic-collapsed' ) + .removeClass( 'flow-topic-expanded flow-topic-collapsed' ); // Remove individual topic states mw.flow.StorageEngine.sessionStorage.removeItem( 'topicCollapserStates' ); diff --git a/modules/new/styles/interactive.less b/modules/new/styles/interactive.less index e38dd20..8564dc8 100644 --- a/modules/new/styles/interactive.less +++ b/modules/new/styles/interactive.less @@ -177,7 +177,27 @@ display: none; } } -// In compact mode, posts are hidden, and the topic titlebar is minimized + + +// Overrides to the default expand/collapse state. Only set when +// necessary and removed when the page-level state changes +// div is used to add specificity. + +div.flow-topic-expanded { + .flow-post, + .flow-reply-form { + display: block; + } +} + +div.flow-topic-collapsed { + .flow-post, + .flow-reply-form { + display: none; + } +} + +// In compact mode, posts are hidden by default, and the topic titlebar is minimized .flow-board-collapsed-compact { .flow-board-navigation { a.flow-board-collapser-compact { @@ -219,14 +239,8 @@ display: block; } } - .flow-topic-collapsed-invert { - .flow-post, - .flow-reply-form { - display: block; - } - } } -// In topics mode, posts are hidden; only the topic titlebar is visible +// In topics mode, posts are hidden by default; only the topic titlebar is visible .flow-board-collapsed-topics { .flow-board-navigation { a.flow-board-collapser-topics { @@ -241,23 +255,10 @@ .flow-reply-form { display: none; } - .flow-topic-collapsed-invert { - .flow-post, - .flow-reply-form { - display: block; - } - } } .flow-topic-moderated { opacity: 0.4; -} - -.flow-topic-collapsed-invert { - .flow-post, - .flow-reply-form { - display: none; - } } // Show that the topic titlebar is clickable diff --git a/tests/browser/features/collapse.feature b/tests/browser/features/collapse.feature index 3f8503e..2f1e703 100644 --- a/tests/browser/features/collapse.feature +++ b/tests/browser/features/collapse.feature @@ -4,11 +4,23 @@ Background: Given I am logged in And I am on Flow page - And I create a non-moderated topic + + And I create a Non-Moderated Topic in Flow new topic + And I create a Initial post of Non-Moderated Topic into Flow body + And I click New topic save + + And I create a Hidden Topic in Flow new topic + And I create a Initial post of Hidden Topic into Flow body + And I click New topic save + And I click the Topic Actions link + And I click the Hide topic button + And I give reason for hiding as being "Test collapsing moderated posts" + And I click Hide topic + And I do not see the dialog box Scenario: Small topics view Given I am on Flow page - When I switch to Small topics view + When I switch from Topics and posts view to Small topics view And the page renders in 1 seconds Then I should see the title of the first non-moderated topic And I should not see who started the first non-moderated topic @@ -16,7 +28,7 @@ Scenario: Topics only view Given I am on Flow page - When I switch to Topics only view + When I switch from Topics and posts view to Topics only view And the page renders in 1 seconds Then I should see the title of the first non-moderated topic And I should see who started the first non-moderated topic @@ -24,8 +36,79 @@ Scenario: Topics and posts view Given I am on Flow page - When I switch to Topics and posts view + # Complete mode cycle + When I switch from Topics and posts view to Topics only view + And I switch from Topics only view to Topics and posts view And the page renders in 1 seconds Then I should see the title of the first non-moderated topic And I should see who started the first non-moderated topic And I should see the comments of the first non-moderated topic + + Scenario: For a non-moderated post, collapse override is forgotten every time the mode is switched + Given I am on Flow page + When I click the first non-moderated topic + And I switch from Topics and posts view to Topics only view + # This "Then" would be the same regardless of whether it forgot + # the override, since the override matches the new default state. + # However, it doesn't hurt to assert it. + Then I should see the title of the first non-moderated topic + And I should see who started the first non-moderated topic + And I should not see the comments of the first non-moderated topic + + When I click the first non-moderated topic + And I switch from Topics only view to Small topics view + Then I should see the title of the first non-moderated topic + And I should not see who started the first non-moderated topic + And I should not see the comments of the first non-moderated topic + + # Click it twice in a row, to test the new default state (everything + # shows) still takes effect even though the user explicitly opened + # and re-closed it on small topics view + When I click the first non-moderated topic + And I click the first non-moderated topic + And I switch from Small topics view to Topics and posts view + Then I should see the title of the first non-moderated topic + And I should see who started the first non-moderated topic + And I should see the comments of the first non-moderated topic + + Scenario: For a moderated post, a mode cycle with no user override keeps it hidden + Given I am on Flow page + Then I should see the title of the first moderated topic + And I should see who started the first moderated topic + And I should not see the comments of the first moderated topic + + When I switch from Topics and posts view to Topics only view + Then I should see the title of the first moderated topic + And I should see who started the first moderated topic + And I should not see the comments of the first moderated topic + + When I switch from Topics only view to Small topics view + Then I should see the title of the first moderated topic + And I should not see who started the first moderated topic + And I should not see the comments of the first moderated topic + + When I switch from Small topics view to Topics and posts view + Then I should see the title of the first moderated topic + And I should see who started the first moderated topic + And I should not see the comments of the first moderated topic + + Scenario: For a moderated post, switching modes does not forget a user-set close override + Given I am on Flow page + # First, erase the server-set close override + When I click the first moderated topic + Then I should see the title of the first moderated topic + And I should see who started the first moderated topic + And I should see the comments of the first moderated topic + + # Now, put a user-set close override + When I click the first moderated topic + Then I should see the title of the first moderated topic + And I should see who started the first moderated topic + And I should not see the comments of the first moderated topic + + # Complete mode cycle + When I switch from Topics and posts view to Topics only view + And I switch from Topics only view to Topics and posts view + Then I should see the title of the first moderated topic + And I should see who started the first moderated topic + And I should not see the comments of the first moderated topic diff --git a/tests/browser/features/step_definitions/collapse_steps.rb b/tests/browser/features/step_definitions/collapse_steps.rb index e6149cb..c5b7823 100644 --- a/tests/browser/features/step_definitions/collapse_steps.rb +++ b/tests/browser/features/step_definitions/collapse_steps.rb @@ -1,28 +1,29 @@ -Given(/^I create a non-moderated topic$/) do - step "I create a Non-Moderated Topic in Flow new topic" - step "I create a Initial post of Non-Moderated Topic into Flow body" - step "I click New topic save" -end +# In order +COLLAPSE_STRING_TO_INDEX = { + "Topics and posts" => 0, + "Topics only" => 1, + "Small topics" => 2 +} -# All of these assume it's starting from topics and posts view -When(/^I switch to Topics only view$/) do - on(FlowPage).topics_only_view_element.when_visible.click -end +COLLAPSE_INDEX_TO_STRING = COLLAPSE_STRING_TO_INDEX.invert -When(/^I switch to Small topics view$/) do +When(/^I switch from (.*) view to (.*) view$/) do |start_mode, end_mode| on(FlowPage) do |page| - page.topics_only_view_element.when_visible.click - page.small_topics_view_element.when_visible.click - end -end + # If current_index is the array index, what must be clicked to + # get to the next one + elementToClick = [ + page.topics_only_view_element, + page.small_topics_view_element, + page.topics_and_posts_view_element + ]; -# This starts from topics and posts view and implicitly tests that -# three clicks cycles back to the beginning -When(/^I switch to Topics and posts view$/) do - on(FlowPage) do |page| - page.topics_only_view_element.when_visible.click - page.small_topics_view_element.when_visible.click - page.topics_and_posts_view_element.when_visible.click + current_index = COLLAPSE_STRING_TO_INDEX[start_mode]; + current_mode = start_mode; + while current_mode != end_mode do + elementToClick[current_index].when_visible.click + current_index = ( current_index + 1 ) % elementToClick.length + current_mode = COLLAPSE_INDEX_TO_STRING[current_index] + end end end @@ -46,6 +47,25 @@ visibility_to_should(on(FlowPage).first_non_moderated_topic_post_content_element, visibility_str) end +When(/^I click the first non-moderated topic$/) do + on(FlowPage).first_non_moderated_topic_title_element.click +end + +Then(/^I should (.*) the title of the first moderated topic$/) do |visibility_str| + visibility_to_should(on(FlowPage).first_moderated_topic_title_element, visibility_str) +end + +Then(/^I should (.*) who started the first moderated topic$/) do |visibility_str| + visibility_to_should(on(FlowPage).first_moderated_topic_starter_element, visibility_str) +end + +Then(/^I should (.*) the comments of the first moderated topic$/) do |visibility_str| + visibility_to_should(on(FlowPage).first_moderated_topic_post_content_element, visibility_str) +end + +When(/^I click the first moderated topic$/) do + on(FlowPage).first_moderated_topic_title_element.click +end When(/^the page renders in (.+) seconds$/) do |seconds| sleep seconds.to_i diff --git a/tests/browser/features/step_definitions/flow_steps.rb b/tests/browser/features/step_definitions/flow_steps.rb index 0a22cfa..da78d22 100644 --- a/tests/browser/features/step_definitions/flow_steps.rb +++ b/tests/browser/features/step_definitions/flow_steps.rb @@ -133,6 +133,10 @@ on(FlowPage).topic_hide_button_element.when_present.should be_visible end +When(/^I click the Hide topic button$/) do + on(FlowPage).topic_hide_button_element.when_present.click +end + Then(/^I should see a Suppress button$/) do on(FlowPage).suppress_button_element.should be_visible end diff --git a/tests/browser/features/step_definitions/moderation_steps.rb b/tests/browser/features/step_definitions/moderation_steps.rb index 92db892..f9b0ce8 100644 --- a/tests/browser/features/step_definitions/moderation_steps.rb +++ b/tests/browser/features/step_definitions/moderation_steps.rb @@ -5,10 +5,22 @@ step 'the top post should have a heading which contains "' + arg1 + '"' end +# TODO (mattflaschen, 2014-06-25): Have the below actions (e.g. 'I +# click Delete topic') wait for the dialog box to be non-visible +# afterwards (to confirm API call finished), rather than use +# timeouts? When(/^I see a dialog box$/) do on(FlowPage).dialog_element.when_present.should be_visible end +When(/^I give reason for hiding as being "(.*?)"$/) do |arg1| + on(FlowPage).dialog_input_element.when_present.send_keys( arg1 ) +end + +When(/^I click Hide topic$/) do + on(FlowPage).dialog_submit_element.when_present.click +end + When(/^I give reason for deletion as being "(.*?)"$/) do |arg1| on(FlowPage).dialog_input_element.when_present.send_keys( arg1 ) end diff --git a/tests/browser/features/support/pages/flow_page.rb b/tests/browser/features/support/pages/flow_page.rb index 9444bd2..a883499 100644 --- a/tests/browser/features/support/pages/flow_page.rb +++ b/tests/browser/features/support/pages/flow_page.rb @@ -46,6 +46,25 @@ span(:first_non_moderated_topic_starter, xpath: '(//*[contains(@class, "flow-topic ") and not(contains(@class, "flow-topic-moderated"))]//*[contains(@class, "flow-topic-titlebar")]//*[contains(@class, "flow-author")])[1]') div(:first_non_moderated_topic_post_content, xpath: '(//*[contains(@class, "flow-topic ") and not(contains(@class, "flow-topic-moderated"))]//*[contains(@class, "flow-post-content")])[1]') + # Works around CSS descendant selector problem (https://github.com/cheezy/page-object/issues/222) + div(:first_moderated_topic, css: '.flow-topic.flow-topic-moderated', index: 0) + + div(:first_moderated_topic_titlebar) do |page| + page.first_moderated_topic_element.div_element(class: 'flow-topic-titlebar') + end + + h2(:first_moderated_topic_title) do |page| + page.first_moderated_topic_titlebar_element.h2_element(class: 'flow-topic-title') + end + + span(:first_moderated_topic_starter) do |page| + page.first_moderated_topic_titlebar_element.span_element(class: 'flow-author') + end + + div(:first_moderated_topic_post_content) do |page| + page.first_moderated_topic_element.div_element(class: 'flow-post', index: 0).div_element(class: 'flow-post-main').div_element(class: 'flow-post-content') + end + # Topic actions menu (all belonging to the first post) a(:topic_actions_link, css: ".flow-topic .flow-topic-titlebar .flow-menu-js-drop a", index: 0) ul(:topic_actions_menu, css: ".flow-topic .flow-topic-titlebar .flow-menu ul", index: 0) @@ -106,10 +125,6 @@ a(:actions_link_permalink_3rd_comment) do |page| page.third_post_actions_menu_element.link_element(text: "Permalink") end - - # Hiding a topic with no-JS; may also be applicable for other moderation - textarea(:topic_reason, name: "topic_reason") - button(:topic_submit, css: '.flow-form-actions button[data-role="submit"]') # New topic creation form(:new_topic_form, css: ".flow-newtopic-form") -- To view, visit https://gerrit.wikimedia.org/r/141070 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: merged Gerrit-Change-Id: I1d8274d7306bc318f693a6a10b712e56219ea25c Gerrit-PatchSet: 7 Gerrit-Project: mediawiki/extensions/Flow Gerrit-Branch: frontend-rewrite Gerrit-Owner: Mattflaschen <[email protected]> Gerrit-Reviewer: Jdlrobson <[email protected]> Gerrit-Reviewer: Mattflaschen <[email protected]> Gerrit-Reviewer: SG <[email protected]> Gerrit-Reviewer: jenkins-bot <> _______________________________________________ MediaWiki-commits mailing list [email protected] https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits
