Merge "New primary and secondary link behavior for Echo"

This commit is contained in:
jenkins-bot 2013-07-01 19:38:20 +00:00 committed by Gerrit Code Review
commit bed40a6933
12 changed files with 346 additions and 51 deletions

View file

@ -69,23 +69,28 @@ $messages['en'] = array(
// Notification
'echo-quotation-marks' => '"$1"',
'notification-link-text-view-message' => 'View message',
'notification-link-text-view-mention' => 'View mention',
'notification-link-text-view-changes' => 'View changes',
'notification-link-text-view-page' => 'View page',
'notification-link-text-view-edit' => 'View edit',
'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-edit-talk-page-flyout2' => '$1 {{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 [[:$3]].',
'notification-page-linked-flyout' => '$2 was {{GENDER:$1|linked}} from [[:$3]].',
'notification-add-comment2' => '[[User:$1|$1]] {{GENDER:$1|commented}} on "[[$3|$2]]" on the "$4" talk page',
'notification-add-talkpage-topic2' => '[[User:$1|$1]] {{GENDER:$1|posted}} a new topic "$2" on [[$3]]',
'notification-add-talkpage-topic-yours2' => '[[User:$1|$1]] {{GENDER:$1|sent}} you a message: "[[$3#$2|$2]]"',
'notification-add-comment-yours2' => '[[User:$1|$1]] {{GENDER:$1|commented}} on "[[$3#$2|$2]]" on your talk page',
'notification-mention' => '[[User:$1|$1]] {{GENDER:$1|mentioned}} you on [[$3#$2|$3]].',
'notification-mention-flyout' => '<b>$1</b> {{GENDER:$1|mentioned}} you on [[$3#$2|$3]].',
'notification-mention-flyout' => '$1 {{GENDER:$1|mentioned}} you on [[$3#$2|$3]].',
'notification-user-rights' => 'Your user rights [[Special:Log/rights/$1|were {{GENDER:$1|changed}}]] by [[User:$1|$1]]. $2. [[Special:ListGroupRights|Learn more]]',
'notification-user-rights-flyout' => 'Your user rights were {{GENDER:$1|changed}} by <b>$1</b>. $2. [[Special:ListGroupRights|Learn more]]',
'notification-user-rights-flyout' => 'Your user rights were {{GENDER:$1|changed}} by $1. $2. [[Special:ListGroupRights|Learn more]]',
'notification-user-rights-add' => 'You are now a member of {{PLURAL:$2|this group|these groups}}: $1',
'notification-user-rights-remove' => 'You are no longer a member of {{PLURAL:$2|this group|these groups}}: $1',
'notification-new-user' => "Welcome to {{SITENAME}}, $1! We're glad you're here.",
'notification-reverted2' => 'Your {{PLURAL:$4|edit on [[:$2]] has|edits on [[:$2]] have}} been {{GENDER:$1|reverted}} by [[User:$1|$1]] $3',
'notification-reverted-flyout2' => 'Your {{PLURAL:$4|edit on <b>$2</b> has|edits on <b>$2</b> have}} been {{GENDER:$1|reverted}} by <b>$1</b> $3',
'notification-reverted-flyout2' => 'Your {{PLURAL:$4|edit on $2 has|edits on $2 have}} been {{GENDER:$1|reverted}} by $1 $3',
'notification-edit-talk-page-email-subject2' => 'You have a new talkpage message on {{SITENAME}}',
'notification-edit-talk-page-email-body3' => '$1
@ -289,6 +294,11 @@ Parameters:
'echo-feedback' => 'Text for a link that goes to a feedback survey shown at [[Special:Notifications]].
{{Identical|Feedback}}',
'echo-quotation-marks' => 'Puts the edit summary in quotation marks. Only translate if different than English.',
'notification-link-text-view-message' => 'Label for button that links to a message on your talk page.',
'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. This is an alternative to the wording in {{msg-mw|notification-link-text-view-edit}}, which serves essentially the same function.',
'notification-link-text-view-page' => 'Label for button that links to a page.',
'notification-link-text-view-edit' => 'Label for button that links to a "diff" view showing an edit made to a page. This is an alternative to the wording in {{msg-mw|notification-link-text-view-changes}}, which serves essentially the same function.',
'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.
@ -351,7 +361,7 @@ See also:
* $3 - the page title of the discussion",
'notification-mention-flyout' => "Flyout-specific format for displaying notifications of a comment including a link to another user's user page.
Parameters:
* <b>$1</b> - the username of the person who mentioned you, plain text. Can be used for GENDER.
* $1 - the username of the person who mentioned you, plain text. Can be used for GENDER.
* $2 - the section title of the discussion
* $3 - the page title of the discussion",
'notification-user-rights' => 'Format for displaying notifications of a user right change in notification page. Parameters:

View file

@ -418,6 +418,8 @@ $wgEchoNotifications = array(
'icon' => 'site',
),
'edit-user-talk' => array(
'primary-link' => array( 'message' => 'notification-link-text-view-message', 'destination' => 'section' ),
'secondary-link' => array( 'message' => 'notification-link-text-view-changes', 'destination' => 'diff' ),
'category' => 'edit-user-talk',
'group' => 'interactive',
'bundle' => array( 'web' => true, 'email' => false ),
@ -439,6 +441,7 @@ $wgEchoNotifications = array(
'icon' => 'chat',
),
'reverted' => array(
'primary-link' => array( 'message' => 'notification-link-text-view-edit', 'destination' => 'diff' ),
'category' => 'reverted',
'group' => 'negative',
'formatter-class' => 'EchoEditFormatter',
@ -456,6 +459,7 @@ $wgEchoNotifications = array(
'icon' => 'revert',
),
'page-linked' => array(
'primary-link' => array( 'message' => 'notification-link-text-view-page', 'destination' => 'link-from-page' ),
'category' => 'article-linked',
'group' => 'neutral',
'bundle' => array( 'web' => true, 'email' => true ),
@ -478,6 +482,8 @@ $wgEchoNotifications = array(
'icon' => 'linked',
),
'mention' => array(
'primary-link' => array( 'message' => 'notification-link-text-view-mention', 'destination' => 'section' ),
'secondary-link' => array( 'message' => 'notification-link-text-view-changes', 'destination' => 'diff' ),
'category' => 'mention',
'group' => 'interactive',
'formatter-class' => 'EchoCommentFormatter',
@ -495,6 +501,7 @@ $wgEchoNotifications = array(
'icon' => 'chat',
),
'user-rights' => array(
'primary-link' => array( 'message' => 'notification-learn-more', 'destination' => 'user-rights-list' ),
'category' => 'system',
'group' => 'neutral',
'formatter-class' => 'EchoUserRightsFormatter',

View file

@ -1,5 +1,8 @@
<?php
/**
* This class represents the controller for notifications and includes functions
* for dealing with notification categories.
*/
class EchoNotificationController {
static protected $blacklist;
static protected $userWhitelist;

View file

@ -203,8 +203,13 @@ class EchoBasicFormatter extends EchoNotificationFormatter {
$content .= Xml::tags( 'div', array( 'class' => 'mw-echo-payload' ), $payload ) . "\n";
}
// Add timestamp
$content .= $this->formatTimestamp( $event->getTimestamp() );
// Add footer (timestamp and secondary link)
$content .= $this->formatFooter( $event, $user );
// Add the primary link (hidden)
if ( $this->outputFormat === 'flyout' ) {
$content .= $this->getLink( $event, $user, 'primary' );
}
$output .= Xml::tags( 'div', array( 'class' => 'mw-echo-content' ), $content ) . "\n";
@ -306,7 +311,7 @@ class EchoBasicFormatter extends EchoNotificationFormatter {
protected function formatPayload( $payload, $event, $user ) {
switch ( $payload ) {
case 'summary':
return $this->formatSummary( $event, $user );
return $this->formatRevisionComment( $event, $user );
break;
case 'comment-text':
return $this->formatCommentText( $event, $user );
@ -358,6 +363,31 @@ class EchoBasicFormatter extends EchoNotificationFormatter {
return substr( $wgParser->guessLegacySectionNameFromWikiText( $extra['section-title'] ), 1 );
}
/**
* Build the footer for the notification (timestamp and secondary link)
* @param EchoEvent $event
* @param User $user The user to format the notification for.
* @return String HTML
*/
protected function formatFooter( $event, $user ) {
global $wgLang;
$timestamp = $this->formatTimestamp( $event->getTimestamp() );
$notificationFooterContent = array();
if ( $this->outputFormat === 'flyout' ) {
$secondaryLink = $this->getLink( $event, $user, 'secondary' );
if ( $secondaryLink ) {
$notificationFooterContent[] = $timestamp;
$notificationFooterContent[] = $secondaryLink;
$footer = $wgLang->pipeList( $notificationFooterContent );
}
}
if ( !$notificationFooterContent ) {
$footer = $timestamp;
}
return Xml::tags( 'div', array( 'class' => 'mw-echo-notification-footer' ), $footer ) . "\n";
}
/**
* Generate links based on output format and passed properties
* $event EchoEvent
@ -507,6 +537,95 @@ class EchoBasicFormatter extends EchoNotificationFormatter {
}
}
/**
* Get the URL for the primary or secondary link for an event
*
* @param EchoEvent $event
* @param User $user The user receiving the notification
* @param String $rank 'primary' or 'secondary' (default is 'primary')
* @param boolean $local True to return a local (relative) URL, false to
* return a full URL (for email for example) (default is true)
* @param boolean $urlOnly True to return only the URL without the <a> tag,
* false to return a full anchor link (default is false)
* @param String $style A style attribute to apply to the anchor, e.g.
* 'border: 1px solid green; text-decoration: none;' (optional)
* @return String URL for link, or HTML for anchor tag, or empty string
*/
protected function getLink( $event, $user, $rank = 'primary', $local = true, $urlOnly = false, $style = '' ) {
$destination = $event->getLinkDestination( $rank );
if ( !$destination ) {
return '';
}
// Get link parameters based on the destination
list( $target, $query ) = $this->getLinkParams( $event, $user, $destination );
if ( !$target ) {
return '';
}
if ( $urlOnly ) {
if ( $local ) {
return $target->getLinkURL( $query );
} else {
return $target->getCanonicalURL( $query );
}
} else {
$message = wfMessage( $event->getLinkMessage( $rank ) )->text();
$attribs = array( 'class' => "mw-echo-notification-{$rank}-link" );
if ( $style ) {
$attribs['style'] = $style;
}
$options = array();
// If local is false, return an absolute url using HTTP protocol
if ( !$local ) {
$options[] = 'http';
}
return Linker::link( $target, $message, $attribs, $query, $options );
}
}
/**
* Helper function for getLink()
*
* @param EchoEvent $event
* @param User $user The user receiving the notification
* @param String $destination The destination type for the link, e.g. 'agent'
* @return Array including target and query parameters
*/
protected function getLinkParams( $event, $user, $destination ) {
$target = null;
$query = array();
// Set up link parameters based on the destination
switch ( $destination ) {
case 'agent':
if ( $event->getAgent() ) {
$target = $event->getAgent()->getUserPage();
}
break;
case 'title':
$target = $event->getTitle();
break;
case 'section':
$target = $event->getTitle();
if ( $target ) {
$fragment = $this->formatSubjectAnchor( $event );
if ( $fragment ) {
$target->setFragment( "#$fragment" );
}
}
break;
case 'diff':
$eventData = $event->getExtra();
if ( isset( $eventData['revid'] ) && $event->getTitle() ) {
$target = $event->getTitle();
$query = array(
'oldid' => $eventData['revid'],
'diff' => 'prev',
);
}
break;
}
return array( $target, $query );
}
/**
* Helper function for processParams()
*

View file

@ -27,7 +27,7 @@ class EchoEditFormatter extends EchoBasicFormatter {
);
$this->setTitleLink( $event, $message, $props );
} elseif ( $param === 'summary' ) {
$message->params( $this->formatSummary( $event, $user ) );
$message->params( $this->formatRevisionComment( $event, $user ) );
} elseif ( $param === 'number' ) {
$eventData = $event->getExtra();
// The folliwing is a bit of a hack...

View file

@ -112,41 +112,67 @@ abstract class EchoNotificationFormatter {
protected function formatTimestamp( $ts ) {
$timestamp = new MWTimestamp( $ts );
$ts = $timestamp->getHumanTimestamp();
if ( $this->outputFormat === 'html' || $this->outputFormat === 'flyout' ) {
return Xml::element( 'div', array( 'class' => 'mw-echo-timestamp' ), $ts );
} else {
return $ts;
}
return $ts;
}
/**
* Formats an edit summary
* TODO: implement parsed option for notifications archive page (where we can use all the html)
* @param $event EchoEvent that the notification is for.
* @param $user User to format the notification for.
* @return string The edit summary (or empty string)
* Formats a revision comment (i.e. edit summary)
* @param EchoEvent $event The event that the notification is for.
* @param User $user The user to format the notification for.
* @return String The revision comment (or empty string)
*/
protected function formatSummary( $event, $user ) {
protected function formatRevisionComment( $event, $user ) {
$revision = $event->getRevision();
if ( $revision === null ) {
return '';
} elseif( !$event->userCan( Revision::DELETED_COMMENT, $user ) ) {
return wfMessage( 'rev-deleted-comment' )->text();
} else {
$summary = $revision->getComment( Revision::FOR_THIS_USER, $user );
$comment = $revision->getComment( Revision::FOR_THIS_USER, $user );
if ( $this->outputFormat === 'html' || $this->outputFormat === 'flyout' ) {
// Parse the edit summary
$summary = Linker::formatComment( $summary, $revision->getTitle() );
if ( $summary ) {
$summary = wfMessage( 'echo-quotation-marks', $summary )->inContentLanguage()->plain();
$summary = Xml::tags( 'span', array( 'class' => 'comment' ), $summary );
$summary = Xml::tags( 'div', array( 'class' => 'mw-echo-summary' ), $summary );
if ( $this->outputFormat === 'html' ) {
// Parse the revision comment
$comment = Linker::formatComment( $comment, $revision->getTitle() );
} else {
$comment = $this->customFormatRevisionComment( $comment );
}
if ( $comment ) {
// No quotation marks for now, but this might need to be reverted.
// $comment = wfMessage( 'echo-quotation-marks', $comment )->inContentLanguage()->plain();
$comment = Xml::tags( 'span', array( 'class' => 'comment' ), $comment );
$comment = Xml::tags( 'div', array( 'class' => 'mw-echo-edit-summary' ), $comment );
}
}
return $summary;
return $comment;
}
}
/**
* Formats a revision comment (i.e. edit summary) for use in the flyout (and
* possibly HTML email). This is a helper function for formatRevisionComment.
* @param String $comment The raw revision comment
* @return String The formatted revision comment (or empty string)
*/
private function customFormatRevisionComment( $comment ) {
// Strip wikitext from the revision comment and manually convert autocomments.
// This bypasses the creation of the arrow section links (→) and turns
// any other links into plain text.
$comment = FeedItem::stripComment( $comment );
$comment = trim( htmlspecialchars( $comment ) );
// Convert autocomments (e.g. section titles) from raw form
// Example input: '/* Foobar */ My changes'
// Output: '<span class='autocomment'>Foobar:</span> My changes'
preg_match( "!(.*)/\*\s*(.*?)\s*\*/(.*)!", $comment, $matches );
if ( $matches ) {
$section = $matches[2];
if ( $matches[3] ) {
// Add a colon after the section name
$section .= wfMessage( 'colon-separator' )->inContentLanguage()->escaped();
}
// Add standard span tag for autocomment
$comment = $matches[1] . "<span class='autocomment'>" . $section . "</span>" . $matches[3];
}
return $comment;
}
}

View file

@ -143,4 +143,34 @@ class EchoPageLinkFormatter extends EchoBasicFormatter {
}
}
/**
* Helper function for getLink()
*
* @param EchoEvent $event
* @param User $user The user receiving the notification
* @param String $destination The destination type for the link
* @return Array including target and query parameters
*/
protected function getLinkParams( $event, $user, $destination ) {
$target = null;
$query = array();
// Set up link parameters based on the destination (or pass to parent)
switch ( $destination ) {
case 'link-from-page':
if ( $this->bundleData['use-bundle'] ) {
if ( $event->getTitle() ) {
$target = SpecialPage::getTitleFor( 'WhatLinksHere', $event->getTitle()->getPrefixedText() );
}
} else {
$extra = self::extractExtra( $event->getExtra() );
if ( $this->isTitleSet( $extra ) ) {
$target = Title::newFromId( $extra['link-from-page-id'] );
}
}
break;
default:
return parent::getLinkParams( $event, $user, $destination );
}
return array( $target, $query );
}
}

View file

@ -32,12 +32,33 @@ class EchoUserRightsFormatter extends EchoBasicFormatter {
}
}
$message->params( $wgLang->semicolonList( $list ) );
break;
break;
default:
parent::processParam( $event, $param, $message, $user );
break;
break;
}
}
/**
* Helper function for getLink()
*
* @param EchoEvent $event
* @param User $user The user receiving the notification
* @param String $destination The destination type for the link
* @return Array including target and query parameters
*/
protected function getLinkParams( $event, $user, $destination ) {
$target = null;
$query = array();
// Set up link parameters based on the destination (or pass to parent)
switch ( $destination ) {
case 'user-rights-list':
$target = SpeicalPage::getTitleFor( 'ListsGroupRights' );
break;
default:
return parent::getLinkParams( $event, $user, $destination );
}
return array( $target, $query );
}
}

View file

@ -409,6 +409,34 @@ class EchoEvent {
return EchoNotificationController::getNotificationCategory( $this->type );
}
/**
* Get the message key of the primary or secondary link for a notification type.
*
* @param $rank String 'primary' or 'secondary'
* @return String i18n message key
*/
public function getLinkMessage( $rank ) {
global $wgEchoNotifications;
if ( isset( $wgEchoNotifications[$this->type][$rank.'-link']['message'] ) ) {
return $wgEchoNotifications[$this->type][$rank.'-link']['message'];
}
return '';
}
/**
* Get the link destination of the primary or secondary link for a notification type.
*
* @param $rank String 'primary' or 'secondary'
* @return String The link destination, e.g. 'agent'
*/
public function getLinkDestination( $rank ) {
global $wgEchoNotifications;
if ( isset( $wgEchoNotifications[$this->type][$rank.'-link']['destination'] ) ) {
return $wgEchoNotifications[$this->type][$rank.'-link']['destination'];
}
return '';
}
/**
* @return string
*/

View file

@ -11,7 +11,8 @@
.mw-echo-payload {
margin-top: 0.3em;
}
.mw-echo-timestamp {
/* Including .mw-echo-timestamp for backwards compat */
.mw-echo-timestamp, .mw-echo-notification-footer {
color: #6D6D6D;
font-size: 9px;
}
@ -67,11 +68,9 @@
margin-left: 1em;
cursor: pointer;
}
.mw-echo-notification span.comment {
font-style: normal;
}
.mw-echo-notification span.autocomment {
color: inherit;
font-style: normal;
}
.mw-echo-icon {
width: 30px;

View file

@ -29,7 +29,7 @@
overflow: auto;
}
#p-personal .mw-echo-overlay li.mw-echo-notification {
background-color: #F0F0F0;
background-color: #F1F1F1;
display: block;
float: none;
border-bottom: 1px solid #DDDDDD;
@ -40,16 +40,28 @@
line-height: 16px;
}
#p-personal .mw-echo-overlay li.mw-echo-notification:hover {
background-color: #F8F8F8;
background-color: #F9F9F9;
}
#p-personal .mw-echo-overlay li.mw-echo-notification.mw-echo-unread,
#p-personal .mw-echo-overlay li.mw-echo-notification.mw-echo-unread:hover {
#p-personal .mw-echo-overlay li.mw-echo-notification.mw-echo-unread {
background-color: white;
}
#p-personal .mw-echo-overlay li.mw-echo-notification.mw-echo-unread:hover {
background-color: #F9F9F9;
}
#p-personal .mw-echo-overlay li.mw-echo-notification:last-child {
border-bottom: none;
}
#p-personal .mw-echo-title a {
font-weight: bold;
text-decoration: none;
}
#p-personal .mw-echo-overlay a.mw-echo-grey-link {
color: #6D6D6D;
}
.mw-echo-notification-primary-link {
display: none;
}
.mw-echo-overlay-title {
/*border-bottom: 1px solid #A7D7F9;*/
font-size: 13px;
@ -93,21 +105,28 @@
width: 149px;
min-height: 14px;
font-size: 13px;
font-weight: bold;
/* @embed */
background: url(../icons/NotificationsPage-ltr.png) no-repeat 17% 50% !important;
}
#mw-echo-overlay-link:hover {
text-decoration: none;
}
#mw-echo-overlay-pref-link {
display: block;
float: left;
width: 174px;
min-height: 14px;
font-size: 13px;
font-weight: bold;
padding: 15px 15px 15px 35px;
border-left: 1px solid #DDDDDD;
/* @embed */
background: url(../icons/Settings.png) no-repeat 5% 50% !important;
}
#mw-echo-overlay-pref-link:hover {
text-decoration: none;
}
#pt-notifications {
position: relative;

