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

Change subject: Echo notifications for mention failures
......................................................................


Echo notifications for mention failures

 - Adds global "$wgEchoMentionStatusNotifications"
   to activate mention status notifications.
   (must be set before extension is loaded)
 - Adds notification types and icon for some basic mention
   failures.
 - Adds failure and stats for anonymous IP.
 - Adds check for links to user subpages.
 - Adds config var for max mention notifications allowed.
 - Bundles notifications.
 - Refactors test for the event generation and adds tests
   for unknown users, user links with subpages and failures
   for too many mentions.

Bug: T136326
Change-Id: I388bdc3714feb9a2865a5ad10dbeabb0a6a09a4f
---
M Echo.php
M Hooks.php
M autoload.php
M i18n/en.json
M i18n/qqq.json
M includes/DiscussionParser.php
A includes/formatters/MentionFailurePresentationModel.php
A modules/icons/mention-failure.svg
M tests/phpunit/DiscussionParserTest.php
A tests/phpunit/revision_txt/747747747.txt
A tests/phpunit/revision_txt/747747748.txt
A tests/phpunit/revision_txt/747747749.txt
12 files changed, 357 insertions(+), 27 deletions(-)

Approvals:
  Addshore: Looks good to me, approved
  jenkins-bot: Verified



diff --git a/Echo.php b/Echo.php
index f01e4a2..daf084e 100644
--- a/Echo.php
+++ b/Echo.php
@@ -167,6 +167,12 @@
 // is deployed to both deployment branches
 $wgEchoMaxUpdateCount = 2000;
 
+// The max number of mention notifications allowed for a user to send at once
+$wgEchoMaxMentionsCount = 50;
+
+// Enable this when you want to enable mention failure notifications for the 
users.
+$wgEchoMentionStatusNotifications = false;
+
 // The time interval between each bundle email in seconds
 // set a small number for test wikis, should set this to 0 to disable email 
bundling
 // if there is no delay queue support
@@ -198,6 +204,10 @@
        'emailuser' => array(
                'web' => true,
                'email' => false,
+       ),
+       'mention-failure' => array(
+               'web' => true,
+               'email' => false
        ),
 );
 
@@ -283,6 +293,10 @@
                'priority' => 4,
                'tooltip' => 'echo-pref-tooltip-mention',
        ),
+       'mention-failure' => array(
+               'priority' => 4,
+               'tooltip' => 'echo-pref-tooltip-mention-failure',
+       ),
        'emailuser' => array(
                'priority' => 9,
                'tooltip' => 'echo-pref-tooltip-emailuser',
@@ -324,6 +338,9 @@
        ),
        'mention' => array(
                'path' => "$echoIconPath/mention.svg",
+       ),
+       'mention-failure' => array(
+               'path' => "$echoIconPath/mention-failure.svg",
        ),
        'reviewed' => array(
                'path' => "$echoIconPath/reviewed.svg",
@@ -413,6 +430,28 @@
                'section' => 'alert',
                'presentation-model' => 'EchoMentionPresentationModel',
        ),
