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

Change subject: Link directly to the edited section from edit-user-talk events
......................................................................


Link directly to the edited section from edit-user-talk events

Adjusted the edit-user-talk event creation to detect and record which section
of the talk page was edited. Flyout, special page, and email messages have
been adjusted to use this section title as a URL fragment when available.

Bug: 46937
Change-Id: I161e2ffda2f2540f64de90cc621fb3b69479d0db
---
M Echo.i18n.php
M Echo.php
M formatters/BasicFormatter.php
M formatters/CommentFormatter.php
M formatters/EditFormatter.php
M formatters/NotificationFormatter.php
M formatters/UserRightsFormatter.php
M includes/DiscussionParser.php
A tests/NotificationFormatterTest.php
A tests/TalkPageFunctionalTest.php
10 files changed, 286 insertions(+), 25 deletions(-)

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



diff --git a/Echo.i18n.php b/Echo.i18n.php
index 0aa22e7..cc48b8a 100644
--- a/Echo.i18n.php
+++ b/Echo.i18n.php
@@ -63,8 +63,8 @@
 
        // Notification
        'echo-quotation-marks' => '"$1"',
-       'notification-edit-talk-page2' => '[[User:$1|$1]] {{GENDER:$1|posted}} 
on your [[User talk:$2|talk page]].',
-       'notification-edit-talk-page-flyout2' => '<b>$1</b> 
{{GENDER:$1|posted}} on your [[User talk:$2|talk page]].',
+       'notification-edit-talk-page2' => '[[User:$1|$1]] {{GENDER:$1|posted}} 
on your [[User talk:$2#$3|talk page]].',
+       'notification-edit-talk-page-flyout2' => '<b>$1</b> 
{{GENDER:$1|posted}} on your [[User talk:$2#$3|talk page]].',
        'notification-page-linked' => '[[:$2]] was {{GENDER:$1|linked}} from 
[[:$3]]: [[Special:WhatLinksHere/$2|See all links to this page]]',
        'notification-page-linked-flyout' => '<b>$2</b> was 
{{GENDER:$1|linked}} from <b>$3</b>: [[Special:WhatLinksHere/$2|See all links 
to this page]]',
        'notification-add-comment2' => '[[User:$1|$1]] {{GENDER:$1|commented}} 
on "[[$3|$2]]" on the "$4" talk page',
@@ -280,12 +280,14 @@
        'notification-edit-talk-page2' => "Format for displaying notifications 
of a user talk page being edited
 * $1 is the username of the person who edited, plain text. Can be used for 
GENDER.
 * $2 is the current user's name, used in the link to their talk page.
+* $3 is the section title of the discussion, if any, used in the link to their 
talk page
 See also:
 * {{msg-mw|Notification-edit-talk-page-flyout2}}
 * {{msg-mw|Notification-add-talkpage-topic2}}",
        'notification-edit-talk-page-flyout2' => "Flyout-specific format for 
displaying notifications of a user talk page being edited
 * $1 is the username of the person who edited, plain text. Can be used for 
GENDER.
 * $2 is the current user's name, used in the link to their talk page.
+* $3 is the section title of the discussion, if any, used in the link to their 
talk page
 See also:
 * {{msg-mw|Notification-edit-talk-page2}}
 * {{msg-mw|Notification-add-talkpage-topic2}}",
diff --git a/Echo.php b/Echo.php
index a2baef3..44a01f3 100644
--- a/Echo.php
+++ b/Echo.php
@@ -377,12 +377,12 @@
                'bundle' => array( 'web' => true, 'email' => false ),
                'formatter-class' => 'EchoEditFormatter',
                'title-message' => 'notification-edit-talk-page2',
-               'title-params' => array( 'agent', 'user' ),
+               'title-params' => array( 'agent', 'user', 'subject-anchor' ),
                'bundle-message' => 'notification-edit-talk-page-bundle',
                'bundle-params' => array( 'agent', 'user', 
'agent-other-display', 'agent-other-count' ),
                'payload' => array( 'summary' ),
                'flyout-message' => 'notification-edit-talk-page-flyout2',
-               'flyout-params' => array( 'agent', 'user' ),
+               'flyout-params' => array( 'agent', 'user', 'subject-anchor' ),
                'email-subject-message' => 
'notification-edit-talk-page-email-subject2',
                'email-body-message' => 
'notification-edit-talk-page-email-body2',
                'email-body-params' => array( 'email-intro', 'titlelink', 
'summary', 'email-footer' ),
diff --git a/formatters/BasicFormatter.php b/formatters/BasicFormatter.php
index b294f55..eb7a57b 100644
--- a/formatters/BasicFormatter.php
+++ b/formatters/BasicFormatter.php
@@ -334,6 +334,25 @@
        }
 
        /**
+        * Extract the subject anchor (linkable portion of the edited page) from
+        * the event.
+        *
+        * @param $event EchoEvent The event to format the subject anchor of
+        * @return string The anchor on page, or an empty string
+        */
+       protected function formatSubjectAnchor( EchoEvent $event ) {
+               global $wgParser;
+
+               $extra = $event->getExtra();
+               if ( empty( $extra['section-title'] ) ) {
+                       return '';
+               }
+
+               // Strip out #, keeping # in the i18n message makes it look 
more clear
+               return substr( $wgParser->guessLegacySectionNameFromWikiText( 
$extra['section-title'] ), 1 );
+       }
+
+       /**
         * Generate links based on output format and passed properties
         * $event EchoEvent
         * $message Message
@@ -353,8 +372,11 @@
                }
 
                if ( isset( $props['fragment'] ) ) {
-                       $title->setFragment( '#' . $props['fragment'] );
+                       $fragment = $props['fragment'];
+               } else {
+                       $fragment = $this->formatSubjectAnchor( $event );
                }
+               $title->setFragment( "#$fragment" );
 
                if ( $this->outputFormat === 'html' || $this->outputFormat === 
'flyout' ) {
                        $class = array();
diff --git a/formatters/CommentFormatter.php b/formatters/CommentFormatter.php
index 2fd7c28..8e7f333 100644
--- a/formatters/CommentFormatter.php
+++ b/formatters/CommentFormatter.php
@@ -48,28 +48,14 @@
         */
        protected function processParam( $event, $param, $message, $user ) {
                $extra = $event->getExtra();
-               if ( $param === 'subject-anchor' ) {
-                       global $wgParser;
-                       if ( !empty( $extra['section-title'] ) ) {
-                               $message->params(
-                                       // Strip out #, keeping # in the i18n 
message makes it look more clear
-                                       substr( 
$wgParser->guessLegacySectionNameFromWikiText( $extra['section-title'] ), 1 )
-                               );
-                       } else {
-                               $message->params( '' );
-                       }
-               } elseif ( $param === 'content-page' ) {
+               if ( $param === 'content-page' ) {
                        if ( $event->getTitle() ) {
                                $message->params( 
$event->getTitle()->getSubjectPage()->getPrefixedText() );
                        } else {
                                $message->params( '' );
                        }
                } elseif ( $param === 'subject-link' ) {
-                       $prop = array();
-                       if ( isset( $extra['section-title'] ) && 
$extra['section-title'] ) {
-                               $prop['fragment'] = $extra['section-title'];
-                       }
-                       $this->setTitleLink( $event, $message, $prop );
+                       $this->setTitleLink( $event, $message );
                } else {
                        parent::processParam( $event, $param, $message, $user );
                }
diff --git a/formatters/EditFormatter.php b/formatters/EditFormatter.php
index 3b9cc06..41ac126 100644
--- a/formatters/EditFormatter.php
+++ b/formatters/EditFormatter.php
@@ -9,7 +9,9 @@
         * @param $user User
         */
        protected function processParam( $event, $param, $message, $user ) {
-               if ( $param === 'difflink' ) {
+               if ( $param === 'subject-anchor' ) {
+                       $message->params( $this->formatSubjectAnchor( $event ) 
);
+               } elseif ( $param === 'difflink' ) {
                        $eventData = $event->getExtra();
                        if ( !isset( $eventData['revid'] ) ) {
                                $message->params( '' );
diff --git a/formatters/NotificationFormatter.php 
b/formatters/NotificationFormatter.php
index 8dc8c3e..cb47205 100644
--- a/formatters/NotificationFormatter.php
+++ b/formatters/NotificationFormatter.php
@@ -62,7 +62,7 @@
 
        /**
         * Set the output format that the notification will be displayed in.
-        * @param $format string A valid output format (by default, 'text', 
'html', and 'email' are allowed)
+        * @param $format string A valid output format (by default, 'text', 
'html', 'flyout', and 'email' are allowed)
         * @throws MWException
         */
        public function setOutputFormat( $format ) {
diff --git a/formatters/UserRightsFormatter.php 
b/formatters/UserRightsFormatter.php
index ca3307e..ddb2de0 100644
--- a/formatters/UserRightsFormatter.php
+++ b/formatters/UserRightsFormatter.php
@@ -22,7 +22,7 @@
                                $list = array();
 
                                foreach ( array( 'add', 'remove' ) as $action ) 
{
-                                       if ( $extra[$action] ) {
+                                       if ( isset( $extra[$action] ) && 
$extra[$action] ) {
                                                // Messages that can be used 
here:
                                                // * 
notification-user-rights-add
                                                // * 
notification-user-rights-remove
diff --git a/includes/DiscussionParser.php b/includes/DiscussionParser.php
index 60cf30c..6cdebef 100644
--- a/includes/DiscussionParser.php
+++ b/includes/DiscussionParser.php
@@ -84,7 +84,11 @@
                                        EchoEvent::create( array(
                                                'type' => 'edit-user-talk',
                                                'title' => $title,
-                                               'extra' => array( 'revid' => 
$revision->getID(), 'minoredit' => $revision->isMinor() ),
+                                               'extra' => array(
+                                                       'revid' => 
$revision->getID(),
+                                                       'minoredit' => 
$revision->isMinor(),
+                                                       'section-title' => 
self::detectSectionTitle( $interpretation ),
+                                               ),
                                                'agent' => $user,
                                        ) );
                                }
@@ -93,6 +97,31 @@
        }
 
        /**
+        * Attempts to determine what section title the edit was performed 
under (if any)
+        *
+        * @param $interpretation array Results of 
self::getChangeInterpretationForRevision
+        * @return string The section title if found otherwise a blank string
+        */
+       protected static function detectSectionTitle( array $interpretation ) {
+               $header = '';
+               foreach ( $interpretation as $action ) {
+                       switch( $action['type'] ) {
+                       case 'add-comment':
+                               $header = self::extractHeader( 
$action['full-section'] );
+                               break;
+
+                       case 'new-section-with-comment':
+                               $header = self::extractHeader( 
$action['content'] );
+                               break;
+                       }
+                       if ( $header ) {
+                               return $header;
+                       }
+               }
+               return '';
+       }
+
+       /**
         * For an action taken on a talk page, notify users whose user pages
         * are linked.
         * @param $header string The subject line for the discussion.
diff --git a/tests/NotificationFormatterTest.php 
b/tests/NotificationFormatterTest.php
new file mode 100644
index 0000000..251777e
--- /dev/null
+++ b/tests/NotificationFormatterTest.php
@@ -0,0 +1,132 @@
+
+<?php
+
+class EchoNotificationFormatterTest extends MediaWikiTestCase {
+
+       public static function provider_editUserTalkEmail() {
+               return array(
+                       array( '/Main_Page#Section_8/', 'Section 8' ),
+                       array( '/Main_Page[^#]/', null ),
+               );
+       }
+
+       /**
+        * @dataProvider provider_editUserTalkEmail
+        */
+       public function testEditUserTalkEmailNotificationLink( $pattern, 
$sectionTitle ) {
+               $event = $this->mockEvent( 'edit-user-talk', array(
+                       'section-title' => $sectionTitle,
+               ) );
+               $event->expects( $this->any() )
+                       ->method( 'getTitle' )
+                       ->will( $this->returnValue( Title::newMainPage() ) );
+
+               $formatted = $this->format( $event, 'email' );
+               $this->assertRegExp( $pattern, $formatted['body'] );
+       }
+
+       public static function provider_editUserTalk() {
+               return array(
+                       array( '/[[User talk:[^#]+#moar_cowbell|talk page]]/', 
'moar_cowbell', 'text' ),
+                       array( '/#moar_cowbell/', 'moar_cowbell', 'html' ),
+                       array( '/#moar_cowbell/', 'moar_cowbell', 'flyout' ),
+               );
+       }
+
+       /**
+        * @dataProvider provider_editUserTalk
+        */
+       public function testEditUserTalkFlyoutSectionLinkFragment( $pattern, 
$sectionTitle, $format ) {
+               // Required hack so parser doesnt turn the links into redlinks 
which contain no fragment
+               LinkCache::singleton()->addGoodLinkObj( 42, Title::newFromText( 
'127.0.0.1', NS_USER_TALK ) );
+
+               $event = $this->mockEvent( 'edit-user-talk', array(
+                       'section-title' => $sectionTitle,
+               ) );
+
+               $this->assertRegExp( $pattern, $this->format( $event, $format ) 
);
+       }
+
+       public function provider_formatterDoesntFail() {
+               // Remove events from this array once they have specific tests 
for their formatting
+               $untested = array(
+                       'welcome' => array(),
+                       'reverted' => array(
+                               'revid' => 42,
+                               'reverted-user-id' => 77,
+                               'reverted-revision-id' => 13,
+                               'method' => 'undo',
+                       ),
+                       'page-linked' => array(
+                               'link-from-namespace' => 0,
+                               'link-from-title' => 'Karl Sims',
+                       ),
+                       'mention' => array(
+                               'content' => 'lorem ipsum dolar sit amet',
+                               'section-title' => 'Zombies',
+                               'revid' => 42,
+                               'mentionedusers' => array( 101 => 101 ),
+                       ),
+                       'user-rights' => array(
+                               'user' => 187,
+                               'add' => array( 'aaa', 'bbb' ),
+                               'remove' => array( 'other' ),
+                       ),
+               );
+               $formats = array( 'html', 'flyout', 'email', 'text' );
+               $tests = array();
+               foreach ( $untested as $type => $extra ) {
+                       foreach ( $formats as $format ) {
+                               // Run tests with blank extra data and with the 
provided extra data
+                               $tests[] = array( $format, $type, $extra );
+                               $tests[] = array( $format, $type, array() );
+                       }
+               }
+
+               return $tests;
+       }
+
+       /**
+        * @dataProvider provider_formatterDoesntFail
+        */
+       public function testFormatterDoesntFail( $format, $type, array $extra ) 
{
+               $result = $this->format( $this->mockEvent( $type, $extra ), 
$format );
+
+               // generic assertion, could do better
+               if ( $format === 'email' ) {
+                       $this->assertInternalType( 'array', $result );
+                       $this->assertCount( 3, $result );
+               } else {
+                       $this->assertInternalType( 'string', $result );
+                       $this->assertGreaterThan( 0, strlen( $result ) );
+               }
+       }
+
+       protected function format( EchoEvent $event, $format, $type = 'web', 
array $params = array() ) {
+               global $wgEchoNotifications;
+
+               $params += $wgEchoNotifications[ $event->getType() ];
+               $formatter = EchoNotificationFormatter::factory( $params );
+               $formatter->setOutputFormat( $format );
+
+               return $formatter->format( $event, new User, $type );
+       }
+
+       protected function mockEvent( $type, array $extra = array(), Revision 
$rev = null ) {
+               $event = $this->getMockBuilder( 'EchoEvent' )
+                       ->disableOriginalConstructor()
+                       ->getMock();
+               $event->expects( $this->any() )
+                       ->method( 'getType' )
+                       ->will( $this->returnValue( $type ) );
+               $event->expects( $this->any() )
+                       ->method( 'getExtra' )
+                       ->will( $this->returnValue( $extra ) );
+               if ( $rev !== null ) {
+                       $event->expects( $this->any() )
+                               ->method( 'getRevision' )
+                               ->will( $this->returnValue( $rev ) );
+               }
+               return $event;
+       }
+}
diff --git a/tests/TalkPageFunctionalTest.php b/tests/TalkPageFunctionalTest.php
new file mode 100644
index 0000000..e5af785
--- /dev/null
+++ b/tests/TalkPageFunctionalTest.php
@@ -0,0 +1,88 @@
+<?php
+/**
+ * @group DataBase
+ * @group medium
+ */
+class EchoTalkPageFunctionalTest extends ApiTestCase {
+
+       protected $dbr;
+
+       public function setUp() {
+               parent::setUp();
+               $this->dbr = MWEchoDbFactory::getDB( DB_SLAVE );
+       }
+
+       /**
+        * Creates and updates a user talk page a few times to ensure proper 
events are
+        * created. The user performing the edits is self::$users['sysop'].
+        */
+       public function testAddCommentsToTalkPage() {
+               $editor = self::$users['sysop']->user->getName();
+               $talkPage = self::$users['uploader']->user->getName();
+               // A set of messages which will be inserted
+               $messages = array(
+                       'Moar Cowbell',
+                       "I can haz test\n\nplz?", // checks that the parser 
allows multi-line comments
+                       'blah blah',
+               );
+
+               $messageCount = 0;
+               $this->assertCount($messageCount, $this->fetchAllEvents() );
+
+               // Start a talkpage
+               $content = "== Section 8 ==\n\n" . $this->signedMessage( 
$editor, $messages[$messageCount] );
+               $this->editPage( $talkPage, $content, '', NS_USER_TALK );
+
+               // Ensure the proper event was created
+               $events = $this->fetchAllEvents();
+               $this->assertCount(1 + $messageCount, $events, 'After initial 
edit a single event must exist.'); // +1 is due to 0 index
+               $row = end( $events );
+               $this->assertEquals( 'edit-user-talk', $row->event_type );
+               $this->assertEventSectionTitle( 'Section 8', $row );
+
+               // Add another message to the talk page
+               $messageCount++;
+               $content .= $this->signedMessage( $editor, 
$messages[$messageCount] );
+               $this->editPage( $talkPage, $content, '', NS_USER_TALK );
+
+               // Ensure another event was created
+               $events = $this->fetchAllEvents();
+               $this->assertCount(1 + $messageCount, $events);
+               $row = end( $events );
+               $this->assertEquals( 'edit-user-talk', $row->event_type );
+               $this->assertEventSectionTitle( 'Section 8', $row );
+
+               // Add a new section and a message within it
+               $messageCount++;
+               $content .= "\n\n== EE ==\n\n" . $this->signedMessage( $editor, 
$messages[$messageCount] );
+               $this->editPage( $talkPage, $content, '', NS_USER_TALK );
+
+               // Ensure this event has the new section title
+               $events = $this->fetchAllEvents();
+               $this->assertCount(1 + $messageCount, $events);
+               $row = end( $events );
+               $this->assertEquals( 'edit-user-talk', $row->event_type );
+               $this->assertEventSectionTitle( 'EE', $row );
+       }
+
+       protected function assertEventSectionTitle( $sectionTitle, $row ) {
+               $this->assertNotNull( $row->event_extra, 'Event must contain 
extra data.' );
+               $extra = unserialize( $row->event_extra );
+               $this->assertArrayHasKey( 'section-title', $extra, 'Extra data 
must include a section-title key.' );
+               $this->assertEquals( $sectionTitle, $extra['section-title'], 
'Detected section title must match' );
+       }
+
+       /**
+        * @return array All events in db sorted from oldest to newest
+        */
+       protected function fetchAllEvents() {
+               $res = $this->dbr->select( 'echo_event', array( '*' ), array(), 
__METHOD__, array( 'ORDER BY' => 'event_id ASC' ) );
+
+               return iterator_to_array( $res );
+       }
+
+       protected function signedMessage( $name, $content = 'Moar cowbell', 
$depth = 1 ) {
+               return str_repeat(':', $depth)." $content [[User:$name|$name]] 
([[User talk:$name|$name]]) 00:17, 7 May 2013 (UTC)\n";
+       }
+
+}

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

Gerrit-MessageType: merged
Gerrit-Change-Id: I161e2ffda2f2540f64de90cc621fb3b69479d0db
Gerrit-PatchSet: 8
Gerrit-Project: mediawiki/extensions/Echo
Gerrit-Branch: master
Gerrit-Owner: EBernhardson (WMF) <[email protected]>
Gerrit-Reviewer: Bsitu <[email protected]>
Gerrit-Reviewer: EBernhardson (WMF) <[email protected]>
Gerrit-Reviewer: Jeroen De Dauw <[email protected]>
Gerrit-Reviewer: Kaldari <[email protected]>
Gerrit-Reviewer: Lwelling <[email protected]>
Gerrit-Reviewer: Mattflaschen <[email protected]>
Gerrit-Reviewer: jenkins-bot

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

Reply via email to