View file

@ -69,6 +69,29 @@
.append( data['*'] )
.appendTo( $ul );
// Grey links in the notification title and footer (except on hover)
$li.find( '.mw-echo-title a, .mw-echo-notification-footer a' )
.addClass( 'mw-echo-grey-link' );
$li.hover(
function() {
$( this ).find( '.mw-echo-title a' ).removeClass( 'mw-echo-grey-link' );
},
function() {
$( this ).find( '.mw-echo-title a' ).addClass( 'mw-echo-grey-link' );
}
);
// If there is a primary link, make the entire notification clickable.
if ( $li.find( '.mw-echo-notification-primary-link' ).length ) {
$li.css( 'cursor', 'pointer' );
$li.click( function() {
if ( mw.echo.clickThroughEnabled ) {
// Log the clickthrough
mw.echo.logInteraction( 'notification-link-click', 'flyout', data.id, data.type );
}
window.location.href = $li.find( '.mw-echo-notification-primary-link' ).attr( 'href' );
} );
}
mw.echo.setupNotificationLogging( $li, 'flyout' );
if ( !data.read ) {
@ -126,14 +149,6 @@
) {
// Add the 'mark all as read' button to the title area
$title.append( $markReadButton );
// Display a feedback link if there is no 'mark read' button
} else {
$( '<a>' )
.attr( 'href', mw.config.get( 'wgEchoFeedbackPage' ) + '?c=flyout' )
.attr( 'id', 'mw-echo-overlay-feedback-link' )
.attr( 'target', '_blank' )
.text( mw.msg( 'echo-feedback' ) )
.appendTo( $title );
}
// Add the header to the title area
@ -167,11 +182,20 @@
$overlayFooter.append(
$( '<a>' )
.attr( 'id', 'mw-echo-overlay-link' )
.addClass( 'mw-echo-grey-link' )
.attr( 'href', mw.util.wikiGetlink( 'Special:Notifications' ) )
.text( mw.msg( 'echo-overlay-link' ) )
.click( function () {
mw.echo.logInteraction( 'ui-archive-link-click', 'flyout' );
} )
.hover(
function() {
$( this ).removeClass( 'mw-echo-grey-link' );
},
function() {
$( this ).addClass( 'mw-echo-grey-link' );
}
)
);
// add link to notification preferences
@ -179,10 +203,19 @@
$prefLink
.clone()
.attr( 'id', 'mw-echo-overlay-pref-link' )
.addClass( 'mw-echo-grey-link' )
.attr( 'href', $prefLink.attr( 'href' ) + '#mw-prefsection-echo' )
.click( function () {
mw.echo.logInteraction( 'ui-prefs-click', 'flyout' );
} )
.hover(
function() {
$( this ).removeClass( 'mw-echo-grey-link' );
},
function() {
$( this ).addClass( 'mw-echo-grey-link' );
}
)
);
$overlay.append( $overlayFooter );