+       'mention-failure' => array(
+               EchoAttributeManager::ATTR_LOCATORS => array(
+                       array( 'EchoUserLocator::locateEventAgent' ),
+               ),
+               'category' => 'mention-failure',
+               'bundle' => array(
+                       'web' => true,
+                       'expandable' => true,
+               ),
+               'group' => 'negative',
+               'section' => 'alert',
+               'presentation-model' => 'EchoMentionFailurePresentationModel',
+       ),
+       'mention-too-many' => array(
+               EchoAttributeManager::ATTR_LOCATORS => array(
+                       array( 'EchoUserLocator::locateEventAgent' ),
+               ),
+               'category' => 'mention-failure',
+               'group' => 'negative',
+               'section' => 'alert',
+               'presentation-model' => 'EchoMentionFailurePresentationModel',
+       ),
        'user-rights' => array(
                EchoAttributeManager::ATTR_LOCATORS => array(
                        array( 'EchoUserLocator::locateFromEventExtra', array( 
'user' ) ),
@@ -480,6 +519,7 @@
 $wgDefaultUserOptions['echo-subscriptions-email-system'] = true;
 $wgDefaultUserOptions['echo-subscriptions-email-user-rights'] = true;
 $wgDefaultUserOptions['echo-subscriptions-web-article-linked'] = false;
+$wgDefaultUserOptions['echo-subscriptions-web-mention-failure'] = false;
 
 // Echo Configuration for EventLogging
 $wgEchoConfig = array(
diff --git a/Hooks.php b/Hooks.php
index d50cc38..972f1e1 100644
--- a/Hooks.php
+++ b/Hooks.php
@@ -10,10 +10,15 @@
         */
        public static function initEchoExtension() {
                global $wgEchoNotifications, $wgEchoNotificationCategories, 
$wgEchoNotificationIcons,
-                       $wgEchoConfig;
+                       $wgEchoConfig, $wgEchoMentionStatusNotifications;
 
                // allow extensions to define their own event
                Hooks::run( 'BeforeCreateEchoEvent', array( 
&$wgEchoNotifications, &$wgEchoNotificationCategories, 
&$wgEchoNotificationIcons ) );
+
+               // Only allow mention status notifications when enabled
+               if ( !$wgEchoMentionStatusNotifications ) {
+                       unset( $wgEchoNotificationCategories['mention-failure'] 
);
+               }
 
                // turn schema off if eventLogging is not enabled
                if ( !class_exists( 'EventLogging' ) ) {
@@ -200,6 +205,14 @@
                                                . '-' . 
$event->getTitle()->getDBkey();
                                }
                                break;
+                       case 'mention-failure':
+                               $bundleString = 'mention-failure';
+                               if ( $event->getTitle() ) {
+                                       $bundleString .= '-' . 
$event->getTitle()->getNamespace()
+                                               . ':' . 
$event->getTitle()->getDBkey()
+                                               . '#' . $event->getExtraParam( 
'section-title' );
+                               }
+                       break;
                }
 
                return true;
diff --git a/autoload.php b/autoload.php
index 38420aa..721255a 100644
--- a/autoload.php
+++ b/autoload.php
@@ -60,6 +60,7 @@
        'EchoIteratorDecorator' => __DIR__ . 
'/includes/iterator/IteratorDecorator.php',
        'EchoLocalCache' => __DIR__ . '/includes/cache/LocalCache.php',
        'EchoMentionPresentationModel' => __DIR__ . 
'/includes/formatters/MentionPresentationModel.php',
+       'EchoMentionFailurePresentationModel' => __DIR__ . 
'/includes/formatters/MentionFailurePresentationModel.php',
        'EchoModelFormatter' => __DIR__ . 
'/includes/formatters/EchoModelFormatter.php',
        'EchoModerationController' => __DIR__ . 
'/includes/controller/ModerationController.php',
        'EchoMultipleIterator' => __DIR__ . 
'/includes/iterator/MultipleIterator.php',
diff --git a/i18n/en.json b/i18n/en.json
index 12a538f..76f7448 100644
--- a/i18n/en.json
+++ b/i18n/en.json
@@ -64,6 +64,7 @@
        "echo-category-title-article-linked": "Page {{PLURAL:$1|link|links}}",
        "echo-category-title-reverted": "Edit {{PLURAL:$1|revert|reverts}}",
        "echo-category-title-mention": "{{PLURAL:$1|Mention|Mentions}}",
+       "echo-category-title-mention-failure": "Failed 
{{PLURAL:$1|mention|mentions}}",
        "echo-category-title-other": "{{PLURAL:$1|Other}}",
        "echo-category-title-system": "{{PLURAL:$1|System}}",
        "echo-category-title-user-rights": "{{PLURAL:$1|User rights change|User 
rights changes}}",
@@ -72,6 +73,7 @@
        "echo-pref-tooltip-article-linked": "Notify me when someone links to a 
page I created from an article page.",
        "echo-pref-tooltip-reverted": "Notify me when someone reverts an edit I 
made, by using the undo or rollback tool.",
        "echo-pref-tooltip-mention": "Notify me when someone links to my user 
page.",
+       "echo-pref-tooltip-mention-failure": "Notify me when my edits fail to 
mention users.",
        "echo-pref-tooltip-user-rights": "Notify me when someone changes my 
user rights.",
        "echo-pref-tooltip-emailuser": "Notify me when someone sends me an 
email.",
        "echo-error-no-formatter": "No formatting defined for notification.",
@@ -124,6 +126,7 @@
        "notification-link-text-collapse-all": "Collapse",
        "notification-link-text-view-message": "View message",
        "notification-link-text-view-mention": "View mention",
+       "notification-link-text-view-mention-failure": "{{PLURAL:$1|View 
mention|View mentions}}",
        "notification-link-text-view-changes": "{{GENDER:$1|View}} changes",
        "notification-link-text-view-page": "View page",
        "notification-header-edit-user-talk": "$1 {{GENDER:$2|left}} a message 
