jenkins-bot has submitted this change and it was merged.

Change subject: Hijack watchstar behaviour in alpha
......................................................................


Hijack watchstar behaviour in alpha

Note: This only overrides default page watchstar. Watchstars in other places
still save to normal watchstar for time being.

Dependency: I00d83f28f88cd3ae63a0858b7b48c5765fe35c20
Dependency: I90e172ba3e1fe2531a7f50f20bb9319a7f5747d3
Bug: T91294
Change-Id: Iac6df0166e12a19acbce44aa23f201ce4e050ce8
---
M Gather.php
M i18n/en.json
M i18n/qqq.json
M includes/Gather.hooks.php
M includes/Resources.php
A resources/ext.gather.watchstar.icons/green_check.svg
A resources/ext.gather.watchstar.icons/grey_check.svg
A resources/ext.gather.watchstar/CollectionsContentOverlay.js
A resources/ext.gather.watchstar/CollectionsWatchstar.js
A resources/ext.gather.watchstar/content.hogan
A resources/ext.gather.watchstar/contentOverlay.less
A resources/ext.gather.watchstar/init.js
A tests/browser/features/add_to_collection.feature
A tests/browser/features/step_definitions/add_to_collection_steps.rb
M tests/browser/features/step_definitions/common_steps.rb
M tests/browser/features/support/pages/article_page.rb
16 files changed, 431 insertions(+), 2 deletions(-)

Approvals:
  Robmoen: Looks good to me, approved
  Jhernandez: Looks good to me, but someone else must approve
  Jdlrobson: Looks good to me, but someone else must approve
  jenkins-bot: Verified



diff --git a/Gather.php b/Gather.php
index ebaf74a..64899bd 100644
--- a/Gather.php
+++ b/Gather.php
@@ -74,7 +74,7 @@
 $wgHooks['UnitTestsList'][] = 'Gather\Hooks::onUnitTestsList';
 $wgHooks['getUserPermissionsErrors'][] = 
'Gather\Hooks::onGetUserPermissionsErrors';
 $wgHooks['ContentHandlerDefaultModelFor'][] = 
'Gather\Hooks::onContentHandlerDefaultModelFor';
-
+$wgHooks['SkinMinervaDefaultModules'][] = 
'Gather\Hooks::onSkinMinervaDefaultModules';
 
 // ResourceLoader modules
 require_once __DIR__ . "/includes/Resources.php";
diff --git a/i18n/en.json b/i18n/en.json
index 58d5a73..2caa2bb 100644
--- a/i18n/en.json
+++ b/i18n/en.json
@@ -2,6 +2,12 @@
        "@metadata": {
                "authors": []
        },
+       "gather-collection-member": "Is member of collection.",
+       "gather-collection-non-member": "Is not member of collection.",
+       "gather-anon-cta": "Add this page to a collection that you can share 
with the world.",
+       "gather-add-to-existing": "Add to existing collection",
+       "gather-add-toast": "The page has been added to your \"$1\" 
collection.",
+       "gather-remove-toast": "The page has been removed from your \"$1\" 
collection.",
        "gather-desc": "Component of Mobile Frontend allowing users to curate 
lists.",
        "gather-anon-view-lists": "You need to be logged in to see your 
Collections.",
        "gather-watchlist-title": "Watchlist",
diff --git a/i18n/qqq.json b/i18n/qqq.json
index c782389..ca70159 100644
--- a/i18n/qqq.json
+++ b/i18n/qqq.json
@@ -4,6 +4,12 @@
                        "Liuxinyu970226"
                ]
        },
+       "gather-collection-member": "Alternative text displayed next to 
collection name when page is a member.",
+       "gather-collection-non-member": "Alternative text displayed next to 
collection name when page is not a member.",
+       "gather-anon-cta": "Message that shows to anonymous users when they 
click the add to collection button.",
+       "gather-add-to-existing": "Heading that shows in the watchstar content 
overlay above a list of your existing collections.",
+       "gather-add-toast": "Message displayed when you add an item to a 
collection. Parameters:\n* $1 - Name of collection.",
+       "gather-remove-toast": "Message displayed when you remove an item from 
a collection. Parameters:\n* $1 - Name of collection.",
        "gather-desc": 
