mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/DiscussionTools
synced 2024-11-23 16:06:53 +00:00
Comment thanking
Bug: T249893 Change-Id: I64f7abc26bfc3e7b226340934a637a555edf754f
This commit is contained in:
parent
d1bffdee70
commit
a86897f890
|
@ -10,6 +10,7 @@ $cfg['directory_list'] = array_merge(
|
||||||
'../../extensions/EventLogging',
|
'../../extensions/EventLogging',
|
||||||
'../../extensions/Gadgets',
|
'../../extensions/Gadgets',
|
||||||
'../../extensions/BetaFeatures',
|
'../../extensions/BetaFeatures',
|
||||||
|
'../../extensions/Thanks',
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -21,6 +22,7 @@ $cfg['exclude_analysis_directory_list'] = array_merge(
|
||||||
'../../extensions/EventLogging',
|
'../../extensions/EventLogging',
|
||||||
'../../extensions/Gadgets',
|
'../../extensions/Gadgets',
|
||||||
'../../extensions/BetaFeatures',
|
'../../extensions/BetaFeatures',
|
||||||
|
'../../extensions/Thanks',
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,7 @@
|
||||||
"topicsubscriptions.js",
|
"topicsubscriptions.js",
|
||||||
"mobile.js",
|
"mobile.js",
|
||||||
"overflowMenu.js",
|
"overflowMenu.js",
|
||||||
|
"thanks.js",
|
||||||
"LedeSectionDialog.js",
|
"LedeSectionDialog.js",
|
||||||
{
|
{
|
||||||
"name": "controller/contLangMessages.json",
|
"name": "controller/contLangMessages.json",
|
||||||
|
@ -154,7 +155,12 @@
|
||||||
"discussiontools-topicsubscription-notify-unsubscribed-body",
|
"discussiontools-topicsubscription-notify-unsubscribed-body",
|
||||||
"discussiontools-topicsubscription-notify-unsubscribed-title",
|
"discussiontools-topicsubscription-notify-unsubscribed-title",
|
||||||
"pagetitle",
|
"pagetitle",
|
||||||
"skin-view-edit"
|
"skin-view-edit",
|
||||||
|
"cancel",
|
||||||
|
"thanks-button-thank",
|
||||||
|
"thanks-button-thanked",
|
||||||
|
"thanks-confirmation2",
|
||||||
|
"thanks-thanked-notice"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"ext.discussionTools.minervaicons": {
|
"ext.discussionTools.minervaicons": {
|
||||||
|
@ -418,6 +424,7 @@
|
||||||
"RevisionDataUpdates": "dataupdates",
|
"RevisionDataUpdates": "dataupdates",
|
||||||
"LoadExtensionSchemaUpdates": "installer",
|
"LoadExtensionSchemaUpdates": "installer",
|
||||||
"GetDoubleUnderscoreIDs": "parser",
|
"GetDoubleUnderscoreIDs": "parser",
|
||||||
|
"ApiMain::moduleManager": "api",
|
||||||
"ParserAfterTidy": "parser",
|
"ParserAfterTidy": "parser",
|
||||||
"ParserOutputPostCacheTransform": "parser",
|
"ParserOutputPostCacheTransform": "parser",
|
||||||
"BeforeDisplayNoArticleText": "page",
|
"BeforeDisplayNoArticleText": "page",
|
||||||
|
@ -446,6 +453,9 @@
|
||||||
"installer": {
|
"installer": {
|
||||||
"class": "MediaWiki\\Extension\\DiscussionTools\\Hooks\\InstallerHooks"
|
"class": "MediaWiki\\Extension\\DiscussionTools\\Hooks\\InstallerHooks"
|
||||||
},
|
},
|
||||||
|
"api": {
|
||||||
|
"class": "MediaWiki\\Extension\\DiscussionTools\\Hooks\\ApiHooks"
|
||||||
|
},
|
||||||
"page": {
|
"page": {
|
||||||
"class": "MediaWiki\\Extension\\DiscussionTools\\Hooks\\PageHooks",
|
"class": "MediaWiki\\Extension\\DiscussionTools\\Hooks\\PageHooks",
|
||||||
"services": [
|
"services": [
|
||||||
|
@ -561,6 +571,10 @@
|
||||||
"value": true,
|
"value": true,
|
||||||
"description": "Enable permalinks frontend features: 1. Convert signature timestamps to comment links. 2. Show notification when the target comment is found on another page."
|
"description": "Enable permalinks frontend features: 1. Convert signature timestamps to comment links. 2. Show notification when the target comment is found on another page."
|
||||||
},
|
},
|
||||||
|
"DiscussionToolsEnableThanks": {
|
||||||
|
"value": true,
|
||||||
|
"description": "Show a button to thank individual comments. Requires the 'Thanks' extension."
|
||||||
|
},
|
||||||
"DiscussionToolsAutoTopicSubEditor": {
|
"DiscussionToolsAutoTopicSubEditor": {
|
||||||
"value": "any",
|
"value": "any",
|
||||||
"description": "Editor which triggers automatic topic subscriptions. Either 'discussiontoolsapi' for edits made using DiscussionTools' API (e.g. reply and new topic tools), or 'any' for any editor."
|
"description": "Editor which triggers automatic topic subscriptions. Either 'discussiontoolsapi' for edits made using DiscussionTools' API (e.g. reply and new topic tools), or 'any' for any editor."
|
||||||
|
|
|
@ -38,5 +38,7 @@
|
||||||
"apihelp-discussiontoolssubscribe-param-commentname": "Name of the topic to subscribe to (or unsubscribe from)",
|
"apihelp-discussiontoolssubscribe-param-commentname": "Name of the topic to subscribe to (or unsubscribe from)",
|
||||||
"apihelp-discussiontoolssubscribe-param-page": "A page on which the topic appears",
|
"apihelp-discussiontoolssubscribe-param-page": "A page on which the topic appears",
|
||||||
"apihelp-discussiontoolssubscribe-param-subscribe": "True to subscribe, false to unsubscribe",
|
"apihelp-discussiontoolssubscribe-param-subscribe": "True to subscribe, false to unsubscribe",
|
||||||
"apihelp-discussiontoolssubscribe-summary": "Subscribe (or unsubscribe) to receive notifications about a topic."
|
"apihelp-discussiontoolssubscribe-summary": "Subscribe (or unsubscribe) to receive notifications about a topic.",
|
||||||
|
"apihelp-discussiontoolsthank-param-commentid": "ID of the comment to thank.",
|
||||||
|
"apihelp-discussiontoolsthank-summary": "Send a public thank-you notification for a comment."
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,5 +40,7 @@
|
||||||
"apihelp-discussiontoolssubscribe-param-commentname": "{{doc-apihelp-param|discussiontoolssubscribe|commentname}}",
|
"apihelp-discussiontoolssubscribe-param-commentname": "{{doc-apihelp-param|discussiontoolssubscribe|commentname}}",
|
||||||
"apihelp-discussiontoolssubscribe-param-page": "{{doc-apihelp-param|discussiontoolssubscribe|page}}",
|
"apihelp-discussiontoolssubscribe-param-page": "{{doc-apihelp-param|discussiontoolssubscribe|page}}",
|
||||||
"apihelp-discussiontoolssubscribe-param-subscribe": "{{doc-apihelp-param|discussiontoolssubscribe|subscribe}}",
|
"apihelp-discussiontoolssubscribe-param-subscribe": "{{doc-apihelp-param|discussiontoolssubscribe|subscribe}}",
|
||||||
"apihelp-discussiontoolssubscribe-summary": "{{doc-apihelp-summary|discussiontoolssubscribe}}"
|
"apihelp-discussiontoolssubscribe-summary": "{{doc-apihelp-summary|discussiontoolssubscribe}}",
|
||||||
|
"apihelp-discussiontoolsthank-param-commentid": "{{doc-apihelp-summary|discussiontoolsthank|commentid}}",
|
||||||
|
"apihelp-discussiontoolsthank-summary": "{{doc-apihelp-summary|discussiontoolsthank}}"
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,6 +71,9 @@
|
||||||
"discussiontools-notification-added-topic-header-bundled": "{{PLURAL:$1|One new topic|$1 new topics|100=99+ new topics}} on \"<strong>$2</strong>\".",
|
"discussiontools-notification-added-topic-header-bundled": "{{PLURAL:$1|One new topic|$1 new topics|100=99+ new topics}} on \"<strong>$2</strong>\".",
|
||||||
"discussiontools-notification-added-topic-header-compact": "$3: <em>$4</em>",
|
"discussiontools-notification-added-topic-header-compact": "$3: <em>$4</em>",
|
||||||
"discussiontools-notification-added-topic-view": "View topic",
|
"discussiontools-notification-added-topic-view": "View topic",
|
||||||
|
"discussiontools-notification-comment-thank-header": "$1 {{GENDER:$2|thanked}} {{GENDER:$4|you}} for your comment in \"<strong>$3</strong>\".",
|
||||||
|
"discussiontools-notification-comment-thank-header-bundled": "{{PLURAL:$1|One person|$1 people|100=99+ people}} thanked {{GENDER:$3|you}} for your comment in \"<strong>$2</strong>\".",
|
||||||
|
"discussiontools-notification-comment-thank-header-compact": "$1 {{GENDER:$2|thanked}} {{GENDER:$3|you}}.",
|
||||||
"discussiontools-notification-removed-topic-body": "{{GENDER:|You}} might no longer receive notifications about {{PLURAL:$1|this topic|these topics}}.",
|
"discussiontools-notification-removed-topic-body": "{{GENDER:|You}} might no longer receive notifications about {{PLURAL:$1|this topic|these topics}}.",
|
||||||
"discussiontools-notification-removed-topic-header": "Topic \"<strong>$4</strong>\" was archived or removed from $3.",
|
"discussiontools-notification-removed-topic-header": "Topic \"<strong>$4</strong>\" was archived or removed from $3.",
|
||||||
"discussiontools-notification-removed-topic-header-bundled": "{{PLURAL:$1|One topic was|$1 topics were|100=99+ topics were}} archived or removed from $2.",
|
"discussiontools-notification-removed-topic-header-bundled": "{{PLURAL:$1|One topic was|$1 topics were|100=99+ topics were}} archived or removed from $2.",
|
||||||
|
|
|
@ -85,6 +85,9 @@
|
||||||
"discussiontools-notification-added-topic-header-bundled": "Notification header for when multiple new topics have been started on a page.\n\n* $1 - number of new topics.\n* $2 - discussion page title.",
|
"discussiontools-notification-added-topic-header-bundled": "Notification header for when multiple new topics have been started on a page.\n\n* $1 - number of new topics.\n* $2 - discussion page title.",
|
||||||
"discussiontools-notification-added-topic-header-compact": "Notification compact header for when a new topic has been started on a page.",
|
"discussiontools-notification-added-topic-header-compact": "Notification compact header for when a new topic has been started on a page.",
|
||||||
"discussiontools-notification-added-topic-view": "Label for button to view topic that was just posted.",
|
"discussiontools-notification-added-topic-view": "Label for button to view topic that was just posted.",
|
||||||
|
"discussiontools-notification-comment-thank-header": "Notification header for when you have been thanked for a comment. Parameters:\n* $1 is the username of the user sending the thanks (not suitable for GENDER).\n* $2 is the thanking user's name for use in GENDER.\n* $3 is the topic in which the thanked comment was posted.\n* $4 is the username of the user being thanked, for use in GENDER.",
|
||||||
|
"discussiontools-notification-comment-thank-header-bundled": "Notification header for when you have received multiple thanks for a comment. Parameters:\n* $1 is the number of users who sent thanks for the same comment. When used with PLURAL, the value 100 represents more than 99.\n* $2 is the topic in which the thanked comment was posted.\n* $3 is the username of the user being thanked, for use in GENDER.",
|
||||||
|
"discussiontools-notification-comment-thank-header-compact": "Notification compact header for when you have been thanked for a comment. Parameters:\n* $1 is the username of the user sending the thanks (not suitable for GENDER).\n* $2 is the thanking user's name for use in GENDER.\n* $3 is the username of the user being thanked, for use in GENDER.",
|
||||||
"discussiontools-notification-removed-topic-body": "Notification body text for when multiple topics were removed from a page.\n\nFollows {{msg-mw|discussiontools-notification-removed-topic-header}} or {{msg-mw|discussiontools-notification-removed-topic-header-bundled}}.\n\nParameters:\n* $1 - the number of topics removed",
|
"discussiontools-notification-removed-topic-body": "Notification body text for when multiple topics were removed from a page.\n\nFollows {{msg-mw|discussiontools-notification-removed-topic-header}} or {{msg-mw|discussiontools-notification-removed-topic-header-bundled}}.\n\nParameters:\n* $1 - the number of topics removed",
|
||||||
"discussiontools-notification-removed-topic-header": "Notification header text for when a topic was removed from a page. Parameters:\n* $1 - the formatted username of the user who replied to the topic (unused)\n* $2 - the username for gender purposes (unused)\n* $3 - title of the page\n* $4 - title of the topic",
|
"discussiontools-notification-removed-topic-header": "Notification header text for when a topic was removed from a page. Parameters:\n* $1 - the formatted username of the user who replied to the topic (unused)\n* $2 - the username for gender purposes (unused)\n* $3 - title of the page\n* $4 - title of the topic",
|
||||||
"discussiontools-notification-removed-topic-header-bundled": "Notification header text for when multiple topics were removed from a page. Parameters:\n* $1 - the number of topics removed\n* $2 - title of the page",
|
"discussiontools-notification-removed-topic-header-bundled": "Notification header text for when multiple topics were removed from a page. Parameters:\n* $1 - the number of topics removed\n* $2 - title of the page",
|
||||||
|
|
158
includes/ApiDiscussionToolsThank.php
Normal file
158
includes/ApiDiscussionToolsThank.php
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MediaWiki\Extension\DiscussionTools;
|
||||||
|
|
||||||
|
use ApiBase;
|
||||||
|
use ApiMain;
|
||||||
|
use ApiUsageException;
|
||||||
|
use MediaWiki\Extension\DiscussionTools\Hooks\HookUtils;
|
||||||
|
use MediaWiki\Extension\DiscussionTools\ThreadItem\ContentCommentItem;
|
||||||
|
use MediaWiki\Extension\Notifications\Model\Event;
|
||||||
|
use MediaWiki\Extension\Thanks\Api\ApiThank;
|
||||||
|
use MediaWiki\Extension\Thanks\Storage\LogStore;
|
||||||
|
use MediaWiki\Extension\VisualEditor\ApiParsoidTrait;
|
||||||
|
use MediaWiki\Permissions\PermissionManager;
|
||||||
|
use MediaWiki\Revision\RevisionLookup;
|
||||||
|
use MediaWiki\User\UserFactory;
|
||||||
|
use Title;
|
||||||
|
use Wikimedia\ParamValidator\ParamValidator;
|
||||||
|
use Wikimedia\Parsoid\Core\ResourceLimitExceededException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* API module to send DiscussionTools comment thanks notifications
|
||||||
|
*
|
||||||
|
* @ingroup API
|
||||||
|
* @ingroup Extensions
|
||||||
|
*/
|
||||||
|
|
||||||
|
class ApiDiscussionToolsThank extends ApiThank {
|
||||||
|
|
||||||
|
use ApiDiscussionToolsTrait;
|
||||||
|
use ApiParsoidTrait;
|
||||||
|
|
||||||
|
private RevisionLookup $revisionLookup;
|
||||||
|
private UserFactory $userFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
ApiMain $main,
|
||||||
|
$action,
|
||||||
|
PermissionManager $permissionManager,
|
||||||
|
LogStore $storage,
|
||||||
|
RevisionLookup $revisionLookup,
|
||||||
|
UserFactory $userFactory
|
||||||
|
) {
|
||||||
|
parent::__construct( $main, $action, $permissionManager, $storage );
|
||||||
|
$this->revisionLookup = $revisionLookup;
|
||||||
|
$this->userFactory = $userFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
* @throws ApiUsageException
|
||||||
|
* @throws ResourceLimitExceededException
|
||||||
|
*/
|
||||||
|
public function execute() {
|
||||||
|
$user = $this->getUser();
|
||||||
|
$this->dieOnBadUser( $user );
|
||||||
|
$this->dieOnUserBlockedFromThanks( $user );
|
||||||
|
|
||||||
|
$params = $this->extractRequestParams();
|
||||||
|
|
||||||
|
$title = Title::newFromText( $params['page'] );
|
||||||
|
$commentId = $params['commentid'];
|
||||||
|
|
||||||
|
if ( !$title ) {
|
||||||
|
$this->dieWithError( [ 'apierror-invalidtitle', wfEscapeWikiText( $params['page'] ) ] );
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Using the data in the permalinks database would be much
|
||||||
|
// faster, we just wouldn't have the comment content.
|
||||||
|
|
||||||
|
// Support oldid?
|
||||||
|
$revision = $this->revisionLookup->getRevisionByTitle( $title );
|
||||||
|
if ( !$revision ) {
|
||||||
|
throw ApiUsageException::newWithMessage(
|
||||||
|
$this,
|
||||||
|
[ 'apierror-missingrev-title', wfEscapeWikiText( $title->getPrefixedText() ) ],
|
||||||
|
'nosuchrevid'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$threadItemSet = HookUtils::parseRevisionParsoidHtml( $revision, __METHOD__ );
|
||||||
|
|
||||||
|
$comment = $threadItemSet->findCommentById( $commentId );
|
||||||
|
|
||||||
|
if ( !$comment || !( $comment instanceof ContentCommentItem ) ) {
|
||||||
|
$this->dieWithError( [ 'apierror-discussiontools-commentid-notfound', $commentId ] );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $user->getRequest()->getSessionData( "discussiontools-thanked-{$comment->getId()}" ) ) {
|
||||||
|
$this->markResultSuccess( $comment->getAuthor() );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$uniqueId = "discussiontools-{$comment->getId()}";
|
||||||
|
// Do one last check to make sure we haven't sent Thanks before
|
||||||
|
if ( $this->haveAlreadyThanked( $user, $uniqueId ) ) {
|
||||||
|
// Pretend the thanks were sent
|
||||||
|
$this->markResultSuccess( $comment->getAuthor() );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$recipient = $this->userFactory->newFromName( $comment->getAuthor() );
|
||||||
|
if ( !$recipient || !$recipient->getId() ) {
|
||||||
|
$this->dieWithError( 'thanks-error-invalidrecipient', 'invalidrecipient' );
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->dieOnBadRecipient( $user, $recipient );
|
||||||
|
|
||||||
|
$heading = $comment->getSubscribableHeading();
|
||||||
|
if ( !$heading ) {
|
||||||
|
$heading = $comment->getHeading();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the notification via Echo extension
|
||||||
|
Event::create( [
|
||||||
|
'type' => 'dt-thank',
|
||||||
|
'title' => $title,
|
||||||
|
'extra' => [
|
||||||
|
'comment-id' => $comment->getId(),
|
||||||
|
'comment-name' => $comment->getName(),
|
||||||
|
'content' => $comment->getBodyText( true ),
|
||||||
|
'section-title' => $heading->getLinkableTitle(),
|
||||||
|
'thanked-user-id' => $recipient->getId(),
|
||||||
|
'revid' => $revision->getId(),
|
||||||
|
],
|
||||||
|
'agent' => $user,
|
||||||
|
] );
|
||||||
|
|
||||||
|
// And mark the thank in session for a cheaper check to prevent duplicates (T48690).
|
||||||
|
$user->getRequest()->setSessionData( "discussiontools-thanked-{$comment->getId()}", true );
|
||||||
|
// Set success message.
|
||||||
|
$this->markResultSuccess( $recipient->getName() );
|
||||||
|
$this->logThanks( $user, $recipient, $uniqueId );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function getAllowedParams() {
|
||||||
|
return [
|
||||||
|
'page' => [
|
||||||
|
ParamValidator::PARAM_REQUIRED => true,
|
||||||
|
// Message will exist if DiscussionTools is installed as VE is a dependency
|
||||||
|
ApiBase::PARAM_HELP_MSG => 'apihelp-visualeditoredit-param-page',
|
||||||
|
],
|
||||||
|
'commentid' => [
|
||||||
|
ParamValidator::PARAM_REQUIRED => true,
|
||||||
|
ParamValidator::PARAM_TYPE => 'string',
|
||||||
|
],
|
||||||
|
'token' => [
|
||||||
|
ParamValidator::PARAM_REQUIRED => true,
|
||||||
|
ParamValidator::PARAM_TYPE => 'string',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
43
includes/Hooks/ApiHooks.php
Normal file
43
includes/Hooks/ApiHooks.php
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* DiscussionTools API hooks
|
||||||
|
*
|
||||||
|
* @file
|
||||||
|
* @ingroup Extensions
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace MediaWiki\Extension\DiscussionTools\Hooks;
|
||||||
|
|
||||||
|
use ApiModuleManager;
|
||||||
|
use ExtensionRegistry;
|
||||||
|
use MediaWiki\Api\Hook\ApiMain__moduleManagerHook;
|
||||||
|
use MediaWiki\Extension\DiscussionTools\ApiDiscussionToolsThank;
|
||||||
|
|
||||||
|
// phpcs:disable MediaWiki.NamingConventions.LowerCamelFunctionsName.FunctionName
|
||||||
|
|
||||||
|
class ApiHooks implements
|
||||||
|
ApiMain__moduleManagerHook
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param ApiModuleManager $moduleManager
|
||||||
|
* @return bool|void
|
||||||
|
*/
|
||||||
|
public function onApiMain__moduleManager( $moduleManager ) {
|
||||||
|
if ( ExtensionRegistry::getInstance()->isLoaded( 'Thanks' ) ) {
|
||||||
|
$moduleManager->addModule(
|
||||||
|
'discussiontoolsthank',
|
||||||
|
'action',
|
||||||
|
[
|
||||||
|
'class' => ApiDiscussionToolsThank::class,
|
||||||
|
'services' => [
|
||||||
|
'PermissionManager',
|
||||||
|
'ThanksLogStore',
|
||||||
|
'RevisionLookup',
|
||||||
|
'UserFactory',
|
||||||
|
]
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,8 +9,11 @@
|
||||||
|
|
||||||
namespace MediaWiki\Extension\DiscussionTools\Hooks;
|
namespace MediaWiki\Extension\DiscussionTools\Hooks;
|
||||||
|
|
||||||
|
use ExtensionRegistry;
|
||||||
use IContextSource;
|
use IContextSource;
|
||||||
use MediaWiki\Extension\DiscussionTools\OverflowMenuItem;
|
use MediaWiki\Extension\DiscussionTools\OverflowMenuItem;
|
||||||
|
use MediaWiki\MediaWikiServices;
|
||||||
|
use MediaWiki\User\UserNameUtils;
|
||||||
|
|
||||||
class DiscussionToolsHooks implements
|
class DiscussionToolsHooks implements
|
||||||
DiscussionToolsAddOverflowMenuItemsHook
|
DiscussionToolsAddOverflowMenuItemsHook
|
||||||
|
@ -41,5 +44,26 @@ class DiscussionToolsHooks implements
|
||||||
2
|
2
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$dtConfig = MediaWikiServices::getInstance()->getConfigFactory()->makeConfig( 'discussiontools' );
|
||||||
|
if ( $dtConfig->get( 'DiscussionToolsEnableThanks' ) ) {
|
||||||
|
$user = $contextSource->getUser();
|
||||||
|
$showThanks = ExtensionRegistry::getInstance()->isLoaded( 'Thanks' );
|
||||||
|
if ( $showThanks && ( $threadItemData['type'] ?? null ) === 'comment' && $user->isNamed() ) {
|
||||||
|
$userNameUtils = MediaWikiServices::getInstance()->getUserNameUtils();
|
||||||
|
$recipient = $userNameUtils->getCanonical( $threadItemData['author'], UserNameUtils::RIGOR_NONE );
|
||||||
|
|
||||||
|
if (
|
||||||
|
$recipient !== $user->getName() &&
|
||||||
|
!$userNameUtils->isIP( $recipient )
|
||||||
|
) {
|
||||||
|
$overflowMenuItems[] = new OverflowMenuItem(
|
||||||
|
'thank',
|
||||||
|
'heart',
|
||||||
|
$contextSource->msg( 'thanks-button-thank' ),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,9 @@
|
||||||
|
|
||||||
namespace MediaWiki\Extension\DiscussionTools\Hooks;
|
namespace MediaWiki\Extension\DiscussionTools\Hooks;
|
||||||
|
|
||||||
|
use ExtensionRegistry;
|
||||||
use MediaWiki\Extension\DiscussionTools\Notifications\AddedTopicPresentationModel;
|
use MediaWiki\Extension\DiscussionTools\Notifications\AddedTopicPresentationModel;
|
||||||
|
use MediaWiki\Extension\DiscussionTools\Notifications\CommentThanksPresentationModel;
|
||||||
use MediaWiki\Extension\DiscussionTools\Notifications\EnhancedEchoEditUserTalkPresentationModel;
|
use MediaWiki\Extension\DiscussionTools\Notifications\EnhancedEchoEditUserTalkPresentationModel;
|
||||||
use MediaWiki\Extension\DiscussionTools\Notifications\EnhancedEchoMentionPresentationModel;
|
use MediaWiki\Extension\DiscussionTools\Notifications\EnhancedEchoMentionPresentationModel;
|
||||||
use MediaWiki\Extension\DiscussionTools\Notifications\EventDispatcher;
|
use MediaWiki\Extension\DiscussionTools\Notifications\EventDispatcher;
|
||||||
|
@ -101,6 +103,25 @@ class EchoHooks implements
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if ( ExtensionRegistry::getInstance()->isLoaded( 'Thanks' ) ) {
|
||||||
|
$notifications['dt-thank'] = [
|
||||||
|
'category' => 'edit-thank',
|
||||||
|
'group' => 'positive',
|
||||||
|
'section' => 'message',
|
||||||
|
'user-locators' => [
|
||||||
|
[
|
||||||
|
[ UserLocator::class, 'locateFromEventExtra' ],
|
||||||
|
[ 'thanked-user-id' ]
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'presentation-model' => CommentThanksPresentationModel::class,
|
||||||
|
'bundle' => [
|
||||||
|
'web' => true,
|
||||||
|
'expandable' => true,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
// Override default handlers
|
// Override default handlers
|
||||||
$notifications['edit-user-talk']['presentation-model'] = EnhancedEchoEditUserTalkPresentationModel::class;
|
$notifications['edit-user-talk']['presentation-model'] = EnhancedEchoEditUserTalkPresentationModel::class;
|
||||||
$notifications['mention']['presentation-model'] = EnhancedEchoMentionPresentationModel::class;
|
$notifications['mention']['presentation-model'] = EnhancedEchoMentionPresentationModel::class;
|
||||||
|
@ -116,6 +137,9 @@ class EchoHooks implements
|
||||||
$bundleString = $event->getType() . '-' . $event->getTitle()->getNamespace()
|
$bundleString = $event->getType() . '-' . $event->getTitle()->getNamespace()
|
||||||
. '-' . $event->getTitle()->getDBkey();
|
. '-' . $event->getTitle()->getDBkey();
|
||||||
break;
|
break;
|
||||||
|
case 'dt-thank':
|
||||||
|
$bundleString = $event->getType() . '-' . $event->getExtraParam( 'comment-name' );
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -304,12 +304,13 @@ class PageHooks implements
|
||||||
// Reply button: share
|
// Reply button: share
|
||||||
$output->addModuleStyles( 'oojs-ui.styles.icons-content' );
|
$output->addModuleStyles( 'oojs-ui.styles.icons-content' );
|
||||||
}
|
}
|
||||||
|
$output->addModuleStyles( [
|
||||||
|
// Overflow menu ('ellipsis' icon)
|
||||||
|
'oojs-ui.styles.icons-interactions',
|
||||||
|
] );
|
||||||
if ( $isMobile ) {
|
if ( $isMobile ) {
|
||||||
$output->addModuleStyles( [
|
$output->addModuleStyles( [
|
||||||
// Mobile overflow menu:
|
// Edit button in overflow menu ('edit' icon)
|
||||||
// ellipsis
|
|
||||||
'oojs-ui.styles.icons-interactions',
|
|
||||||
// edit
|
|
||||||
'oojs-ui.styles.icons-editing-core',
|
'oojs-ui.styles.icons-editing-core',
|
||||||
] );
|
] );
|
||||||
}
|
}
|
||||||
|
|
150
includes/Notifications/CommentThanksPresentationModel.php
Normal file
150
includes/Notifications/CommentThanksPresentationModel.php
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* EchoEventPresentationModel for comment thanks notifications
|
||||||
|
*
|
||||||
|
* @file
|
||||||
|
* @ingroup Extensions
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace MediaWiki\Extension\DiscussionTools\Notifications;
|
||||||
|
|
||||||
|
use Language;
|
||||||
|
use MediaWiki\Extension\Notifications\Formatters\EchoEventPresentationModel;
|
||||||
|
use MediaWiki\Extension\Notifications\Formatters\EchoPresentationModelSection;
|
||||||
|
use MediaWiki\Extension\Notifications\Model\Event;
|
||||||
|
use MediaWiki\Revision\RevisionRecord;
|
||||||
|
use Message;
|
||||||
|
use RawMessage;
|
||||||
|
use User;
|
||||||
|
|
||||||
|
class CommentThanksPresentationModel extends EchoEventPresentationModel {
|
||||||
|
|
||||||
|
use DiscussionToolsEventTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var EchoPresentationModelSection
|
||||||
|
*/
|
||||||
|
private $section;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
protected function __construct( Event $event, Language $language, User $user, $distributionType ) {
|
||||||
|
parent::__construct( $event, $language, $user, $distributionType );
|
||||||
|
$this->section = new EchoPresentationModelSection( $event, $user, $language );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function getIconType() {
|
||||||
|
return 'thanks';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function canRender() {
|
||||||
|
return (bool)$this->event->getTitle();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function getPrimaryLink() {
|
||||||
|
return [
|
||||||
|
'url' => $this->getCommentLink() ?: $this->event->getTitle()->getFullURL(),
|
||||||
|
'label' => $this->msg( 'discussiontools-notification-subscribed-new-comment-view' )->text()
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a link to the individual comment, if available.
|
||||||
|
*
|
||||||
|
* @return string|null Full URL linking to the comment, null if not available
|
||||||
|
*/
|
||||||
|
protected function getCommentLink(): ?string {
|
||||||
|
if ( !$this->userCan( RevisionRecord::DELETED_TEXT ) ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// Thanks notifications are bundled by comment-id, so the link will always be to a single comment
|
||||||
|
// (unlike in DiscussionToolsEventTrait)
|
||||||
|
$commentId = $this->event->getExtraParam( 'comment-id' );
|
||||||
|
if ( !$commentId ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
$title = $this->event->getTitle();
|
||||||
|
return $title->createFragmentTarget( $commentId )->getFullURL();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
protected function getHeaderMessageKey() {
|
||||||
|
if ( $this->isBundled() ) {
|
||||||
|
return 'discussiontools-notification-comment-thank-header-bundled';
|
||||||
|
} else {
|
||||||
|
return 'discussiontools-notification-comment-thank-header';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function getHeaderMessage() {
|
||||||
|
$title = $this->section->getTruncatedSectionTitle();
|
||||||
|
if ( !$title ) {
|
||||||
|
// Comment could have been at the top of the page before
|
||||||
|
// any section titles. Use the page title instead.
|
||||||
|
$title = $this->event->getTitle()->getPrefixedText();
|
||||||
|
}
|
||||||
|
if ( $this->isBundled() ) {
|
||||||
|
$count = $this->getNotificationCountForOutput();
|
||||||
|
$msg = $this->msg( $this->getHeaderMessageKey() );
|
||||||
|
|
||||||
|
// Params 1, 2, 3:
|
||||||
|
$msg->numParams( $count );
|
||||||
|
$msg->plaintextParams( $title );
|
||||||
|
$msg->params( $this->getViewingUserForGender() );
|
||||||
|
return $msg;
|
||||||
|
} else {
|
||||||
|
$msg = parent::getHeaderMessage();
|
||||||
|
// Params 3, 4:
|
||||||
|
$msg->plaintextParams( $title );
|
||||||
|
$msg->params( $this->getViewingUserForGender() );
|
||||||
|
return $msg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function getCompactHeaderMessage() {
|
||||||
|
$msg = $this->getMessageWithAgent( 'discussiontools-notification-comment-thank-header-compact' );
|
||||||
|
// Param 3:
|
||||||
|
$msg->params( $this->getViewingUserForGender() );
|
||||||
|
return $msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function getBodyMessage() {
|
||||||
|
if ( !$this->isBundled() ) {
|
||||||
|
return new RawMessage( '$1', [ Message::plaintextParam( $this->getContentSnippet() ) ] );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function getSecondaryLinks() {
|
||||||
|
$pageLink = $this->getPageLink( $this->event->getTitle(), '', true );
|
||||||
|
if ( $this->isBundled() ) {
|
||||||
|
return [ $pageLink ];
|
||||||
|
} else {
|
||||||
|
return [ $this->getAgentLink(), $pageLink ];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -27,6 +27,7 @@ let mobile = null;
|
||||||
if ( OO.ui.isMobile() && mw.config.get( 'skin' ) === 'minerva' ) {
|
if ( OO.ui.isMobile() && mw.config.get( 'skin' ) === 'minerva' ) {
|
||||||
mobile = require( './mobile.js' );
|
mobile = require( './mobile.js' );
|
||||||
}
|
}
|
||||||
|
require( './thanks.js' );
|
||||||
|
|
||||||
mw.messages.set( require( './controller/contLangMessages.json' ) );
|
mw.messages.set( require( './controller/contLangMessages.json' ) );
|
||||||
|
|
||||||
|
|
77
modules/thanks.js
Normal file
77
modules/thanks.js
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
const cacheKey = 'dt-thanks';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thank a comment item
|
||||||
|
*
|
||||||
|
* @param {CommentItem} commentItem Comment item
|
||||||
|
* @return {jQuery.Promise} Resolves when thanks successfully sent, rejects on error
|
||||||
|
*/
|
||||||
|
function thankComment( commentItem ) {
|
||||||
|
// TODO: Add recipient gender for messages
|
||||||
|
const recipientGender = 'unknown';
|
||||||
|
return OO.ui.confirm( mw.msg( 'thanks-confirmation2', mw.user ), {
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
action: 'accept',
|
||||||
|
label: mw.msg( 'thanks-button-thank', mw.user, recipientGender ),
|
||||||
|
flags: [ 'primary', 'progressive' ]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action: 'cancel',
|
||||||
|
label: mw.msg( 'cancel' ),
|
||||||
|
flags: 'safe'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
} ).then( ( confirmed ) => {
|
||||||
|
if ( !confirmed ) {
|
||||||
|
return $.Deferred().reject().promise();
|
||||||
|
}
|
||||||
|
|
||||||
|
const api = require( './controller.js' ).getApi();
|
||||||
|
|
||||||
|
return api.postWithToken( 'csrf', {
|
||||||
|
action: 'discussiontoolsthank',
|
||||||
|
// We don't need to store the correct transcluded comment page
|
||||||
|
// for a thank, any page the comment appears on will do.
|
||||||
|
page: mw.config.get( 'wgRelevantPageName' ),
|
||||||
|
commentid: commentItem.id
|
||||||
|
} ).then( () => {
|
||||||
|
mw.notify( mw.msg( 'thanks-thanked-notice', commentItem.author, recipientGender, mw.user ), { type: 'success' } );
|
||||||
|
cacheThanked( commentItem );
|
||||||
|
}, ( code, data ) => {
|
||||||
|
mw.notify( api.getErrorMessage( data ), { type: 'error' } );
|
||||||
|
return $.Deferred().reject().promise();
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
function isThanked( threadItem ) {
|
||||||
|
const cache = mw.storage.getObject( cacheKey ) || {};
|
||||||
|
return cache[ threadItem.id ];
|
||||||
|
}
|
||||||
|
|
||||||
|
function cacheThanked( threadItem ) {
|
||||||
|
const cache = mw.storage.getObject( cacheKey ) || {};
|
||||||
|
cache[ threadItem.id ] = true;
|
||||||
|
mw.storage.setObject( cacheKey, cache );
|
||||||
|
}
|
||||||
|
|
||||||
|
mw.hook( 'discussionToolsOverflowMenuOnChoose' ).add( ( id, menuItem, threadItem ) => {
|
||||||
|
// TODO: Add recipient gender for messages
|
||||||
|
const recipientGender = 'unknown';
|
||||||
|
if ( id === 'thank' ) {
|
||||||
|
thankComment( threadItem ).then( () => {
|
||||||
|
menuItem.setLabel( mw.msg( 'thanks-button-thanked', mw.user, recipientGender ) );
|
||||||
|
menuItem.setDisabled( true );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
|
mw.hook( 'discussionToolsOverflowMenuOnAddItem' ).add( ( id, menuItem, threadItem ) => {
|
||||||
|
// TODO: Add recipient gender for messages
|
||||||
|
const recipientGender = 'unknown';
|
||||||
|
if ( id === 'thank' && isThanked( threadItem ) ) {
|
||||||
|
menuItem.setLabel( mw.msg( 'thanks-button-thanked', mw.user, recipientGender ) );
|
||||||
|
menuItem.setDisabled( true );
|
||||||
|
}
|
||||||
|
} );
|
Loading…
Reference in a new issue