on <strong>{{GENDER:$3|your}} talk page</strong>.",
@@ -142,6 +145,12 @@
        "notification-header-mention-agent-talkpage-nosection": "$1 
{{GENDER:$2|mentioned}} {{GENDER:$3|you}} on 
<strong>{{GENDER:$2|his|her|their}} talk page</strong>.",
        "notification-header-mention-article-talkpage": "$1 
{{GENDER:$2|mentioned}} {{GENDER:$3|you}} on the <strong>$4</strong> talk page 
in \"<strong>$5</strong>\".",
        "notification-header-mention-article-talkpage-nosection": "$1 
{{GENDER:$2|mentioned}} {{GENDER:$3|you}} on the <strong>$4</strong> talk 
page.",
+       "notification-header-mention-failure-user-unknown": "{{GENDER:$2|Your}} 
mention for \"$3\" was not sent because the user was not found.",
+       "notification-header-mention-failure-user-anonymous": 
"{{GENDER:$2|Your}} mention for \"$3\" was not sent because the user is 
anonymous.",
+       "notification-header-mention-failure-too-many": "{{GENDER:$2|Your}} 
mentions were not sent because they exceeded the limit of $3.",
+       "notification-header-mention-failure-bundle": "$3 mentions 
{{GENDER:$2|you made}} on the <strong>$4</strong> talk page were not sent.",
+       "notification-compact-header-mention-failure-user-unknown": 
"<strong>Unknown user:</strong> \"$1\"",
+       "notification-compact-header-mention-failure-user-anonymous": 
"<strong>Anonymous user:</strong> \"$1\"",
        "notification-header-user-rights-add-only": "{{GENDER:$4|Your}} user 
rights were {{GENDER:$1|changed}}. You have been added to: $2.",
        "notification-header-user-rights-remove-only": "{{GENDER:$4|Your}} user 
rights were {{GENDER:$1|changed}}. You are no longer a member of: $2.",
        "notification-header-user-rights-add-and-remove": "{{GENDER:$6|Your}} 
user rights were {{GENDER:$1|changed}}. You have been added to: $2. You are no 
longer a member of: $4.",
diff --git a/i18n/qqq.json b/i18n/qqq.json
index 096c7a6..9fbf5ee 100644
--- a/i18n/qqq.json
+++ b/i18n/qqq.json
@@ -56,6 +56,7 @@
        "echo-category-title-article-linked": "This is a short title for 
notification category.\n\nUsed in a list of options under the heading 
{{msg-mw|Prefs-echosubscriptions}} in Special:Preferences. As far as I can see 
this always needs to be a plural for an unspecified number.\n\nIt used to be 
used as <code>$1</code> in {{msg-mw|Echo-dismiss-message}}, but this message is 
no longer used, apparently.\n\nParameters:\n* $1 - number of messages, for 
PLURAL support\n{{Related|Echo-category-title}}",
        "echo-category-title-reverted": "This is a short title for notification 
category.\n\nUsed in a list of options under the heading 
{{msg-mw|Prefs-echosubscriptions}} in Special:Preferences. As far as I can see 
this always needs to be a plural for an unspecified number.\n\nIt used to be 
used as <code>$1</code> in {{msg-mw|Echo-dismiss-message}}, but this message is 
no longer used, apparently.\n\nParameters:\n* $1 - number of messages, for 
PLURAL support\n{{Related|Echo-category-title}}",
        "echo-category-title-mention": "This is a short title for notification 
category.\n\nUsed in a list of options under the heading 
{{msg-mw|Prefs-echosubscriptions}} in Special:Preferences. As far as I can see 
this always needs to be a plural for an unspecified number.\n\nIt used to be 
used as <code>$1</code> in {{msg-mw|Echo-dismiss-message}}, but this message is 
no longer used, apparently.\n\nParameters:\n* $1 - number of messages, for 
PLURAL support\n{{Related|Echo-category-title}}\n{{Identical|Mention}}",
+       "echo-category-title-mention-failure": "This is a short title for 
notification category.\n\nUsed in a list of options under the heading 
{{msg-mw|Prefs-echosubscriptions}} in Special:Preferences. As far as I can see 
this always needs to be a plural for an unspecified number.\n\nIt used to be 
used as <code>$1</code> in {{msg-mw|Echo-dismiss-message}}, but this message is 
no longer used, apparently.",
        "echo-category-title-other": "This is a short title for notification 
category.\n\nUsed in a list of options under the heading 
{{msg-mw|Prefs-echosubscriptions}} in Special:Preferences. As far as I can see 
this always needs to be a plural for an unspecified number.\n\nIt used to be 
used as <code>$1</code> in {{msg-mw|Echo-dismiss-message}}, but this message is 
no longer used, apparently.\n\nParameters:\n* $1 - number of messages, for 
PLURAL support\n{{Related|Echo-category-title}}\n{{Identical|Other}}",
        "echo-category-title-system": "This is a short title for notification 