"{{desc|name=Gather|url=https://www.mediawiki.org/wiki/Extension:Gather}}";,
        "gather-anon-view-lists": "Note: Experimental feature. Messages and UI 
may change radically at any time. Translate at your own risk.\n Text shown when 
trying to see your own collections on [[Special:Gather]] but the user is not 
logged in.",
        "gather-watchlist-title": "Note: Experimental feature. Messages and UI 
may change radically at any time. Translate at your own risk.\n Title used for 
special casing the Watchlist collection on the [[Special:Gather]] 
page.\n{{Identical|Watchlist}}",
diff --git a/includes/Gather.hooks.php b/includes/Gather.hooks.php
index c1595da..c0f88e0 100644
--- a/includes/Gather.hooks.php
+++ b/includes/Gather.hooks.php
@@ -26,6 +26,20 @@
                        die( -1 );
                }
        }
+
+       /**
+        * Modify mobile frontend modules to hook into the watchstar
+        * @param SkinMinerva $skin
+        * @param array $modules Resource loader modules
+        * @return boolean
+        */
+       public static function onSkinMinervaDefaultModules( $skin, &$modules ) {
+               if ( MobileContext::singleton()->isAlphaGroupMember() ) {
+                       $modules['watch'] = array( 'ext.gather.watchstar' );
+               }
+               return true;
+       }
+
        /**
         * Add collections link in personal tools menu
         * @param array &$items Items array to be added to menu
diff --git a/includes/Resources.php b/includes/Resources.php
index 7524dac..78315dd 100644
--- a/includes/Resources.php
+++ b/includes/Resources.php
@@ -71,4 +71,43 @@
                'group' => 'other',
        ),
 
+       'ext.gather.watchstar.icons' => $wgGatherResourceFileModuleBoilerplate 
+ array(
+               'class' => 'ResourceLoaderImageModule',
+               'prefix' => 'mw-ui',
+               'images' => array(
+                       // FIXME: ':before' suffix should be configurable in 
image module.
+                       'icon' => array(
+                               'tick-disabled:before' => 
'ext.gather.watchstar.icons/grey_check.svg',
+                               'tick:before' => 
'ext.gather.watchstar.icons/green_check.svg',
+                       ),
+               ),
+       ),
+
+       'ext.gather.watchstar' => $wgGatherResourceFileModuleBoilerplate + 
array(
+               'dependencies' => array(
+                       'mobile.watchstar',
+                       'mobile.contentOverlays',
+                       'ext.gather.watchstar.icons',
+               ),
+               'styles' => array(
+                       'ext.gather.watchstar/contentOverlay.less',
+               ),
+               'messages' => array(
+                       'gather-add-to-existing',
+                       'gather-watchlist-title',
+                       'gather-add-toast',
+                       'gather-remove-toast',
+                       'gather-anon-cta',
+                       'gather-collection-member',
+                       'gather-collection-non-member',
+               ),
+               'templates' => array(
+                       'content.hogan' => 'ext.gather.watchstar/content.hogan',
+               ),
+               'scripts' => array(
+                       
'resources/ext.gather.watchstar/CollectionsContentOverlay.js',
+                       
'resources/ext.gather.watchstar/CollectionsWatchstar.js',
+                       'resources/ext.gather.watchstar/init.js',
+               ),
+       ),
 ) );
diff --git a/resources/ext.gather.watchstar.icons/green_check.svg 
b/resources/ext.gather.watchstar.icons/green_check.svg
new file mode 100644
index 0000000..15afc29
--- /dev/null
+++ b/resources/ext.gather.watchstar.icons/green_check.svg
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 
6.00 Build 0)  -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" 
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd";>
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg"; 
xmlns:xlink="http://www.w3.org/1999/xlink"; x="0px" y="0px"
+        viewBox="0 0 51.6 51.6" enable-background="new 0 0 51.6 51.6" 
xml:space="preserve">
+<g>
+       <circle fill="#00AF89" cx="25.8" cy="25.8" r="24.7"/>
+       <polygon fill="#FFFFFF" points="39,18.6 35.7,15.3 21.3,29.8 15.9,24.3 
12.7,27.6 21.3,36.3       "/>
+</g>
+</svg>
diff --git a/resources/ext.gather.watchstar.icons/grey_check.svg 
b/resources/ext.gather.watchstar.icons/grey_check.svg
new file mode 100644
index 0000000..c4e93a6
--- /dev/null
+++ b/resources/ext.gather.watchstar.icons/grey_check.svg
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 
6.00 Build 0)  -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" 
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd";>
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg"; 
xmlns:xlink="http://www.w3.org/1999/xlink"; x="0px" y="0px"
+        viewBox="0 0 51.6 51.6" enable-background="new 0 0 51.6 51.6" 
xml:space="preserve">
+<g>
+       <circle fill="#CCCCCC" cx="25.8" cy="25.8" r="24.7"/>
+       <polygon fill="#FFFFFF" points="39,18.6 35.7,15.3 21.3,29.8 15.9,24.3 
12.7,27.6 21.3,36.3       "/>
+</g>
+</svg>
diff --git a/resources/ext.gather.watchstar/CollectionsContentOverlay.js 
b/resources/ext.gather.watchstar/CollectionsContentOverlay.js
new file mode 100644
index 0000000..d441fd9
--- /dev/null
+++ b/resources/ext.gather.watchstar/CollectionsContentOverlay.js
@@ -0,0 +1,108 @@
+( function ( M, $ ) {
+
+       var CollectionsContentOverlay,
+               icons = M.require( 'icons' ),
+               toast = M.require( 'toast' ),
+               Icon = M.require( 'Icon' ),
+               WatchstarApi = M.require( 'modules/watchstar/WatchstarApi' ),
+               ContentOverlay = M.require( 'modules/tutorials/ContentOverlay' 
);
+
+       /**
+        * A clickable watchstar for managing collections
+        * @class CollectionsContentOverlay
+        * @extends ContentOverlay
+        */
+       CollectionsContentOverlay = ContentOverlay.extend( {
+               /**
+                * FIXME: re-evaluate content overlay default classes/css.
+                * @inheritdoc
+                */
+               className: 'collection-overlay content-overlay overlay 
position-fixed',
+               /** @inheritdoc */
+               templatePartials: {
+                       content: mw.template.get( 'ext.gather.watchstar', 
'content.hogan' )
+               },
+               /** @inheritdoc */
+               events: {
+                       click: 'onClickInsideOverlay',
+                       'click .overlay-content li': 'onSelectCollection'
+               },
+               /** @inheritdoc */
+               hasFixedHeader: false,
+               /** @inheritdoc */
+               defaults: {
+                       /** @inheritdoc */
+                       fixedHeader: false,
+                       iconButton: new Icon( {
+                               name: 'tick',
+                               label: mw.msg( 'gather-collection-member' )
+                       } ).toHtmlString(),
+                       iconDisabledButton: new Icon( {
+                               name: 'tick-disabled',
+                               label: mw.msg( 'gather-collection-non-member' )
+                       } ).toHtmlString(),
+                       title: mw.config.get( 'wgTitle' ),
+                       spinner: icons.spinner().toHtmlString(),
+                       subheading: mw.msg( 'gather-add-to-existing' ),
+                       collections: []
+               },
+               /** @inheritdoc */
+               initialize: function () {
+                       this.api = new WatchstarApi();
+                       ContentOverlay.prototype.initialize.apply( this, 
arguments );
+               },
+               /** @inheritdoc */
+               postRender: function () {
+                       this.$( '.spinner' ).hide();
+               },
+               /**
+                * Event handler for all clicks inside overlay.
+                * @param {jQuery.Event} ev
+                */
+               onClickInsideOverlay: function ( ev ) {
+                       ev.stopPropagation();
+               },
+               /**
+                * Event handler for selecting an existing collection.
+                * @param {jQuery.Event} ev
+                */
+               onSelectCollection: function ( ev ) {
+                       var self = this,
+                               api = this.api,
+                               collection,
+                               $target = $( ev.target ),
+                               page = M.getCurrentPage();
+
+                       collection = {
+                               title: $target.data( 'collection-title' ),
+                               id:  $target.data( 'collection-id' )
+                       };
+                       api.toggleStatus( page ).done( function () {
+                               var msg, page = M.getCurrentPage();
+                               // update current page
+                               page.options.isWatched = api.isWatchedPage( 
page );
+                               // show toast
+                               msg = api.isWatchedPage( page ) ? 
'gather-add-toast' : 'gather-remove-toast';
+                               toast.show( mw.msg( msg, collection.title ), 
'toast' );
+                               self.hide();
+                               if ( page.options.isWatched ) {
+                                       /**
+                                        * @event watch
+                                        * Fired when the watch star is changed 
to watched status
+                                        */
+                                       self.emit( 'watch' );
+                               } else {
+                                       /**
+                                        * @event unwatch
+                                        * Fired when the watch star is changed 
to unwatched status
+                                        */
+                                       self.emit( 'unwatch' );
+                               }
+                       } );
+                       this.$( '.spinner' ).show();
+                       ev.stopPropagation();
+               }
+       } );
+       M.define( 'ext.gather.watchstar/CollectionsContentOverlay', 
CollectionsContentOverlay );
+
+}( mw.mobileFrontend, jQuery ) );
diff --git a/resources/ext.gather.watchstar/CollectionsWatchstar.js 
b/resources/ext.gather.watchstar/CollectionsWatchstar.js
new file mode 100644
index 0000000..aa57197
--- /dev/null
+++ b/resources/ext.gather.watchstar/CollectionsWatchstar.js
@@ -0,0 +1,84 @@
+// jscs:disable requireCamelCaseOrUpperCaseIdentifiers
+( function ( M, $ ) {
+
+       var CollectionsWatchstar,
+               CollectionsContentOverlay = M.require( 
'ext.gather.watchstar/CollectionsContentOverlay' ),
+               Watchstar = M.require( 'modules/watchstar/Watchstar' );
+
+       /**
+        * A clickable watchstar for managing collections
+        * @class CollectionsWatchstar
+        * @extends Watchstar
+        */
+       CollectionsWatchstar = Watchstar.extend( {
+               /** @inheritdoc */
+               ctaDrawerOptions: {
+                       content: mw.msg( 'gather-anon-cta' ),
+                       queryParams: {
+                               campaign: 'gather',
+                               returntoquery: 
'article_action=add_to_collection'
+                       }
+               },
+               /**
+                * @inheritdoc
+                * @cfg {Object} defaults Default options hash.
+                * @cfg {Object} defaults.collections definitions of the users 
existing collections
+                */
+               defaults: $.extend( {}, Watchstar.prototype.defaults, {
+                       collections: []
+               } ),
+               /** @inheritdoc */
+               postRender: function ( options ) {
+                       var $el = this.$el;
+                       Watchstar.prototype.postRender.apply( this, arguments );
+                       // For newly authenticated users via CTA force dialog 
to open.
+                       if ( options.isNewlyAuthenticatedUser ) {
+                               setTimeout( function () {
+                                       $el.trigger( 'click' );
+                               }, 500 );
+                               delete options.isNewlyAuthenticatedUser;
+                       }
+               },
+               /** @inheritdoc */
+               onStatusToggle: function ( ev ) {
+                       // Open the collections content overlay to deal with 
this.
+                       var self = this,
+                               overlay = new CollectionsContentOverlay( {
+                                       collections: this.options.collections
+                               } );
+                       overlay.on( 'watch', function () {
+                               self.newStatus( true );
+                       } );
+                       overlay.on( 'unwatch', function () {
+                               self.newStatus( false );
+                       } );
+                       overlay.show();
+                       ev.stopPropagation();
+               },
+               /**
+                * Sets a new status on the watchstar
+                * @param {bool} newStatus
+                */
+               newStatus: function ( newStatus ) {
+                       if ( newStatus ) {
+                               this.options.isWatched = true;
+                               this.render();
+                               /**
+                                * @event watch
+                                * Fired when the watch star is changed to 
watched status
+                                */
+                               this.emit( 'watch' );
+                       } else {
+                               this.options.isWatched = false;
+                               this.render();
+                               /**
+                                * @event unwatch
+                                * Fired when the watch star is changed to 
unwatched status
+                                */
+                               this.emit( 'unwatch' );
+                       }
+               }
+       } );
+       M.define( 'ext.gather.watchstar/CollectionsWatchstar', 
CollectionsWatchstar );
+
+}( mw.mobileFrontend, jQuery ) );
diff --git a/resources/ext.gather.watchstar/content.hogan 
b/resources/ext.gather.watchstar/content.hogan
new file mode 100644
index 0000000..be10732
--- /dev/null
+++ b/resources/ext.gather.watchstar/content.hogan
@@ -0,0 +1,10 @@
+{{{spinner}}}
+<h3>{{subheading}}</h3>
+<ul>
+{{#collections}}
+       <li data-collection-title="{{title}}" data-collection-id="{{id}}">
+               {{title}} 
{{^titleInCollection}}{{{iconDisabledButton}}}{{/titleInCollection}}
+               {{#titleInCollection}}{{{iconButton}}}{{/titleInCollection}}
+       </li>
+{{/collections}}
+</ul>
diff --git a/resources/ext.gather.watchstar/contentOverlay.less 
b/resources/ext.gather.watchstar/contentOverlay.less
new file mode 100644
index 0000000..e943d80
--- /dev/null
+++ b/resources/ext.gather.watchstar/contentOverlay.less
@@ -0,0 +1,52 @@
+@import "minerva.variables";
+@import "minerva.mixins";
+
+.overlay.collection-overlay {
+       font-size: .9em;
+       text-align: left;
+       left: 3.4em;
+       right: 3.4em;
+       width: auto;
+       // No padding on the overlay to have the header stuck at the top, 
padding on
+       // the content goes on .overlay-content
+       padding: 0;
+
+       .overlay-header-container .overlay-header {
+               li {
+                       // Make the heading row normal height
+                       padding: 0;
+                       margin: 0;
+               }
+       }
+
+       .overlay-content {
+               padding: 1em;
+
+               h3 {
+                       margin-top: 0.5em;
+               }
+
+               li {
+                       cursor: pointer;
+                       border-top: solid 1px @grayLight;
+                       font-size: 1.4em;
+                       padding: 12px 0;
+                       margin: 12px 0;
+
+                       > div {
+                               float: right;
+                       }
+               }
+       }
+
+       &.content-overlay {
+               background: white;
+               color: @grayDark;
+               top: @headerHeight * 1.2;
+               bottom: @headerHeight * 1.2;
+               left: 2em;
+               right: 2em;
+               width: auto;
+       }
+
+}
diff --git a/resources/ext.gather.watchstar/init.js 
b/resources/ext.gather.watchstar/init.js
new file mode 100644
index 0000000..a446cd3
--- /dev/null
+++ b/resources/ext.gather.watchstar/init.js
@@ -0,0 +1,34 @@
+// jscs:disable requireCamelCaseOrUpperCaseIdentifiers
+( function ( M, $ ) {
+
+       var CollectionsWatchstar = M.require( 
'ext.gather.watchstar/CollectionsWatchstar' ),
+               util = M.require( 'util' ),
+               user = M.require( 'user' );
+
+       /**
+        * Toggle the watch status of a known page
+        * @method
+        * @param {Page} page
+        * @ignore
+        */
+       function init( page ) {
+               var $container = $( '#ca-watch' );
+               if ( !page.inNamespace( 'special' ) ) {
+                       new CollectionsWatchstar( {
+                               el: $container,
+                               collections: [
+                                       {
+                                               id: 0,
+                                               title: mw.msg( 
'gather-watchlist-title' ),
+                                               titleInCollection: 
page.isWatched()
+                                       }
+                               ],
+                               page: page,
+                               isAnon: user.isAnon(),
+                               isNewlyAuthenticatedUser: 
util.query.article_action === 'add_to_collection'
+                       } );
+               }
+       }
+       init( M.getCurrentPage() );
+
+}( mw.mobileFrontend, jQuery ) );
diff --git a/tests/browser/features/add_to_collection.feature 
b/tests/browser/features/add_to_collection.feature
new file mode 100644
index 0000000..6d1b0a4
--- /dev/null
+++ b/tests/browser/features/add_to_collection.feature
@@ -0,0 +1,17 @@
+@chrome @en.m.wikipedia.beta.wmflabs.org
+Feature: Add to a collection
+
+  Background:
+    Given I am logged in
+      And I am using the mobile site
+      And I am in alpha mode
+      And I am on the "Selenium Gather test" page
+
+  Scenario:
+    When I click the watchstar
+    Then I see the collection dialog
+
+  Scenario:
+    When I click the watchstar
+     And I select a collection
+    Then I see a toast
diff --git a/tests/browser/features/step_definitions/add_to_collection_steps.rb 
b/tests/browser/features/step_definitions/add_to_collection_steps.rb
new file mode 100644
index 0000000..770f825
--- /dev/null
+++ b/tests/browser/features/step_definitions/add_to_collection_steps.rb
@@ -0,0 +1,7 @@
+When(/^I select a collection$/) do
+  on(ArticlePage).collections_overlay_collection_one_element.when_present.click
+end
+
+Then(/^I see the collection dialog$/) do
+  expect(on(ArticlePage).collections_overlay_element).to exist
+end
diff --git a/tests/browser/features/step_definitions/common_steps.rb 
b/tests/browser/features/step_definitions/common_steps.rb
index 3511c82..68f3d4c 100644
--- a/tests/browser/features/step_definitions/common_steps.rb
+++ b/tests/browser/features/step_definitions/common_steps.rb
@@ -6,3 +6,26 @@
     page.refresh
   end
 end
+
+Given(/^I am in alpha mode$/) do
+  on(MainPage) do |page|
+    page.goto
+    # A domain is explicitly given to avoid a bug in earlier versions of Chrome
+    page.browser.cookies.add 'optin', 'alpha', domain: 
URI.parse(page.page_url_value).host
+    page.refresh
+  end
+end
+
+Then(/^I see a toast$/) do
+  expect(on(ArticlePage).toast_element.when_present).to be_visible
+end
+
+Given(/^I am on the "(.+)" page$/) do |article|
+  # Ensure we do not cause a redirect
+  article = article.sub(/ /, '_')
+  visit(ArticlePage, using_params: { article_name: article })
+end
+
+When(/^I click the watchstar$/) do
+  on(ArticlePage).watch_star_element.when_present.click
+end
diff --git a/tests/browser/features/support/pages/article_page.rb 
b/tests/browser/features/support/pages/article_page.rb
index 635a589..98a53b0 100644
--- a/tests/browser/features/support/pages/article_page.rb
+++ b/tests/browser/features/support/pages/article_page.rb
@@ -3,5 +3,14 @@
 
   include URL
   page_url URL.url('<%=params[:article_name]%><%=params[:hash]%>')
-end
 
+  # UI elements
+  li(:watch_star, id: 'ca-watch')
+
+  # toast
+  div(:toast, class: 'toast')
+
+  # collections
+  div(:collections_overlay, css: '.collection-overlay')
+  li(:collections_overlay_collection_one, css: '.collection-overlay ul li', 
index: 0)
+end

-- 
To view, visit https://gerrit.wikimedia.org/r/193015
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: Iac6df0166e12a19acbce44aa23f201ce4e050ce8
Gerrit-PatchSet: 15
Gerrit-Project: mediawiki/extensions/Gather
Gerrit-Branch: master
Gerrit-Owner: Jdlrobson <[email protected]>
Gerrit-Reviewer: Jdlrobson <[email protected]>
Gerrit-Reviewer: Jhernandez <[email protected]>
Gerrit-Reviewer: Robmoen <[email protected]>
Gerrit-Reviewer: jenkins-bot <>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to