category.\n\nUsed in a list of options under the heading 
{{msg-mw|Prefs-echosubscriptions}} in Special:Preferences. As far as I can see 
this always needs to be a plural for an unspecified number.\n\nIt used to be 
used as <code>$1</code> in {{msg-mw|Echo-dismiss-message}}, but this message is 
no longer used, apparently.\n\nParameters:\n* $1 - number of messages, for 
PLURAL support\n{{Related|Echo-category-title}}\n{{Identical|System}}",
        "echo-category-title-user-rights": "This is a short title for 
notification category.\n\nUsed in a list of options under the heading 
{{msg-mw|Prefs-echosubscriptions}} in Special:Preferences. As far as I can see 
this always needs to be a plural for an unspecified number.\n\nIt used to be 
used as <code>$1</code> in {{msg-mw|Echo-dismiss-message}}, but this message is 
no longer used, apparently.\n\nParameters:\n* $1 - number of messages, for 
PLURAL support\n{{Related|Echo-category-title}}",
@@ -64,6 +65,7 @@
        "echo-pref-tooltip-article-linked": "This is a short description of the 
article-linked notification category.\n{{Related|Echo-pref-tooltip}}",
        "echo-pref-tooltip-reverted": "This is a short description of the 
tooltip-reverted notification category.\n{{Related|Echo-pref-tooltip}}",
        "echo-pref-tooltip-mention": "This is a short description of the 
mention notification category.\n{{Related|Echo-pref-tooltip}}",
+       "echo-pref-tooltip-mention-failure": "This is a short description of 
the mention failure notification category.\n{{Related|Echo-pref-tooltip}}",
        "echo-pref-tooltip-user-rights": "This is a short description of the 
user rights changes notification category\n{{Related|Echo-pref-tooltip}}",
        "echo-pref-tooltip-emailuser": "This is a short description of the user 
email notification category\n{{Related|Echo-pref-tooltip}}",
        "echo-error-no-formatter": "Error message displayed when no formatting 
has been defined for a notification. In other words, the extension doesn't know 
how to properly display the notification.",
@@ -117,6 +119,7 @@
        "notification-link-text-view-message": "Label for button that links to 
a message on your talk page.\n{{Identical|View message}}",
        "notification-link-text-view-mention": "Label for button that links to 
a discussion where you were mentioned.",
        "notification-link-text-view-changes": "Label for button that links to 
a \"diff\" view showing changes made to a page. Paramters:\n* $1 - name of the 
user viewing the notification, can be used for GENDER.\n{{Identical|View 
changes}}",
+       "notification-link-text-view-mention-failure": "Label for button that 
links to a discussion where your mentions failed.\n* $1 - The number of failed 
mentions; uses standard number formatting and used for PLURAL",
        "notification-link-text-view-page": "Label for button that links to a 
page.\n{{Identical|View page}}",
        "notification-header-edit-user-talk": "Flyout-specific format for 
displaying notification header of a user talk page being 
edited.\n\nParameters:\n* $1 - the formatted username of the person who 
edited.\n* $2 - the username for GENDER\n* $3 - username of the current user, 
can be used for GENDER",
        "notification-header-edit-user-talk-with-section": "Flyout-specific 
format for displaying notification header of a user talk page being edited with 
a new section or new comment.\n\nParameters:\n* $1 - the formatted username of 
the person who edited.\n* $2 - the username for GENDER\n* $3 - username of the 
current user, can be used for GENDER\n* $4 - the raw section title text",
@@ -134,6 +137,12 @@
        "notification-header-mention-agent-talkpage-nosection": 
"{{doc-singularthey}}\nHeader text for a notification when you are mentioned by 
another user on their talk page.\n* $1 - user's name (not suitable for 
GENDER).\n* $2 - user's name for use in GENDER.\n* $3 - name of the user 
viewing the notification, can be used for GENDER",
        "notification-header-mention-article-talkpage": "Header text for a 
notification when you are mentioned by another user in a section on an article 
talk page.\n* $1 - user's name (not suitable for GENDER).\n* $2 - user's name 
for use in GENDER.\n* $3 - name of the user viewing the notification, can be 
used for GENDER\n* $4 - name of the article whose talk page you are mentioned 
in (without namespace).\n* $5 - name of the section they were mentioned in",
        "notification-header-mention-article-talkpage-nosection": "Header text 
for a notification when you are mentioned by another user on an article talk 
page.\n* $1 - user's name (not suitable for GENDER).\n* $2 - user's name for 
use in GENDER.\n* $3 - name of the user viewing the notification, can be used 
for GENDER\n* $4 - name of the article whose talk page you are mentioned in 
(without namespace)",
+       "notification-header-mention-failure-user-unknown": "Header text for a 
notification when your mention has failed because the user was not found.\n* $2 
- user's name for use in GENDER.\n* $3 - username that was not found.",
+       "notification-header-mention-failure-user-anonymous": "Header text for 
a notification when your mention has failed because the user is an anonymous 
IP.\n* $2 - user's name for use in GENDER.\n* $3 - username that is anonymous.",
+       "notification-header-mention-failure-too-many": "Header text for a 
notification when your mention has failed because you tried to mention too many 
users.\n* $2 - user's name for use in GENDER.\n* $3 - max number of mentions 
allowed; uses standard number formatting",
+       "notification-header-mention-failure-bundle": "Header text for a 
bundled notification when multiple mentions failed.\n* $2 - user's name for use 
in GENDER.\n* $3 - number of mentions; uses standard number formatting and used 
for PLURAL.\n* $4 - talk page title.",
+       "notification-compact-header-mention-failure-user-unknown": "Compact 
header text for a notification when your mention has failed because the user 
was not found.\n* $1 - username that was not found.",
+       "notification-compact-header-mention-failure-user-anonymous": "Compact 
header text for a notification when your mention has failed because the user is 
an anonymous IP.\n* $1 - username that is anonymous.",
        "notification-header-user-rights-add-only": "Notifications header 
message when a user is added to groups.  Parameters:\n* $1 - the raw username 
of the person who made the user rights change, can be used for GENDER 
support\n* $2 - a localized list of the groups that were added\n* $3 - the 
number of groups that were added, can be used for PLURAL\n* $4 - name of the 
user viewing the notification, can be used for GENDER",
        "notification-header-user-rights-remove-only": "Notifications header 
message when a user is removed from groups.  Parameters:\n* $1 - the raw 
username of the person who made the user rights change, can be used for GENDER 
support\n* $2 - a localized list of the groups that were removed\n* $3 - the 
number of groups that were removed, can be used for PLURAL\n* $4 - name of the 
user viewing the notification, can be used for GENDER",
        "notification-header-user-rights-add-and-remove": "Notifications header 
message when a user is added to groups and removed from groups.  Parameters:\n* 
$1 - the raw username of the person who made the user rights change, can be 
used for GENDER support\n* $2 - a localized list of the groups that were 
added\n* $4 - a localized list of the groups that were removed\n* $6 - name of 
the user viewing the notification, can be used for GENDER",
diff --git a/includes/DiscussionParser.php b/includes/DiscussionParser.php
index 58af341..115ee8e 100644
--- a/includes/DiscussionParser.php
+++ b/includes/DiscussionParser.php
@@ -139,6 +139,8 @@
                Revision $revision,
                User $agent
        ) {
+               global $wgEchoMaxMentionsCount, 
$wgEchoMentionStatusNotifications;
+
                $title = $revision->getTitle();
                if ( !$title ) {
                        return;
@@ -152,41 +154,103 @@
                        return;
                }
                $mentionedUsers = array();
+               $unknownUsers = array();
+               $anonymousUsers = array();
                $count = 0;
                $stats = 
MediaWikiServices::getInstance()->getStatsdDataFactory();
 
                foreach ( $links[NS_USER] as $dbk => $page_id ) {
-                       $user = User::newFromName( $dbk );
-
                        // we should not add user to 'mention' notification 
list if
-                       // 1. the user name is not valid
+                       // 1. the user link links to a subpage
+                       if ( self::hasSubpage( $dbk ) ) {
+                               continue;
+                       }
+
+                       // 2. user is an anonymous IP
+                       if ( User::isIP( $dbk ) ) {
+                               $anonymousUsers[] = $dbk;
+                               $count++;
+                               $stats->increment( 
'echo.event.mention.error.anonUser' );
+                               continue;
+                       }
+
+                       $user = User::newFromName( $dbk );
+                       // 3. the user name is not valid
                        if ( !$user ) {
+                               $unknownUsers[] = str_replace( '_', ' ', $dbk );
+                               $count++;
                                $stats->increment( 
'echo.event.mention.error.invalidUser' );
                                continue;
                        }
-                       // 2. the user mentions themselves
+                       // 4. the user mentions themselves
                        if ( $user->getId() == $revision->getUser( 
Revision::RAW ) ) {
                                $stats->increment( 
'echo.event.mention.error.sameUser' );
                                continue;
                        }
-                       // 3. the user is the owner of the talk page
+                       // 5. the user is the owner of the talk page
                        if ( $title->getNamespace() === NS_USER_TALK && 
$title->getDBkey() === $dbk ) {
                                $stats->increment( 
'echo.event.mention.error.ownPage' );
                                continue;
                        }
-                       // 4. user is anonymous or does not exist
-                       if ( $user->isAnon() ) {
-                               $stats->increment( 
'echo.event.mention.error.anonUser' );
+                       // 6. user does not exist
+                       if ( $user->getId() === 0 ) {
+                               $unknownUsers[] = str_replace( '_', ' ', $dbk );
+                               $count++;
+                               $stats->increment( 
'echo.event.mention.error.unknownUser' );
                                continue;
                        }
 
                        $mentionedUsers[$user->getId()] = $user->getId();
                        $count++;
-                       // If more than 50 users are being pinged this is 
likely a spam/attack vector
+                       // If more users are being pinged this is likely a 
spam/attack vector
                        // Don't send any mention notifications.
-                       if ( $count > 50 ) {
+                       if ( $count > $wgEchoMaxMentionsCount ) {
+                               if ( $wgEchoMentionStatusNotifications ) {
+                                       EchoEvent::create( array(
+                                               'type' => 'mention-too-many',
+                                               'title' => $title,
+                                               'extra' => array(
+                                                       'max-mentions' => 
$wgEchoMaxMentionsCount,
+                                                       'section-title' => 
$header,
+                                                       'notifyAgent' => true
+                                               ),
+                                               'agent' => $agent,
+                                       ) );
+                               }
                                $stats->increment( 
'echo.event.mention.error.tooMany' );
                                return;
+                       }
+               }
+
+               if ( $wgEchoMentionStatusNotifications ) {
+                       // TODO batch?
+                       foreach ( $unknownUsers as $unknownUser ) {
+                               EchoEvent::create( array(
+                                       'type' => 'mention-failure',
+                                       'title' => $title,
+                                       'extra' => array(
+                                               'failure-type' => 
'user-unknown',
+                                               'subject-name' => $unknownUser,
+                                               'section-title' => $header,
+                                               'notifyAgent' => true
+                                       ),
+                                       'agent' => $agent,
+                               ) );
+                       }
+
+                       // TODO batch?
+                       foreach ( $anonymousUsers as $anonymousUser ) {
+                               EchoEvent::create( array(
+                                       'type' => 'mention-failure',
+                                       'title' => $title,
+                                       'extra' => array(
+                                               'failure-type' => 
'user-anonymous',
+                                               'subject-name' => 
$anonymousUser,
+                                               'section-title' => $header,
+                                               'notifyAgent' => true
+                                       ),
+                                       'agent' => $agent,
+                               ) );
                        }
                }
 
@@ -207,6 +271,10 @@
                ) );
        }
 
+       private static function hasSubpage( $dbk ) {
+               return strpos( $dbk, '/' ) !== false;
+       }
+
        /**
         * It's like Article::prepareTextForEdit,
         *  but not for editing (old wikitext usually)
diff --git a/includes/formatters/MentionFailurePresentationModel.php 
b/includes/formatters/MentionFailurePresentationModel.php
new file mode 100644
index 0000000..2673965
--- /dev/null
+++ b/includes/formatters/MentionFailurePresentationModel.php
@@ -0,0 +1,98 @@
+<?php
+
+/**
+ * Presenter for 'mention-failure' notifications
+ *
+ * @author Christoph Fischer <[email protected]>
+ *
+ * @license GNU GPL v2+
+ */
+class EchoMentionFailurePresentationModel extends EchoEventPresentationModel {
+       use EchoPresentationModelSectionTrait;
+
+       public function getIconType() {
+               return 'mention-failure';
+       }
+
+       public function getHeaderMessage() {
+               if ( $this->isTooManyMentionsFailure() ) {
+                       $msg = $this->getMessageWithAgent( 
'notification-header-mention-failure-too-many' );
+                       $msg->numParams( $this->getMaxMentions() );
+                       return $msg;
+               }
+
+               if ( $this->isBundled() ) {
+                       $msg = $this->getMessageWithAgent( 
'notification-header-mention-failure-bundle' );
+                       $msg->numParams( $this->getBundleCount() );
+                       $msg->params( $this->getTruncatedTitleText( 
$this->event->getTitle() ) );
+                       return $msg;
+               }
+
+               // Messages that can be used here:
+               // * notification-header-mention-failure-user-unknown
+               // * notification-header-mention-failure-user-anonymous
+               $msg = $this->getMessageWithAgent( 
'notification-header-mention-failure-' . $this->getFailureType() );
+               $msg->params( $this->getSubjectName() );
+
+               return $msg;
+       }
+
+       public function getCompactHeaderMessage() {
+               // Messages that can be used here:
+               // * notification-compact-header-mention-failure-user-unknown
+               // * notification-compact-header-mention-failure-user-anonymous
+               $msg = $this->msg( 
'notification-compact-header-mention-failure-' . $this->getFailureType() );
+               $msg->params( $this->getSubjectName() );
+
+               return $msg;
+       }
+
+       public function getPrimaryLink() {
+               return array(
+                       // Need FullURL so the section is included
+                       'url' => $this->getTitleWithSection()->getFullURL(),
+                       'label' => $this->msg( 
'notification-link-text-view-mention-failure' )
+                               ->numParams( $this->getBundleCount() )
+                               ->text()
+               );
+       }
+
+       public function getSecondaryLinks() {
+               if ( $this->isBundled() ) {
+                       $viewMentionsLink = array_merge(
+                               $this->getPrimaryLink(),
+                               array(
+                                       'icon' => 'speechBubbles',
+                                       'prioritized' => true
+                               )
+                       );
+
+                       return array( $viewMentionsLink );
+               }
+
+               $talkPageLink = $this->getPageLink(
+                       $this->getTitleWithSection(),
+                       '',
+                       true
+               );
+
+               return array( $talkPageLink );
+       }
+
+       private function getSubjectName() {
+               return $this->event->getExtraParam( 'subject-name', '' );
+       }
+
+       private function getFailureType() {
+               return $this->event->getExtraParam( 'failure-type', 
'user-unknown' );
+       }
+
+       private function isTooManyMentionsFailure() {
+               return $this->getType() === 'mention-too-many';
+       }
+
+       private function getMaxMentions() {
+               global $wgEchoMaxMentionsCount;
+               return $this->event->getExtraParam( 'max-mentions', 
$wgEchoMaxMentionsCount );
+       }
+}
diff --git a/modules/icons/mention-failure.svg 
b/modules/icons/mention-failure.svg
new file mode 100644
index 0000000..3b78b41
--- /dev/null
+++ b/modules/icons/mention-failure.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg"; width="30" height="30" viewBox="0 0 
188.36253 188.36253">
+    <path d="M45.695 153.668h135.38l-23.01-22.484V41.298l30.298-30.297L177.362 
0 0 177.362l11 11 34.695-34.694zM20.002 137.68V30.003H127.68L20.002 137.68z" 
fill="#555" fill-rule="evenodd"/>
+</svg>
\ No newline at end of file
diff --git a/tests/phpunit/DiscussionParserTest.php 
b/tests/phpunit/DiscussionParserTest.php
index b8a51ed..07952ae 100644
--- a/tests/phpunit/DiscussionParserTest.php
+++ b/tests/phpunit/DiscussionParserTest.php
@@ -295,6 +295,25 @@
                                ),
                                'precondition' => 'isParserFunctionsInstalled',
                        ),
+                       array(
+                               'new' => 747747748,
+                               'old' => 747747747,
+                               'username' => 'Admin',
+                               'lang' => 'en',
+                               'pages' => array(),
+                               'title' => 'UTPage',
+                               'expected' => array(
+                                       array(
+                                               'type' => 'mention-failure',
+                                               'agent' => 'Admin',
+                                       ),
+                                       array(
+                                               'type' => 'mention-failure',
+                                               'agent' => 'Admin',
+                                       ),
+                               ),
+
+                       ),
                );
        }
 
@@ -311,6 +330,63 @@
                        }
                }
 
+               $revision = $this->setupTestRevisionsForEventGeneration( 
$newId, $oldId, $username, $lang, $pages, $title );
+
+               $events = array();
+               $this->setupEventCallbackForEventGeneration(
+                       function ( EchoEvent $event ) use ( &$events ) {
+                               $events[] = array(
+                                       'type' => $event->getType(),
+                                       'agent' => 
$event->getAgent()->getName(),
+                               );
+                               return false;
+                       }
+               );
+
+               // enable mention failure and success notifications
+               $this->setMwGlobals( 'wgEchoMentionStatusNotifications', true );
+
+               EchoDiscussionParser::generateEventsForRevision( $revision );
+
+               $this->assertEquals( $expected, $events );
+       }
+
+       public function testGenerateEventsForRevision_tooManyMentionsFailure() {
+               $expected = array(
+                       array(
+                               'type' => 'mention-too-many',
+                               'agent' => 'Admin',
+                               'max-mentions' => 5,
+                       ),
+               );
+
+               $revision = $this->setupTestRevisionsForEventGeneration( 
747747749, 747747747, 'Admin', 'en', array(), 'UTPage' );
+
+               $events = array();
+               $this->setupEventCallbackForEventGeneration(
+                       function ( EchoEvent $event ) use ( &$events ) {
+                               $events[] = array(
+                                       'type' => $event->getType(),
+                                       'agent' => 
$event->getAgent()->getName(),
+                                       'max-mentions' => 
$event->getExtraParam( 'max-mentions' ),
+                               );
+                               return false;
+                       }
+               );
+
+               $this->setMwGlobals( array(
+                       // enable mention failure and success notifications
+                       'wgEchoMentionStatusNotifications' => true,
+                       // lower limit for the mention-too-many notification
+                       'wgEchoMaxMentionsCount' => 5
+               ) );
+
+               EchoDiscussionParser::generateEventsForRevision( $revision );
+
+               $this->assertEquals( $expected, $events );
+       }
+
+       private function setupTestRevisionsForEventGeneration( $newId, $oldId, 
$username, $lang, $pages, $title ) {
                $langObj = Language::factory( $lang );
                $this->setMwGlobals( array(
                        // this global is used by the code that interprets the 
namespace part of
@@ -387,31 +463,18 @@
                $property = $class->getProperty( 'revisionInterpretationCache' 
);
                $property->setAccessible( true );
                $property->setValue( array( $revision->getId() => $output ) );
+               return $revision;
+       }
 
+       private function setupEventCallbackForEventGeneration( callable 
$callback ) {
                // to catch the generated event, I'm going to attach a callback 
to the
                // hook that's being run just prior to sending the 
notifications out
-               $events = array();
-               $callback = function ( EchoEvent $event ) use ( &$events ) {
-                       $events[] = array(
-                               'type' => $event->getType(),
-                               'agent' => $event->getAgent()->getName(),
-                       );
-
-                       // don't let the event go out, abort from within this 
hook
-                       return false;
-               };
-
                // can't use setMwGlobals here, so I'll just re-attach to the 
same key
                // for every dataProvider value (and don't worry, I'm removing 
it on
                // tearDown too - I just felt the attaching should be happening 
here
                // instead of on setUp, or code would get too messy)
                global $wgHooks;
                $wgHooks['BeforeEchoEventInsert'][999] = $callback;
-
-               // finally, dear god, start generating the events already!
-               EchoDiscussionParser::generateEventsForRevision( $revision );
-
-               $this->assertEquals( $expected, $events );
        }
 
        // TODO test cases for:
diff --git a/tests/phpunit/revision_txt/747747747.txt 
b/tests/phpunit/revision_txt/747747747.txt
new file mode 100644
index 0000000..6da5594
--- /dev/null
+++ b/tests/phpunit/revision_txt/747747747.txt
@@ -0,0 +1,3 @@
+== Hello World ==
+
+Hello!
diff --git a/tests/phpunit/revision_txt/747747748.txt 
b/tests/phpunit/revision_txt/747747748.txt
new file mode 100644
index 0000000..169ebc9
--- /dev/null
+++ b/tests/phpunit/revision_txt/747747748.txt
@@ -0,0 +1,11 @@
+== Hello World ==
+
+Hello!
+
+== Hello Users ==
+
+Try to mention non existing users:
+
+[[User:Ping]] [[User:Po?ng]] [[User:Peng/subpage]]
+
+[[:User:Admin|Admin]] 13:22, 23 June 2016 (UTC)
\ No newline at end of file
diff --git a/tests/phpunit/revision_txt/747747749.txt 
b/tests/phpunit/revision_txt/747747749.txt
new file mode 100644
index 0000000..8782510
--- /dev/null
+++ b/tests/phpunit/revision_txt/747747749.txt
@@ -0,0 +1,11 @@
+== Hello World ==
+
+Hello!
+
+== Hello Users ==
+
+Try to mention non existing users:
+
+[[User:Test11]] [[User:Buster7]] [[User:Kudpung]] [[User:Samwalton9]] 
[[User:PatHadley]] [[User:PauloEduardo]]
+
+[[:User:Admin|Admin]] 13:22, 23 June 2016 (UTC)
\ No newline at end of file

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

Gerrit-MessageType: merged
Gerrit-Change-Id: I388bdc3714feb9a2865a5ad10dbeabb0a6a09a4f
Gerrit-PatchSet: 35
Gerrit-Project: mediawiki/extensions/Echo
Gerrit-Branch: master
Gerrit-Owner: WMDE-Fisch <[email protected]>
Gerrit-Reviewer: Addshore <[email protected]>
Gerrit-Reviewer: Catrope <[email protected]>
Gerrit-Reviewer: Gabriel Birke <[email protected]>
Gerrit-Reviewer: Jakob <[email protected]>
Gerrit-Reviewer: Kai Nissen (WMDE) <[email protected]>
Gerrit-Reviewer: Matěj Suchánek <[email protected]>
Gerrit-Reviewer: Sbisson <[email protected]>
Gerrit-Reviewer: Siebrand <[email protected]>
Gerrit-Reviewer: Tobias Gritschacher <[email protected]>
Gerrit-Reviewer: WMDE-Fisch <[email protected]>
Gerrit-Reviewer: WMDE-leszek <[email protected]>
Gerrit-Reviewer: jenkins-bot <>

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

Reply via email to