2012-04-27 15:14:24 +00:00
|
|
|
<?php
|
|
|
|
|
2016-05-25 19:03:29 +00:00
|
|
|
use MediaWiki\Auth\AuthManager;
|
2016-02-29 06:13:25 +00:00
|
|
|
use MediaWiki\Logger\LoggerFactory;
|
2017-03-17 11:16:28 +00:00
|
|
|
use MediaWiki\MediaWikiServices;
|
2016-02-29 06:13:25 +00:00
|
|
|
|
2012-04-27 15:14:24 +00:00
|
|
|
class EchoHooks {
|
2016-12-01 00:17:30 +00:00
|
|
|
public static function registerExtension() {
|
|
|
|
global $wgNotificationSender, $wgPasswordSender, $wgAllowHTMLEmail,
|
|
|
|
$wgEchoNotificationCategories, $wgDefaultUserOptions;
|
|
|
|
|
|
|
|
$wgNotificationSender = $wgPasswordSender;
|
|
|
|
|
|
|
|
if ( $wgAllowHTMLEmail ) {
|
|
|
|
$wgDefaultUserOptions['echo-email-format'] = 'html'; /*EchoHooks::EMAIL_FORMAT_HTML*/
|
|
|
|
} else {
|
|
|
|
$wgDefaultUserOptions['echo-email-format'] = 'plain-text'; /*EchoHooks::EMAIL_FORMAT_PLAIN_TEXT*/
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set all of the events to notify by web but not email by default (won't affect events that don't email)
|
|
|
|
foreach ( $wgEchoNotificationCategories as $category => $categoryData ) {
|
|
|
|
$wgDefaultUserOptions["echo-subscriptions-email-{$category}"] = false;
|
|
|
|
$wgDefaultUserOptions["echo-subscriptions-web-{$category}"] = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// most settings default to web on, email off, but override these
|
|
|
|
$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;
|
|
|
|
$wgDefaultUserOptions['echo-subscriptions-web-mention-success'] = false;
|
|
|
|
}
|
|
|
|
|
2013-01-15 23:21:39 +00:00
|
|
|
/**
|
|
|
|
* Initialize Echo extension with necessary data, this function is invoked
|
|
|
|
* from $wgExtensionFunctions
|
|
|
|
*/
|
|
|
|
public static function initEchoExtension() {
|
2014-07-18 03:58:21 +00:00
|
|
|
global $wgEchoNotifications, $wgEchoNotificationCategories, $wgEchoNotificationIcons,
|
2016-12-01 16:50:18 +00:00
|
|
|
$wgEchoEventLoggingSchemas, $wgEchoMentionStatusNotifications;
|
2013-02-16 02:20:34 +00:00
|
|
|
|
|
|
|
// allow extensions to define their own event
|
2016-12-05 18:51:07 +00:00
|
|
|
Hooks::run( 'BeforeCreateEchoEvent', [ &$wgEchoNotifications, &$wgEchoNotificationCategories, &$wgEchoNotificationIcons ] );
|
2013-02-16 02:20:34 +00:00
|
|
|
|
2016-06-21 14:42:56 +00:00
|
|
|
// Only allow mention status notifications when enabled
|
|
|
|
if ( !$wgEchoMentionStatusNotifications ) {
|
|
|
|
unset( $wgEchoNotificationCategories['mention-failure'] );
|
2016-07-21 13:00:54 +00:00
|
|
|
unset( $wgEchoNotificationCategories['mention-success'] );
|
2016-06-21 14:42:56 +00:00
|
|
|
}
|
|
|
|
|
2013-03-06 21:22:17 +00:00
|
|
|
// turn schema off if eventLogging is not enabled
|
2015-01-07 18:26:29 +00:00
|
|
|
if ( !class_exists( 'EventLogging' ) ) {
|
2016-12-01 16:50:18 +00:00
|
|
|
foreach ( $wgEchoEventLoggingSchemas as $schema => $property ) {
|
2013-03-06 21:22:17 +00:00
|
|
|
if ( $property['enabled'] ) {
|
2016-12-01 16:50:18 +00:00
|
|
|
$wgEchoEventLoggingSchemas[$schema]['enabled'] = false;
|
2013-03-06 21:22:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-08-08 20:35:58 +00:00
|
|
|
}
|
2014-02-25 06:09:27 +00:00
|
|
|
|
2014-08-08 20:35:58 +00:00
|
|
|
public static function getNotificationSenderName() {
|
|
|
|
global $wgNotificationSenderName;
|
2014-02-25 06:09:27 +00:00
|
|
|
if ( $wgNotificationSenderName === null ) {
|
|
|
|
$wgNotificationSenderName = wfMessage( 'emailsender' )->inContentLanguage()->text();
|
|
|
|
}
|
|
|
|
|
2014-08-08 20:35:58 +00:00
|
|
|
return $wgNotificationSenderName;
|
2013-01-15 23:21:39 +00:00
|
|
|
}
|
|
|
|
|
2014-08-05 22:18:38 +00:00
|
|
|
/**
|
|
|
|
* ResourceLoaderTestModules hook handler
|
|
|
|
* @see https://www.mediawiki.org/wiki/Manual:Hooks/ResourceLoaderTestModules
|
|
|
|
*
|
|
|
|
* @param array $testModules
|
|
|
|
* @param ResourceLoader $resourceLoader
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
public static function onResourceLoaderTestModules( array &$testModules,
|
|
|
|
ResourceLoader $resourceLoader
|
|
|
|
) {
|
|
|
|
global $wgResourceModules;
|
|
|
|
|
2016-12-05 18:51:07 +00:00
|
|
|
$testModuleBoilerplate = [
|
2014-08-05 22:18:38 +00:00
|
|
|
'localBasePath' => __DIR__,
|
|
|
|
'remoteExtPath' => 'Echo',
|
2016-12-05 18:51:07 +00:00
|
|
|
];
|
2014-08-05 22:18:38 +00:00
|
|
|
|
|
|
|
// find test files for every RL module
|
|
|
|
$prefix = 'ext.echo';
|
|
|
|
foreach ( $wgResourceModules as $key => $module ) {
|
|
|
|
if ( substr( $key, 0, strlen( $prefix ) ) === $prefix && isset( $module['scripts'] ) ) {
|
2016-12-05 18:51:07 +00:00
|
|
|
$testFiles = [];
|
2014-08-05 22:18:38 +00:00
|
|
|
foreach ( $module['scripts'] as $script ) {
|
|
|
|
$testFile = 'tests/qunit/' . dirname( $script ) . '/test_' . basename( $script );
|
|
|
|
// if a test file exists for a given JS file, add it
|
|
|
|
if ( file_exists( $testModuleBoilerplate['localBasePath'] . '/' . $testFile ) ) {
|
|
|
|
$testFiles[] = $testFile;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// if test files exist for given module, create a corresponding test module
|
|
|
|
if ( count( $testFiles ) > 0 ) {
|
2016-12-05 18:51:07 +00:00
|
|
|
$testModules['qunit']["$key.tests"] = $testModuleBoilerplate + [
|
|
|
|
'dependencies' => [ $key ],
|
2014-08-05 22:18:38 +00:00
|
|
|
'scripts' => $testFiles,
|
2016-12-05 18:51:07 +00:00
|
|
|
];
|
2014-08-05 22:18:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-06-01 18:58:37 +00:00
|
|
|
public static function onEventLoggingRegisterSchemas( array &$schemas ) {
|
2016-12-01 16:50:18 +00:00
|
|
|
global $wgEchoEventLoggingSchemas;
|
|
|
|
foreach ( $wgEchoEventLoggingSchemas as $schema => $property ) {
|
2015-07-08 19:29:00 +00:00
|
|
|
if ( $property['enabled'] && $property['client'] ) {
|
2015-06-01 18:58:37 +00:00
|
|
|
$schemas[$schema] = $property['revision'];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-01 00:26:59 +00:00
|
|
|
/**
|
|
|
|
* Handler for ResourceLoaderRegisterModules hook
|
|
|
|
*/
|
|
|
|
public static function onResourceLoaderRegisterModules( ResourceLoader &$resourceLoader ) {
|
2016-12-01 16:50:18 +00:00
|
|
|
global $wgEchoEventLoggingSchemas;
|
2015-06-01 18:58:37 +00:00
|
|
|
|
2015-08-13 00:54:16 +00:00
|
|
|
// ext.echo.logger is used by mobile notifications as well, so be sure not to add any
|
2015-06-01 18:58:37 +00:00
|
|
|
// dependencies that do not target mobile.
|
2016-12-05 18:51:07 +00:00
|
|
|
$definition = [
|
2015-06-01 18:58:37 +00:00
|
|
|
'position' => 'top',
|
2016-12-05 18:51:07 +00:00
|
|
|
'scripts' => [
|
2015-08-13 00:54:16 +00:00
|
|
|
'logger/mw.echo.Logger.js',
|
2016-12-05 18:51:07 +00:00
|
|
|
],
|
|
|
|
'dependencies' => [
|
2015-08-13 00:54:16 +00:00
|
|
|
'oojs'
|
2016-12-05 18:51:07 +00:00
|
|
|
],
|
2015-06-01 18:58:37 +00:00
|
|
|
'localBasePath' => __DIR__ . '/modules',
|
|
|
|
'remoteExtPath' => 'Echo/modules',
|
2016-12-05 18:51:07 +00:00
|
|
|
'targets' => [ 'desktop', 'mobile' ],
|
|
|
|
];
|
2013-03-01 00:26:59 +00:00
|
|
|
|
2015-09-16 19:29:01 +00:00
|
|
|
$hasSchemas = false;
|
2016-12-01 16:50:18 +00:00
|
|
|
foreach ( $wgEchoEventLoggingSchemas as $schema => $property ) {
|
2015-07-08 19:29:00 +00:00
|
|
|
if ( $property['enabled'] && $property['client'] ) {
|
2015-06-01 18:58:37 +00:00
|
|
|
$definition['dependencies'][] = 'schema.' . $schema;
|
2015-09-16 19:29:01 +00:00
|
|
|
$hasSchemas = true;
|
2013-03-01 00:26:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-16 19:29:01 +00:00
|
|
|
if ( $hasSchemas ) {
|
|
|
|
$definition['dependencies'][] = 'ext.eventLogging';
|
|
|
|
}
|
|
|
|
|
2015-08-13 00:54:16 +00:00
|
|
|
$resourceLoader->register( 'ext.echo.logger', $definition );
|
2016-12-14 23:08:34 +00:00
|
|
|
|
2017-03-02 05:01:56 +00:00
|
|
|
global $wgExtensionDirectory, $wgEchoNotificationIcons, $wgEchoSecondaryIcons;
|
2016-12-14 23:08:34 +00:00
|
|
|
$resourceLoader->register( 'ext.echo.emailicons', [
|
|
|
|
'class' => 'ResourceLoaderEchoImageModule',
|
2017-03-02 05:01:56 +00:00
|
|
|
'icons' => $wgEchoNotificationIcons,
|
|
|
|
'selector' => '.mw-echo-icon-{name}',
|
|
|
|
'localBasePath' => $wgExtensionDirectory,
|
|
|
|
'remoteExtPath' => 'Echo/modules'
|
|
|
|
] );
|
|
|
|
$resourceLoader->register( 'ext.echo.secondaryicons', [
|
|
|
|
'class' => 'ResourceLoaderEchoImageModule',
|
|
|
|
'icons' => $wgEchoSecondaryIcons,
|
2016-12-14 23:08:34 +00:00
|
|
|
'selector' => '.mw-echo-icon-{name}',
|
|
|
|
'localBasePath' => $wgExtensionDirectory,
|
|
|
|
'remoteExtPath' => 'Echo/modules'
|
|
|
|
] );
|
2013-03-01 00:26:59 +00:00
|
|
|
}
|
|
|
|
|
2012-05-17 00:29:37 +00:00
|
|
|
/**
|
|
|
|
* @param $updater DatabaseUpdater object
|
|
|
|
*/
|
2015-06-02 00:28:25 +00:00
|
|
|
public static function onLoadExtensionSchemaUpdates( DatabaseUpdater $updater ) {
|
|
|
|
global $wgEchoCluster;
|
|
|
|
if ( $wgEchoCluster !== false ) {
|
|
|
|
// DatabaseUpdater does not support other databases, so skip
|
|
|
|
return;
|
|
|
|
}
|
2012-08-31 21:50:46 +00:00
|
|
|
$dir = __DIR__;
|
2012-04-27 15:14:24 +00:00
|
|
|
$baseSQLFile = "$dir/echo.sql";
|
2013-03-26 00:57:42 +00:00
|
|
|
$updater->addExtensionTable( 'echo_event', $baseSQLFile );
|
2012-11-27 01:53:35 +00:00
|
|
|
$updater->addExtensionTable( 'echo_email_batch', "$dir/db_patches/echo_email_batch.sql" );
|
2014-08-06 00:16:10 +00:00
|
|
|
$updater->addExtensionTable( 'echo_target_page', "$dir/db_patches/echo_target_page.sql" );
|
2012-04-27 15:14:24 +00:00
|
|
|
|
2013-05-20 17:23:51 +00:00
|
|
|
if ( $updater->getDB()->getType() === 'sqlite' ) {
|
|
|
|
$updater->modifyExtensionField( 'echo_event', 'event_agent', "$dir/db_patches/patch-event_agent-split.sqlite.sql" );
|
|
|
|
$updater->modifyExtensionField( 'echo_event', 'event_variant', "$dir/db_patches/patch-event_variant_nullability.sqlite.sql" );
|
2015-03-16 15:47:13 +00:00
|
|
|
$updater->addExtensionField( 'echo_target_page', 'etp_id', "$dir/db_patches/patch-multiple_target_pages.sqlite.sql" );
|
2016-09-07 17:38:12 +00:00
|
|
|
$updater->dropExtensionField( 'echo_target_page', 'etp_user', "$dir/db_patches/patch-drop-echo_target_page-etp_user.sqlite.sql" );
|
2013-05-20 17:23:51 +00:00
|
|
|
// There is no need to run the patch-event_extra-size or patch-event_agent_ip-size because
|
|
|
|
// sqlite ignores numeric arguments in parentheses that follow the type name (ex: VARCHAR(255))
|
|
|
|
// see http://www.sqlite.org/datatype3.html Section 2.2 for more info
|
|
|
|
} else {
|
|
|
|
$updater->modifyExtensionField( 'echo_event', 'event_agent', "$dir/db_patches/patch-event_agent-split.sql" );
|
|
|
|
$updater->modifyExtensionField( 'echo_event', 'event_variant', "$dir/db_patches/patch-event_variant_nullability.sql" );
|
|
|
|
$updater->modifyExtensionField( 'echo_event', 'event_extra', "$dir/db_patches/patch-event_extra-size.sql" );
|
|
|
|
$updater->modifyExtensionField( 'echo_event', 'event_agent_ip', "$dir/db_patches/patch-event_agent_ip-size.sql" );
|
2015-03-16 15:47:13 +00:00
|
|
|
$updater->addExtensionField( 'echo_target_page', 'etp_id', "$dir/db_patches/patch-multiple_target_pages.sql" );
|
2016-09-07 17:38:12 +00:00
|
|
|
$updater->dropExtensionField( 'echo_target_page', 'etp_user', "$dir/db_patches/patch-drop-echo_target_page-etp_user.sql" );
|
2013-05-20 17:23:51 +00:00
|
|
|
}
|
2013-01-15 23:21:39 +00:00
|
|
|
|
|
|
|
$updater->addExtensionField( 'echo_notification', 'notification_bundle_base',
|
|
|
|
"$dir/db_patches/patch-notification-bundling-field.sql" );
|
2013-05-20 17:23:51 +00:00
|
|
|
// This index was renamed twice, first from type_page to event_type and later from event_type to echo_event_type
|
|
|
|
if ( $updater->getDB()->indexExists( 'echo_event', 'type_page', __METHOD__ ) ) {
|
|
|
|
$updater->addExtensionIndex( 'echo_event', 'event_type', "$dir/db_patches/patch-alter-type_page-index.sql" );
|
|
|
|
}
|
2017-02-27 15:13:06 +00:00
|
|
|
$updater->dropExtensionTable( 'echo_subscription', "$dir/db_patches/patch-drop-echo_subscription.sql" );
|
2013-03-26 00:57:42 +00:00
|
|
|
$updater->dropExtensionField( 'echo_event', 'event_timestamp', "$dir/db_patches/patch-drop-echo_event-event_timestamp.sql" );
|
2013-03-06 00:04:48 +00:00
|
|
|
$updater->addExtensionField( 'echo_email_batch', 'eeb_event_hash',
|
|
|
|
"$dir/db_patches/patch-email_batch-new-field.sql" );
|
2013-05-09 18:50:05 +00:00
|
|
|
$updater->addExtensionField( 'echo_event', 'event_page_id', "$dir/db_patches/patch-add-echo_event-event_page_id.sql" );
|
2013-05-20 17:23:51 +00:00
|
|
|
$updater->addExtensionIndex( 'echo_event', 'echo_event_type', "$dir/db_patches/patch-alter-event_type-index.sql" );
|
2013-06-14 20:14:21 +00:00
|
|
|
$updater->addExtensionIndex( 'echo_notification', 'echo_user_timestamp', "$dir/db_patches/patch-alter-user_timestamp-index.sql" );
|
2016-06-13 19:42:27 +00:00
|
|
|
$updater->addExtensionIndex( 'echo_notification', 'echo_notification_event', "$dir/db_patches/patch-add-notification_event-index.sql" );
|
2016-06-06 18:27:23 +00:00
|
|
|
$updater->addPostDatabaseUpdateMaintenance( 'RemoveOrphanedEvents' );
|
2016-03-04 19:23:02 +00:00
|
|
|
$updater->addExtensionField( 'echo_event', 'event_deleted', "$dir/db_patches/patch-add-echo_event-event_deleted.sql" );
|
|
|
|
$updater->addExtensionIndex( 'echo_notification', 'echo_notification_user_read_timestamp', "$dir/db_patches/patch-add-user_read_timestamp-index.sql" );
|
|
|
|
$updater->addExtensionIndex( 'echo_target_page', 'echo_target_page_page_event', "$dir/db_patches/patch-add-page_event-index.sql" );
|
2016-08-26 01:35:39 +00:00
|
|
|
$updater->addExtensionIndex( 'echo_event', 'echo_event_page_id', "$dir/db_patches/patch-add-event_page_id-index.sql" );
|
2016-06-06 17:49:57 +00:00
|
|
|
$updater->dropExtensionIndex( 'echo_notification', 'user_event', "$dir/db_patches/patch-notification-pk.sql" );
|
2013-01-15 23:21:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handler for EchoGetBundleRule hook, which defines the bundle rule for each notification
|
2014-05-27 18:28:37 +00:00
|
|
|
*
|
2013-01-15 23:21:39 +00:00
|
|
|
* @param $event EchoEvent
|
|
|
|
* @param $bundleString string Determines how the notification should be bundled, for example,
|
|
|
|
* talk page notification is bundled based on namespace and title, the bundle string would be
|
|
|
|
* 'edit-user-talk-' + namespace + title, email digest/email bundling would use this hash as
|
|
|
|
* a key to identify bundle-able event. For web bundling, we bundle further based on user's
|
|
|
|
* visit to the overlay, we would generate a display hash based on the hash of $bundleString
|
|
|
|
*
|
2014-05-27 18:28:37 +00:00
|
|
|
* @return bool
|
2013-01-15 23:21:39 +00:00
|
|
|
*/
|
|
|
|
public static function onEchoGetBundleRules( $event, &$bundleString ) {
|
|
|
|
switch ( $event->getType() ) {
|
|
|
|
case 'edit-user-talk':
|
|
|
|
$bundleString = 'edit-user-talk';
|
|
|
|
if ( $event->getTitle() ) {
|
|
|
|
$bundleString .= '-' . $event->getTitle()->getNamespace()
|
2015-10-01 13:48:52 +00:00
|
|
|
. '-' . $event->getTitle()->getDBkey();
|
2013-01-15 23:21:39 +00:00
|
|
|
}
|
2015-10-01 13:48:52 +00:00
|
|
|
break;
|
2013-01-15 23:21:39 +00:00
|
|
|
case 'page-linked':
|
|
|
|
$bundleString = 'page-linked';
|
|
|
|
if ( $event->getTitle() ) {
|
|
|
|
$bundleString .= '-' . $event->getTitle()->getNamespace()
|
2015-10-01 13:48:52 +00:00
|
|
|
. '-' . $event->getTitle()->getDBkey();
|
2013-01-15 23:21:39 +00:00
|
|
|
}
|
2015-10-01 13:48:52 +00:00
|
|
|
break;
|
2016-07-21 13:00:54 +00:00
|
|
|
case 'mention-success':
|
2016-07-29 10:15:34 +00:00
|
|
|
case 'mention-failure':
|
|
|
|
$bundleString = 'mention-status-' . $event->getExtraParam( 'revid' );
|
2016-07-21 13:00:54 +00:00
|
|
|
break;
|
2013-01-15 23:21:39 +00:00
|
|
|
}
|
2015-10-01 13:48:52 +00:00
|
|
|
|
2012-04-27 15:14:24 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-11-30 23:14:57 +00:00
|
|
|
/**
|
|
|
|
* Handler for the GetBetaFeaturePreferences hook.
|
|
|
|
* @see https://www.mediawiki.org/wiki/Manual:Hooks/GetBetaFeaturePreferences
|
|
|
|
*
|
|
|
|
* @param $user User to get preferences for
|
|
|
|
* @param &$preferences Preferences array
|
|
|
|
*
|
|
|
|
* @return bool true in all cases
|
|
|
|
*/
|
|
|
|
public static function getBetaFeaturePreferences( User $user, array &$preferences ) {
|
2016-05-13 20:48:03 +00:00
|
|
|
global $wgExtensionAssetsPath, $wgEchoUseCrossWikiBetaFeature, $wgEchoCrossWikiNotifications;
|
2015-11-30 23:14:57 +00:00
|
|
|
|
2016-05-13 20:48:03 +00:00
|
|
|
if ( $wgEchoUseCrossWikiBetaFeature && $wgEchoCrossWikiNotifications ) {
|
2016-12-05 18:51:07 +00:00
|
|
|
$preferences['echo-cross-wiki-notifications'] = [
|
2015-11-30 23:14:57 +00:00
|
|
|
'label-message' => 'echo-pref-beta-feature-cross-wiki-message',
|
|
|
|
'desc-message' => 'echo-pref-beta-feature-cross-wiki-description',
|
|
|
|
// Paths to images that represents the feature.
|
2016-12-05 18:51:07 +00:00
|
|
|
'screenshot' => [
|
2015-11-30 23:14:57 +00:00
|
|
|
'rtl' => "$wgExtensionAssetsPath/Echo/images/betafeatures-icon-notifications-rtl.svg",
|
|
|
|
'ltr' => "$wgExtensionAssetsPath/Echo/images/betafeatures-icon-notifications-ltr.svg",
|
2016-12-05 18:51:07 +00:00
|
|
|
],
|
2016-01-15 19:00:12 +00:00
|
|
|
'info-link' => 'https://www.mediawiki.org/wiki/Special:Mylanguage/Help:Notifications/Cross-wiki',
|
2015-11-30 23:14:57 +00:00
|
|
|
// Link to discussion about the feature - talk pages might work
|
2016-01-15 19:00:12 +00:00
|
|
|
'discussion-link' => 'https://www.mediawiki.org/wiki/Help_talk:Notifications',
|
2016-12-05 18:51:07 +00:00
|
|
|
];
|
2015-11-30 23:14:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-05-17 00:29:37 +00:00
|
|
|
/**
|
|
|
|
* Handler for GetPreferences hook.
|
|
|
|
* @see http://www.mediawiki.org/wiki/Manual:Hooks/GetPreferences
|
2014-05-27 18:28:37 +00:00
|
|
|
*
|
2012-05-17 00:29:37 +00:00
|
|
|
* @param $user User to get preferences for
|
|
|
|
* @param &$preferences Preferences array
|
2014-05-27 18:28:37 +00:00
|
|
|
*
|
|
|
|
* @throws MWException
|
2012-09-02 09:30:38 +00:00
|
|
|
* @return bool true in all cases
|
2012-05-17 00:29:37 +00:00
|
|
|
*/
|
2012-04-27 15:14:24 +00:00
|
|
|
public static function getPreferences( $user, &$preferences ) {
|
2016-05-25 19:03:29 +00:00
|
|
|
global $wgEchoEnableEmailBatch,
|
2013-05-01 20:48:12 +00:00
|
|
|
$wgEchoNotifiers, $wgEchoNotificationCategories, $wgEchoNotifications,
|
2016-03-18 00:00:05 +00:00
|
|
|
$wgEchoNewMsgAlert, $wgAllowHTMLEmail, $wgEchoUseCrossWikiBetaFeature,
|
2016-11-10 04:37:10 +00:00
|
|
|
$wgEchoShowFooterNotice, $wgEchoCrossWikiNotifications, $wgEchoPerUserBlacklist;
|
2012-11-16 21:03:57 +00:00
|
|
|
|
BREAKING CHANGE: Change $wgEchoDefaultNotificationTypes to be logical
Merge and deploy at the *same time* as:
* BounceHandler - I3c669945080d8e1f67880bd8a31af7f88a70904d
* mediawiki-config - I13817c139967ed9e230cfb0c87c5de66da793c96
Despite claiming to be about categories, $wgEchoDefaultNotificationTypes
was actually configuring both categories and types (which go inside
categories).
For example, 'thank-you-edit' is a type, but 'emailuser' is both
a category and a type (when used as a category, this has special
effects at Special:Preferences).
Since types and categories can and sometimes do have the same names,
this leaves no way to properly and clearly configure them. It also
makes it difficult to document what is going on (as required by
T132127).
Split into three variables:
$wgDefaultNotifyTypeAvailability - Applies unless overriden
$wgNotifyTypeAvailabilityByCategory - By category; this can be and is
displayed at Special:Preferences
$wgNotifyTypeAvailabilityByNotificationType - By type; this cannot
be displayed at Special:Preferences. To avoid confusing the user,
we introduce a restriction (which was previously followed in practice,
AFAICT) that types can only be overridden if the category is not
displayed in preferences.
Otherwise, it can look to the user like a category is on/off, but the
types within might have the opposite state.
Due to this configuration change, this is a breaking change, and needs
coordinated deployments.
This also lays the groundwork for T132127
Also change terminology to consistently use "notify type" for web/email.
It was mixing between that and output format (which unfortunately
sounds like the API format, e.g. 'model').
Bug: T132820
Bug: T132127
Change-Id: I09f39f5fc5f13f3253af9f7819bca81f1601da93
2016-04-19 02:54:15 +00:00
|
|
|
$attributeManager = EchoAttributeManager::newFromGlobalVars();
|
|
|
|
|
2012-11-16 21:03:57 +00:00
|
|
|
// Show email frequency options
|
|
|
|
$never = wfMessage( 'echo-pref-email-frequency-never' )->plain();
|
|
|
|
$immediately = wfMessage( 'echo-pref-email-frequency-immediately' )->plain();
|
2016-12-05 18:51:07 +00:00
|
|
|
$freqOptions = [
|
2016-05-05 13:05:03 +00:00
|
|
|
$never => EchoEmailFrequency::NEVER,
|
|
|
|
$immediately => EchoEmailFrequency::IMMEDIATELY,
|
2016-12-05 18:51:07 +00:00
|
|
|
];
|
2012-12-14 20:48:41 +00:00
|
|
|
// Only show digest options if email batch is enabled
|
|
|
|
if ( $wgEchoEnableEmailBatch ) {
|
|
|
|
$daily = wfMessage( 'echo-pref-email-frequency-daily' )->plain();
|
|
|
|
$weekly = wfMessage( 'echo-pref-email-frequency-weekly' )->plain();
|
2016-12-05 18:51:07 +00:00
|
|
|
$freqOptions += [
|
2016-05-05 13:05:03 +00:00
|
|
|
$daily => EchoEmailFrequency::DAILY_DIGEST,
|
|
|
|
$weekly => EchoEmailFrequency::WEEKLY_DIGEST,
|
2016-12-05 18:51:07 +00:00
|
|
|
];
|
2012-12-14 20:48:41 +00:00
|
|
|
}
|
2016-12-05 18:51:07 +00:00
|
|
|
$preferences['echo-email-frequency'] = [
|
2012-11-16 21:03:57 +00:00
|
|
|
'type' => 'select',
|
2013-04-18 00:44:20 +00:00
|
|
|
'label-message' => 'echo-pref-send-me',
|
|
|
|
'section' => 'echo/emailsettings',
|
2012-12-14 20:48:41 +00:00
|
|
|
'options' => $freqOptions
|
2016-12-05 18:51:07 +00:00
|
|
|
];
|
2012-11-16 21:03:57 +00:00
|
|
|
|
|
|
|
// Display information about the user's currently set email address
|
|
|
|
$prefsTitle = SpecialPage::getTitleFor( 'Preferences', false, 'mw-prefsection-echo' );
|
|
|
|
$link = Linker::link(
|
|
|
|
SpecialPage::getTitleFor( 'ChangeEmail' ),
|
|
|
|
wfMessage( $user->getEmail() ? 'prefs-changeemail' : 'prefs-setemail' )->escaped(),
|
2016-12-05 18:51:07 +00:00
|
|
|
[],
|
|
|
|
[ 'returnto' => $prefsTitle->getFullText() ]
|
2012-11-16 21:03:57 +00:00
|
|
|
);
|
2016-05-12 17:18:58 +00:00
|
|
|
$emailAddress = $user->getEmail() && $user->isAllowed( 'viewmyprivateinfo' )
|
|
|
|
? htmlspecialchars( $user->getEmail() ) : '';
|
2016-05-25 19:03:29 +00:00
|
|
|
if ( $user->isAllowed( 'editmyprivateinfo' ) && self::isEmailChangeAllowed() ) {
|
2012-11-16 21:03:57 +00:00
|
|
|
if ( $emailAddress === '' ) {
|
|
|
|
$emailAddress .= $link;
|
|
|
|
} else {
|
|
|
|
$emailAddress .= wfMessage( 'word-separator' )->escaped()
|
|
|
|
. wfMessage( 'parentheses' )->rawParams( $link )->escaped();
|
|
|
|
}
|
|
|
|
}
|
2016-12-05 18:51:07 +00:00
|
|
|
$preferences['echo-emailaddress'] = [
|
2012-11-16 21:03:57 +00:00
|
|
|
'type' => 'info',
|
|
|
|
'raw' => true,
|
2013-04-18 00:44:20 +00:00
|
|
|
'default' => $emailAddress,
|
|
|
|
'label-message' => 'echo-pref-send-to',
|
|
|
|
'section' => 'echo/emailsettings'
|
2016-12-05 18:51:07 +00:00
|
|
|
];
|
2012-11-16 21:03:57 +00:00
|
|
|
|
2013-06-24 00:22:08 +00:00
|
|
|
// Only show this option if html email is allowed, otherwise it is always plain text format
|
|
|
|
if ( $wgAllowHTMLEmail ) {
|
|
|
|
// Email format
|
2016-12-05 18:51:07 +00:00
|
|
|
$preferences['echo-email-format'] = [
|
2013-06-24 00:22:08 +00:00
|
|
|
'type' => 'select',
|
|
|
|
'label-message' => 'echo-pref-email-format',
|
|
|
|
'section' => 'echo/emailsettings',
|
2016-12-05 18:51:07 +00:00
|
|
|
'options' => [
|
2016-05-05 13:05:03 +00:00
|
|
|
wfMessage( 'echo-pref-email-format-html' )->plain() => EchoEmailFormat::HTML,
|
|
|
|
wfMessage( 'echo-pref-email-format-plain-text' )->plain() => EchoEmailFormat::PLAIN_TEXT,
|
2016-12-05 18:51:07 +00:00
|
|
|
]
|
|
|
|
];
|
2013-06-24 00:22:08 +00:00
|
|
|
}
|
|
|
|
|
2013-02-16 02:20:34 +00:00
|
|
|
// Sort notification categories by priority
|
2016-12-05 18:51:07 +00:00
|
|
|
$categoriesAndPriorities = [];
|
BREAKING CHANGE: Change $wgEchoDefaultNotificationTypes to be logical
Merge and deploy at the *same time* as:
* BounceHandler - I3c669945080d8e1f67880bd8a31af7f88a70904d
* mediawiki-config - I13817c139967ed9e230cfb0c87c5de66da793c96
Despite claiming to be about categories, $wgEchoDefaultNotificationTypes
was actually configuring both categories and types (which go inside
categories).
For example, 'thank-you-edit' is a type, but 'emailuser' is both
a category and a type (when used as a category, this has special
effects at Special:Preferences).
Since types and categories can and sometimes do have the same names,
this leaves no way to properly and clearly configure them. It also
makes it difficult to document what is going on (as required by
T132127).
Split into three variables:
$wgDefaultNotifyTypeAvailability - Applies unless overriden
$wgNotifyTypeAvailabilityByCategory - By category; this can be and is
displayed at Special:Preferences
$wgNotifyTypeAvailabilityByNotificationType - By type; this cannot
be displayed at Special:Preferences. To avoid confusing the user,
we introduce a restriction (which was previously followed in practice,
AFAICT) that types can only be overridden if the category is not
displayed in preferences.
Otherwise, it can look to the user like a category is on/off, but the
types within might have the opposite state.
Due to this configuration change, this is a breaking change, and needs
coordinated deployments.
This also lays the groundwork for T132127
Also change terminology to consistently use "notify type" for web/email.
It was mixing between that and output format (which unfortunately
sounds like the API format, e.g. 'model').
Bug: T132820
Bug: T132127
Change-Id: I09f39f5fc5f13f3253af9f7819bca81f1601da93
2016-04-19 02:54:15 +00:00
|
|
|
foreach ( $attributeManager->getInternalCategoryNames() as $category ) {
|
|
|
|
// See if the category should be hidden from preferences.
|
|
|
|
if ( !$attributeManager->isCategoryDisplayedInPreferences( $category ) ) {
|
2013-02-16 02:20:34 +00:00
|
|
|
continue;
|
|
|
|
}
|
BREAKING CHANGE: Change $wgEchoDefaultNotificationTypes to be logical
Merge and deploy at the *same time* as:
* BounceHandler - I3c669945080d8e1f67880bd8a31af7f88a70904d
* mediawiki-config - I13817c139967ed9e230cfb0c87c5de66da793c96
Despite claiming to be about categories, $wgEchoDefaultNotificationTypes
was actually configuring both categories and types (which go inside
categories).
For example, 'thank-you-edit' is a type, but 'emailuser' is both
a category and a type (when used as a category, this has special
effects at Special:Preferences).
Since types and categories can and sometimes do have the same names,
this leaves no way to properly and clearly configure them. It also
makes it difficult to document what is going on (as required by
T132127).
Split into three variables:
$wgDefaultNotifyTypeAvailability - Applies unless overriden
$wgNotifyTypeAvailabilityByCategory - By category; this can be and is
displayed at Special:Preferences
$wgNotifyTypeAvailabilityByNotificationType - By type; this cannot
be displayed at Special:Preferences. To avoid confusing the user,
we introduce a restriction (which was previously followed in practice,
AFAICT) that types can only be overridden if the category is not
displayed in preferences.
Otherwise, it can look to the user like a category is on/off, but the
types within might have the opposite state.
Due to this configuration change, this is a breaking change, and needs
coordinated deployments.
This also lays the groundwork for T132127
Also change terminology to consistently use "notify type" for web/email.
It was mixing between that and output format (which unfortunately
sounds like the API format, e.g. 'model').
Bug: T132820
Bug: T132127
Change-Id: I09f39f5fc5f13f3253af9f7819bca81f1601da93
2016-04-19 02:54:15 +00:00
|
|
|
|
2016-12-28 11:29:44 +00:00
|
|
|
// See if user is eligible to receive this notification (per user group restrictions)
|
2014-07-22 21:33:22 +00:00
|
|
|
if ( $attributeManager->getCategoryEligibility( $user, $category ) ) {
|
|
|
|
$categoriesAndPriorities[$category] = $attributeManager->getCategoryPriority( $category );
|
2013-01-14 23:52:46 +00:00
|
|
|
}
|
|
|
|
}
|
2013-02-16 02:20:34 +00:00
|
|
|
asort( $categoriesAndPriorities );
|
|
|
|
$validSortedCategories = array_keys( $categoriesAndPriorities );
|
2013-01-14 23:52:46 +00:00
|
|
|
|
2013-05-09 21:23:15 +00:00
|
|
|
// Show subscription options. IMPORTANT: 'echo-subscriptions-email-edit-user-talk' is a
|
|
|
|
// virtual option, its value is saved to existing talk page notification option
|
|
|
|
// 'enotifusertalkpages', see onUserLoadOptions() and onUserSaveOptions() for more
|
|
|
|
// information on how it is handled. Doing it in this way, we can avoid keeping running
|
|
|
|
// massive data migration script to keep these two options synced when echo is enabled on
|
|
|
|
// new wikis or Echo is disabled and re-enabled for some reason. We can update the name
|
|
|
|
// if Echo is ever merged to core
|
2013-02-16 02:20:34 +00:00
|
|
|
|
BREAKING CHANGE: Change $wgEchoDefaultNotificationTypes to be logical
Merge and deploy at the *same time* as:
* BounceHandler - I3c669945080d8e1f67880bd8a31af7f88a70904d
* mediawiki-config - I13817c139967ed9e230cfb0c87c5de66da793c96
Despite claiming to be about categories, $wgEchoDefaultNotificationTypes
was actually configuring both categories and types (which go inside
categories).
For example, 'thank-you-edit' is a type, but 'emailuser' is both
a category and a type (when used as a category, this has special
effects at Special:Preferences).
Since types and categories can and sometimes do have the same names,
this leaves no way to properly and clearly configure them. It also
makes it difficult to document what is going on (as required by
T132127).
Split into three variables:
$wgDefaultNotifyTypeAvailability - Applies unless overriden
$wgNotifyTypeAvailabilityByCategory - By category; this can be and is
displayed at Special:Preferences
$wgNotifyTypeAvailabilityByNotificationType - By type; this cannot
be displayed at Special:Preferences. To avoid confusing the user,
we introduce a restriction (which was previously followed in practice,
AFAICT) that types can only be overridden if the category is not
displayed in preferences.
Otherwise, it can look to the user like a category is on/off, but the
types within might have the opposite state.
Due to this configuration change, this is a breaking change, and needs
coordinated deployments.
This also lays the groundwork for T132127
Also change terminology to consistently use "notify type" for web/email.
It was mixing between that and output format (which unfortunately
sounds like the API format, e.g. 'model').
Bug: T132820
Bug: T132127
Change-Id: I09f39f5fc5f13f3253af9f7819bca81f1601da93
2016-04-19 02:54:15 +00:00
|
|
|
// Build the columns (notify types)
|
2016-12-05 18:51:07 +00:00
|
|
|
$columns = [];
|
2013-02-16 02:20:34 +00:00
|
|
|
foreach ( $wgEchoNotifiers as $notifierType => $notifierData ) {
|
2013-04-16 22:40:45 +00:00
|
|
|
$formatMessage = wfMessage( 'echo-pref-' . $notifierType )->escaped();
|
2013-02-16 02:20:34 +00:00
|
|
|
$columns[$formatMessage] = $notifierType;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Build the rows (notification categories)
|
2016-12-05 18:51:07 +00:00
|
|
|
$rows = [];
|
|
|
|
$tooltips = [];
|
2013-02-16 02:20:34 +00:00
|
|
|
foreach ( $validSortedCategories as $category ) {
|
2013-04-16 22:40:45 +00:00
|
|
|
$categoryMessage = wfMessage( 'echo-category-title-' . $category )->numParams( 1 )->escaped();
|
2013-02-16 02:20:34 +00:00
|
|
|
$rows[$categoryMessage] = $category;
|
2013-05-02 00:12:59 +00:00
|
|
|
if ( isset( $wgEchoNotificationCategories[$category]['tooltip'] ) ) {
|
2016-04-02 13:41:56 +00:00
|
|
|
$tooltips[$categoryMessage] = wfMessage( $wgEchoNotificationCategories[$category]['tooltip'] )->text();
|
2013-05-02 00:12:59 +00:00
|
|
|
}
|
2013-02-16 02:20:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Figure out the individual exceptions in the matrix and make them disabled
|
2016-12-05 18:51:07 +00:00
|
|
|
$forceOptionsOff = $forceOptionsOn = [];
|
2013-02-16 02:20:34 +00:00
|
|
|
foreach ( $wgEchoNotifiers as $notifierType => $notifierData ) {
|
|
|
|
foreach ( $validSortedCategories as $category ) {
|
BREAKING CHANGE: Change $wgEchoDefaultNotificationTypes to be logical
Merge and deploy at the *same time* as:
* BounceHandler - I3c669945080d8e1f67880bd8a31af7f88a70904d
* mediawiki-config - I13817c139967ed9e230cfb0c87c5de66da793c96
Despite claiming to be about categories, $wgEchoDefaultNotificationTypes
was actually configuring both categories and types (which go inside
categories).
For example, 'thank-you-edit' is a type, but 'emailuser' is both
a category and a type (when used as a category, this has special
effects at Special:Preferences).
Since types and categories can and sometimes do have the same names,
this leaves no way to properly and clearly configure them. It also
makes it difficult to document what is going on (as required by
T132127).
Split into three variables:
$wgDefaultNotifyTypeAvailability - Applies unless overriden
$wgNotifyTypeAvailabilityByCategory - By category; this can be and is
displayed at Special:Preferences
$wgNotifyTypeAvailabilityByNotificationType - By type; this cannot
be displayed at Special:Preferences. To avoid confusing the user,
we introduce a restriction (which was previously followed in practice,
AFAICT) that types can only be overridden if the category is not
displayed in preferences.
Otherwise, it can look to the user like a category is on/off, but the
types within might have the opposite state.
Due to this configuration change, this is a breaking change, and needs
coordinated deployments.
This also lays the groundwork for T132127
Also change terminology to consistently use "notify type" for web/email.
It was mixing between that and output format (which unfortunately
sounds like the API format, e.g. 'model').
Bug: T132820
Bug: T132127
Change-Id: I09f39f5fc5f13f3253af9f7819bca81f1601da93
2016-04-19 02:54:15 +00:00
|
|
|
// See if this notify type is non-dismissable
|
|
|
|
if ( !$attributeManager->isNotifyTypeDismissableForCategory( $category, $notifierType ) ) {
|
2013-04-29 17:58:05 +00:00
|
|
|
$forceOptionsOn[] = "$notifierType-$category";
|
2013-01-14 23:52:46 +00:00
|
|
|
}
|
2013-02-16 02:20:34 +00:00
|
|
|
|
BREAKING CHANGE: Change $wgEchoDefaultNotificationTypes to be logical
Merge and deploy at the *same time* as:
* BounceHandler - I3c669945080d8e1f67880bd8a31af7f88a70904d
* mediawiki-config - I13817c139967ed9e230cfb0c87c5de66da793c96
Despite claiming to be about categories, $wgEchoDefaultNotificationTypes
was actually configuring both categories and types (which go inside
categories).
For example, 'thank-you-edit' is a type, but 'emailuser' is both
a category and a type (when used as a category, this has special
effects at Special:Preferences).
Since types and categories can and sometimes do have the same names,
this leaves no way to properly and clearly configure them. It also
makes it difficult to document what is going on (as required by
T132127).
Split into three variables:
$wgDefaultNotifyTypeAvailability - Applies unless overriden
$wgNotifyTypeAvailabilityByCategory - By category; this can be and is
displayed at Special:Preferences
$wgNotifyTypeAvailabilityByNotificationType - By type; this cannot
be displayed at Special:Preferences. To avoid confusing the user,
we introduce a restriction (which was previously followed in practice,
AFAICT) that types can only be overridden if the category is not
displayed in preferences.
Otherwise, it can look to the user like a category is on/off, but the
types within might have the opposite state.
Due to this configuration change, this is a breaking change, and needs
coordinated deployments.
This also lays the groundwork for T132127
Also change terminology to consistently use "notify type" for web/email.
It was mixing between that and output format (which unfortunately
sounds like the API format, e.g. 'model').
Bug: T132820
Bug: T132127
Change-Id: I09f39f5fc5f13f3253af9f7819bca81f1601da93
2016-04-19 02:54:15 +00:00
|
|
|
if ( !$attributeManager->isNotifyTypeAvailableForCategory( $category, $notifierType ) ) {
|
2013-04-29 17:58:05 +00:00
|
|
|
$forceOptionsOff[] = "$notifierType-$category";
|
2013-01-14 23:52:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-29 17:58:05 +00:00
|
|
|
$invalid = array_intersect( $forceOptionsOff, $forceOptionsOn );
|
|
|
|
if ( $invalid ) {
|
|
|
|
throw new MWException( sprintf(
|
|
|
|
'The following notifications are both forced and removed: %s',
|
|
|
|
implode( ', ', $invalid )
|
|
|
|
) );
|
|
|
|
}
|
2016-12-05 18:51:07 +00:00
|
|
|
$preferences['echo-subscriptions'] = [
|
2013-02-16 02:20:34 +00:00
|
|
|
'class' => 'HTMLCheckMatrix',
|
|
|
|
'section' => 'echo/echosubscriptions',
|
|
|
|
'rows' => $rows,
|
|
|
|
'columns' => $columns,
|
2013-05-22 19:50:24 +00:00
|
|
|
'prefix' => 'echo-subscriptions-',
|
2013-04-29 17:58:05 +00:00
|
|
|
'force-options-off' => $forceOptionsOff,
|
|
|
|
'force-options-on' => $forceOptionsOn,
|
2013-05-02 00:12:59 +00:00
|
|
|
'tooltips' => $tooltips,
|
2016-12-05 18:51:07 +00:00
|
|
|
];
|
2013-03-20 01:10:23 +00:00
|
|
|
|
2016-05-13 20:48:03 +00:00
|
|
|
if ( !$wgEchoUseCrossWikiBetaFeature && $wgEchoCrossWikiNotifications ) {
|
2016-12-05 18:51:07 +00:00
|
|
|
$preferences['echo-cross-wiki-notifications'] = [
|
2016-03-11 01:48:06 +00:00
|
|
|
'type' => 'toggle',
|
|
|
|
'label-message' => 'echo-pref-cross-wiki-notifications',
|
|
|
|
'section' => 'echo/echocrosswiki'
|
2016-12-05 18:51:07 +00:00
|
|
|
];
|
2016-03-11 01:48:06 +00:00
|
|
|
}
|
|
|
|
|
2013-05-14 07:05:09 +00:00
|
|
|
if ( $wgEchoNewMsgAlert ) {
|
2016-12-05 18:51:07 +00:00
|
|
|
$preferences['echo-show-alert'] = [
|
2013-05-14 07:05:09 +00:00
|
|
|
'type' => 'toggle',
|
|
|
|
'label-message' => 'echo-pref-new-message-indicator',
|
|
|
|
'section' => 'echo/newmessageindicator',
|
2016-12-05 18:51:07 +00:00
|
|
|
];
|
2013-05-14 07:05:09 +00:00
|
|
|
}
|
|
|
|
|
2013-04-28 21:55:05 +00:00
|
|
|
// If we're using Echo to handle user talk page post notifications,
|
|
|
|
// hide the old (non-Echo) preference for this. If Echo is moved to core
|
|
|
|
// we'll want to remove this old user option entirely. For now, though,
|
|
|
|
// we need to keep it defined in case Echo is ever uninstalled.
|
|
|
|
// Otherwise, that preference could be lost entirely. This hiding logic
|
|
|
|
// is not abstracted since there is only a single preference in core
|
|
|
|
// that is potentially made obsolete by Echo.
|
|
|
|
if ( isset( $wgEchoNotifications['edit-user-talk'] ) ) {
|
|
|
|
$preferences['enotifusertalkpages']['type'] = 'hidden';
|
|
|
|
unset( $preferences['enotifusertalkpages']['section'] );
|
|
|
|
}
|
|
|
|
|
2016-03-18 00:00:05 +00:00
|
|
|
if ( $wgEchoShowFooterNotice ) {
|
2016-12-05 18:51:07 +00:00
|
|
|
$preferences['echo-dismiss-special-page-invitation'] = [
|
2016-03-18 00:00:05 +00:00
|
|
|
'type' => 'api',
|
2016-12-05 18:51:07 +00:00
|
|
|
];
|
2016-03-18 00:00:05 +00:00
|
|
|
}
|
|
|
|
|
2016-11-10 04:37:10 +00:00
|
|
|
if ( $wgEchoPerUserBlacklist ) {
|
|
|
|
$preferences['echo-notifications-blacklist'] = [
|
2017-05-20 13:51:30 +00:00
|
|
|
'type' => 'usersmultiselect',
|
2016-11-10 04:37:10 +00:00
|
|
|
'label-message' => 'echo-pref-notifications-blacklist',
|
|
|
|
'section' => 'echo/blocknotificationslist',
|
|
|
|
];
|
|
|
|
}
|
|
|
|
|
2012-04-27 15:14:24 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-05-25 19:03:29 +00:00
|
|
|
/**
|
|
|
|
* Test whether email address change is supposed to be allowed
|
|
|
|
* @return boolean
|
|
|
|
*/
|
|
|
|
private static function isEmailChangeAllowed() {
|
|
|
|
global $wgAuth, $wgDisableAuthManager;
|
|
|
|
|
|
|
|
if ( class_exists( AuthManager::class ) && !$wgDisableAuthManager ) {
|
|
|
|
return AuthManager::singleton()->allowsPropertyChange( 'emailaddress' );
|
|
|
|
} else {
|
|
|
|
return $wgAuth->allowPropChange( 'emailaddress' );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-17 00:29:37 +00:00
|
|
|
/**
|
2016-10-06 16:22:28 +00:00
|
|
|
* Handler for PageContentSaveComplete hook
|
2016-10-09 17:48:45 +00:00
|
|
|
* @see http://www.mediawiki.org/wiki/Manual:Hooks/PageContentSaveComplete
|
2012-05-17 00:29:37 +00:00
|
|
|
* @param $article Article edited
|
|
|
|
* @param $user User who edited
|
2016-10-06 16:22:28 +00:00
|
|
|
* @param $content Content New article text
|
2012-09-02 09:30:38 +00:00
|
|
|
* @param $summary string Edit summary
|
|
|
|
* @param $minoredit bool Minor edit or not
|
|
|
|
* @param $watchthis bool Watch this article?
|
2012-09-26 05:09:43 +00:00
|
|
|
* @param $sectionanchor string Section that was edited
|
|
|
|
* @param $flags int Edit flags
|
2012-05-17 00:29:37 +00:00
|
|
|
* @param $revision Revision that was created
|
|
|
|
* @param $status Status
|
2016-12-18 02:21:53 +00:00
|
|
|
* @param $baseRevId Int
|
|
|
|
* @param $undidRevId Int
|
2012-09-02 09:30:38 +00:00
|
|
|
* @return bool true in all cases
|
2012-05-17 00:29:37 +00:00
|
|
|
*/
|
2016-12-18 02:21:53 +00:00
|
|
|
public static function onPageContentSaveComplete( &$article, &$user, $content, $summary, $minoredit,
|
|
|
|
$watchthis, $sectionanchor, &$flags, $revision, &$status, $baseRevId, $undidRevId = 0 ) {
|
|
|
|
global $wgEchoNotifications;
|
2013-11-04 17:58:47 +00:00
|
|
|
|
2015-05-18 19:58:15 +00:00
|
|
|
if ( !$revision ) {
|
|
|
|
return true;
|
|
|
|
}
|
2016-02-24 08:27:17 +00:00
|
|
|
|
|
|
|
// unless status is "good" (not only ok, also no warnings or errors), we
|
|
|
|
// probably shouldn't process it at all (e.g. null edits)
|
|
|
|
if ( !$status->isGood() ) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-01-18 23:55:47 +00:00
|
|
|
$title = $article->getTitle();
|
2012-07-27 22:16:19 +00:00
|
|
|
|
2015-05-18 19:58:15 +00:00
|
|
|
// Try to do this after the HTTP response
|
2015-10-01 13:48:52 +00:00
|
|
|
DeferredUpdates::addCallableUpdate( function () use ( $revision ) {
|
2015-05-18 19:58:15 +00:00
|
|
|
EchoDiscussionParser::generateEventsForRevision( $revision );
|
|
|
|
} );
|
|
|
|
|
2016-01-18 23:55:47 +00:00
|
|
|
// If the user is not an IP and this is not a null edit,
|
|
|
|
// test for them reaching a congratulatory threshold
|
2016-02-26 23:51:25 +00:00
|
|
|
$thresholds = [ 1, 10, 100, 1000, 10000, 100000, 1000000 ];
|
2016-01-18 23:55:47 +00:00
|
|
|
if ( $user->isLoggedIn() && $status->value['revision'] ) {
|
2016-08-03 15:41:50 +00:00
|
|
|
$thresholdCount = $user->getEditCount();
|
2016-03-09 01:05:05 +00:00
|
|
|
if ( in_array( $thresholdCount, $thresholds ) ) {
|
2016-08-03 15:41:50 +00:00
|
|
|
DeferredUpdates::addCallableUpdate( function () use ( $user, $title, $thresholdCount ) {
|
|
|
|
|
|
|
|
$notificationMapper = new EchoNotificationMapper();
|
2016-12-05 18:51:07 +00:00
|
|
|
$notifications = $notificationMapper->fetchByUser( $user, 10, null, [ 'thank-you-edit' ] );
|
2016-08-03 15:41:50 +00:00
|
|
|
/** @var EchoNotification $notification */
|
|
|
|
foreach ( $notifications as $notification ) {
|
|
|
|
if ( $notification->getEvent()->getExtraParam( 'editCount' ) === $thresholdCount ) {
|
|
|
|
LoggerFactory::getInstance( 'Echo' )->debug(
|
|
|
|
'{user} (id: {id}) has already been thanked for their {count} edit',
|
2016-12-05 18:51:07 +00:00
|
|
|
[
|
2016-08-03 15:41:50 +00:00
|
|
|
'user' => $user->getName(),
|
|
|
|
'id' => $user->getId(),
|
|
|
|
'count' => $thresholdCount,
|
2016-12-05 18:51:07 +00:00
|
|
|
]
|
2016-08-03 15:41:50 +00:00
|
|
|
);
|
|
|
|
return;
|
|
|
|
}
|
2016-03-04 05:48:01 +00:00
|
|
|
}
|
2016-08-03 15:41:50 +00:00
|
|
|
|
2016-12-05 18:51:07 +00:00
|
|
|
EchoEvent::create( [
|
2016-01-18 23:55:47 +00:00
|
|
|
'type' => 'thank-you-edit',
|
|
|
|
'title' => $title,
|
|
|
|
'agent' => $user,
|
|
|
|
// Edit threshold notifications are sent to the agent
|
2016-12-05 18:51:07 +00:00
|
|
|
'extra' => [
|
2016-01-18 23:55:47 +00:00
|
|
|
'notifyAgent' => true,
|
2016-03-11 16:58:21 +00:00
|
|
|
'editCount' => $thresholdCount,
|
2016-12-05 18:51:07 +00:00
|
|
|
]
|
|
|
|
]
|
2016-01-18 23:55:47 +00:00
|
|
|
);
|
|
|
|
} );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-18 19:58:15 +00:00
|
|
|
// Handle the case of someone undoing an edit, either through the
|
|
|
|
// 'undo' link in the article history or via the API.
|
2016-12-18 02:21:53 +00:00
|
|
|
if ( isset( $wgEchoNotifications['reverted'] ) && $undidRevId ) {
|
|
|
|
$undidRevision = Revision::newFromId( $undidRevId );
|
|
|
|
if ( $undidRevision && $undidRevision->getTitle()->equals( $title ) ) {
|
|
|
|
$victimId = $undidRevision->getUser();
|
|
|
|
if ( $victimId ) { // No notifications for anonymous users
|
|
|
|
EchoEvent::create( [
|
|
|
|
'type' => 'reverted',
|
|
|
|
'title' => $title,
|
|
|
|
'extra' => [
|
|
|
|
'revid' => $revision->getId(),
|
|
|
|
'reverted-user-id' => $victimId,
|
|
|
|
'reverted-revision-id' => $undidRevId,
|
|
|
|
'method' => 'undo',
|
|
|
|
'summary' => $summary,
|
|
|
|
],
|
|
|
|
'agent' => $user,
|
|
|
|
] );
|
2012-07-18 19:39:33 +00:00
|
|
|
}
|
|
|
|
}
|
2012-11-01 20:15:37 +00:00
|
|
|
}
|
2015-05-18 19:58:15 +00:00
|
|
|
|
2012-04-27 15:14:24 +00:00
|
|
|
return true;
|
|
|
|
}
|
2012-06-01 10:57:09 +00:00
|
|
|
|
2013-05-01 19:27:32 +00:00
|
|
|
/**
|
|
|
|
* Handler for EchoAbortEmailNotification hook
|
|
|
|
* @param $user User
|
|
|
|
* @param $event EchoEvent
|
|
|
|
* @return bool true - send email, false - do not send email
|
|
|
|
*/
|
|
|
|
public static function onEchoAbortEmailNotification( $user, $event ) {
|
|
|
|
if ( $event->getType() === 'edit-user-talk' ) {
|
|
|
|
$extra = $event->getExtra();
|
|
|
|
if ( !empty( $extra['minoredit'] ) ) {
|
|
|
|
global $wgEnotifMinorEdits;
|
|
|
|
if ( !$wgEnotifMinorEdits || !$user->getOption( 'enotifminoredits' ) ) {
|
|
|
|
// Do not send talk page notification email
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Proceed to send talk page notification email
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
BREAKING CHANGE: Change $wgEchoDefaultNotificationTypes to be logical
Merge and deploy at the *same time* as:
* BounceHandler - I3c669945080d8e1f67880bd8a31af7f88a70904d
* mediawiki-config - I13817c139967ed9e230cfb0c87c5de66da793c96
Despite claiming to be about categories, $wgEchoDefaultNotificationTypes
was actually configuring both categories and types (which go inside
categories).
For example, 'thank-you-edit' is a type, but 'emailuser' is both
a category and a type (when used as a category, this has special
effects at Special:Preferences).
Since types and categories can and sometimes do have the same names,
this leaves no way to properly and clearly configure them. It also
makes it difficult to document what is going on (as required by
T132127).
Split into three variables:
$wgDefaultNotifyTypeAvailability - Applies unless overriden
$wgNotifyTypeAvailabilityByCategory - By category; this can be and is
displayed at Special:Preferences
$wgNotifyTypeAvailabilityByNotificationType - By type; this cannot
be displayed at Special:Preferences. To avoid confusing the user,
we introduce a restriction (which was previously followed in practice,
AFAICT) that types can only be overridden if the category is not
displayed in preferences.
Otherwise, it can look to the user like a category is on/off, but the
types within might have the opposite state.
Due to this configuration change, this is a breaking change, and needs
coordinated deployments.
This also lays the groundwork for T132127
Also change terminology to consistently use "notify type" for web/email.
It was mixing between that and output format (which unfortunately
sounds like the API format, e.g. 'model').
Bug: T132820
Bug: T132127
Change-Id: I09f39f5fc5f13f3253af9f7819bca81f1601da93
2016-04-19 02:54:15 +00:00
|
|
|
/**
|
|
|
|
* Get overrides for new users. This allows changes that only apply going forward,
|
|
|
|
* without affecting existing users.
|
|
|
|
*
|
|
|
|
* @return Associative array mapping key to boolean for whether it should be enabled
|
|
|
|
*/
|
|
|
|
public static function getNewUserPreferenceOverrides() {
|
2016-12-05 18:51:07 +00:00
|
|
|
return [
|
BREAKING CHANGE: Change $wgEchoDefaultNotificationTypes to be logical
Merge and deploy at the *same time* as:
* BounceHandler - I3c669945080d8e1f67880bd8a31af7f88a70904d
* mediawiki-config - I13817c139967ed9e230cfb0c87c5de66da793c96
Despite claiming to be about categories, $wgEchoDefaultNotificationTypes
was actually configuring both categories and types (which go inside
categories).
For example, 'thank-you-edit' is a type, but 'emailuser' is both
a category and a type (when used as a category, this has special
effects at Special:Preferences).
Since types and categories can and sometimes do have the same names,
this leaves no way to properly and clearly configure them. It also
makes it difficult to document what is going on (as required by
T132127).
Split into three variables:
$wgDefaultNotifyTypeAvailability - Applies unless overriden
$wgNotifyTypeAvailabilityByCategory - By category; this can be and is
displayed at Special:Preferences
$wgNotifyTypeAvailabilityByNotificationType - By type; this cannot
be displayed at Special:Preferences. To avoid confusing the user,
we introduce a restriction (which was previously followed in practice,
AFAICT) that types can only be overridden if the category is not
displayed in preferences.
Otherwise, it can look to the user like a category is on/off, but the
types within might have the opposite state.
Due to this configuration change, this is a breaking change, and needs
coordinated deployments.
This also lays the groundwork for T132127
Also change terminology to consistently use "notify type" for web/email.
It was mixing between that and output format (which unfortunately
sounds like the API format, e.g. 'model').
Bug: T132820
Bug: T132127
Change-Id: I09f39f5fc5f13f3253af9f7819bca81f1601da93
2016-04-19 02:54:15 +00:00
|
|
|
'echo-subscriptions-web-reverted' => false,
|
|
|
|
'echo-subscriptions-email-reverted' => false,
|
|
|
|
'echo-subscriptions-web-article-linked' => true,
|
|
|
|
'echo-subscriptions-email-mention' => true,
|
|
|
|
'echo-subscriptions-email-article-linked' => true,
|
2016-12-05 18:51:07 +00:00
|
|
|
];
|
BREAKING CHANGE: Change $wgEchoDefaultNotificationTypes to be logical
Merge and deploy at the *same time* as:
* BounceHandler - I3c669945080d8e1f67880bd8a31af7f88a70904d
* mediawiki-config - I13817c139967ed9e230cfb0c87c5de66da793c96
Despite claiming to be about categories, $wgEchoDefaultNotificationTypes
was actually configuring both categories and types (which go inside
categories).
For example, 'thank-you-edit' is a type, but 'emailuser' is both
a category and a type (when used as a category, this has special
effects at Special:Preferences).
Since types and categories can and sometimes do have the same names,
this leaves no way to properly and clearly configure them. It also
makes it difficult to document what is going on (as required by
T132127).
Split into three variables:
$wgDefaultNotifyTypeAvailability - Applies unless overriden
$wgNotifyTypeAvailabilityByCategory - By category; this can be and is
displayed at Special:Preferences
$wgNotifyTypeAvailabilityByNotificationType - By type; this cannot
be displayed at Special:Preferences. To avoid confusing the user,
we introduce a restriction (which was previously followed in practice,
AFAICT) that types can only be overridden if the category is not
displayed in preferences.
Otherwise, it can look to the user like a category is on/off, but the
types within might have the opposite state.
Due to this configuration change, this is a breaking change, and needs
coordinated deployments.
This also lays the groundwork for T132127
Also change terminology to consistently use "notify type" for web/email.
It was mixing between that and output format (which unfortunately
sounds like the API format, e.g. 'model').
Bug: T132820
Bug: T132127
Change-Id: I09f39f5fc5f13f3253af9f7819bca81f1601da93
2016-04-19 02:54:15 +00:00
|
|
|
}
|
|
|
|
|
2012-08-31 23:35:16 +00:00
|
|
|
/**
|
2016-05-25 19:03:29 +00:00
|
|
|
* Handler for LocalUserCreated hook.
|
|
|
|
* @see http://www.mediawiki.org/wiki/Manual:Hooks/LocalUserCreated
|
2012-08-31 23:35:16 +00:00
|
|
|
* @param $user User object that was created.
|
2016-05-25 19:03:29 +00:00
|
|
|
* @param $autocreated bool True when account was auto-created
|
2012-09-26 05:09:43 +00:00
|
|
|
* @return bool
|
2012-08-31 23:35:16 +00:00
|
|
|
*/
|
2016-05-25 19:03:29 +00:00
|
|
|
public static function onLocalUserCreated( $user, $autocreated ) {
|
|
|
|
if ( !$autocreated ) {
|
|
|
|
$overrides = self::getNewUserPreferenceOverrides();
|
|
|
|
foreach ( $overrides as $prefKey => $value ) {
|
|
|
|
$user->setOption( $prefKey, $value );
|
|
|
|
}
|
2013-04-25 00:41:47 +00:00
|
|
|
|
2016-05-25 19:03:29 +00:00
|
|
|
$user->saveSettings();
|
2013-04-25 00:41:47 +00:00
|
|
|
|
2016-12-05 18:51:07 +00:00
|
|
|
EchoEvent::create( [
|
2016-05-25 19:03:29 +00:00
|
|
|
'type' => 'welcome',
|
|
|
|
'agent' => $user,
|
|
|
|
// Welcome notification is sent to the agent
|
2016-12-05 18:51:07 +00:00
|
|
|
'extra' => [
|
2016-05-25 19:03:29 +00:00
|
|
|
'notifyAgent' => true
|
2016-12-05 18:51:07 +00:00
|
|
|
]
|
|
|
|
] );
|
2016-05-25 19:03:29 +00:00
|
|
|
}
|
2012-08-31 23:35:16 +00:00
|
|
|
|
2016-09-09 21:46:11 +00:00
|
|
|
$seenTime = EchoSeenTime::newFromUser( $user );
|
|
|
|
|
|
|
|
// Set seen time to UNIX epoch, so initially all notifications are unseen.
|
|
|
|
$seenTime->setTime( wfTimestamp( TS_MW, 1 ), 'all' );
|
|
|
|
|
2012-08-31 23:35:16 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-03-13 00:49:19 +00:00
|
|
|
/**
|
2015-09-22 18:16:59 +00:00
|
|
|
* Handler for UserGroupsChanged hook.
|
|
|
|
* @see http://www.mediawiki.org/wiki/Manual:Hooks/UserGroupsChanged
|
2014-05-27 18:28:37 +00:00
|
|
|
*
|
2015-09-22 18:16:59 +00:00
|
|
|
* @param User $user user that was changed
|
|
|
|
* @param string[] $add strings corresponding to groups added
|
|
|
|
* @param string[] $remove strings corresponding to groups removed
|
|
|
|
* @param User|bool $performer
|
2016-02-08 20:45:44 +00:00
|
|
|
* @param string|bool $reason Reason given by the user changing the rights
|
2014-05-27 18:28:37 +00:00
|
|
|
*
|
|
|
|
* @return bool
|
2013-03-13 00:49:19 +00:00
|
|
|
*/
|
2017-04-08 07:43:05 +00:00
|
|
|
public static function onUserGroupsChanged( $user, $add, $remove, $performer,
|
|
|
|
$reason, array $oldUGMs = [], array $newUGMs = [] ) {
|
2015-09-22 18:16:59 +00:00
|
|
|
if ( !$performer ) {
|
|
|
|
// TODO: Implement support for autopromotion
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( !$user instanceof User ) {
|
|
|
|
// TODO: Support UserRightsProxy
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( $user->equals( $performer ) ) {
|
|
|
|
// Don't notify for self changes
|
|
|
|
return true;
|
|
|
|
}
|
2013-03-13 00:49:19 +00:00
|
|
|
|
2017-04-08 07:43:05 +00:00
|
|
|
// If any old groups are in $add, those groups are having their expiry
|
|
|
|
// changed, not actually being added
|
|
|
|
$expiryChanged = [];
|
|
|
|
$reallyAdded = [];
|
|
|
|
foreach ( $add as $group ) {
|
|
|
|
if ( isset( $oldUGMs[$group] ) ) {
|
|
|
|
$expiryChanged[] = $group;
|
|
|
|
} else {
|
|
|
|
$reallyAdded[] = $group;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( $expiryChanged ) {
|
|
|
|
// use a separate notification for these, so the notification text doesn't
|
|
|
|
// get too long
|
|
|
|
EchoEvent::create(
|
|
|
|
[
|
|
|
|
'type' => 'user-rights',
|
|
|
|
'extra' => [
|
|
|
|
'user' => $user->getID(),
|
|
|
|
'expiry-changed' => $expiryChanged,
|
|
|
|
'reason' => $reason,
|
|
|
|
],
|
|
|
|
'agent' => $performer,
|
|
|
|
]
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( $reallyAdded || $remove ) {
|
2013-03-13 00:49:19 +00:00
|
|
|
EchoEvent::create(
|
2016-12-05 18:51:07 +00:00
|
|
|
[
|
2013-03-13 00:49:19 +00:00
|
|
|
'type' => 'user-rights',
|
2016-12-05 18:51:07 +00:00
|
|
|
'extra' => [
|
2013-03-13 00:49:19 +00:00
|
|
|
'user' => $user->getID(),
|
2017-04-08 07:43:05 +00:00
|
|
|
'add' => $reallyAdded,
|
2016-02-08 20:45:44 +00:00
|
|
|
'remove' => $remove,
|
|
|
|
'reason' => $reason,
|
2016-12-05 18:51:07 +00:00
|
|
|
],
|
2015-09-22 18:16:59 +00:00
|
|
|
'agent' => $performer,
|
2016-12-05 18:51:07 +00:00
|
|
|
]
|
2013-03-13 00:49:19 +00:00
|
|
|
);
|
|
|
|
}
|
2015-10-01 13:48:52 +00:00
|
|
|
|
2013-03-13 00:49:19 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-12-26 22:05:29 +00:00
|
|
|
/**
|
|
|
|
* Handler for LinksUpdateAfterInsert hook.
|
|
|
|
* @see http://www.mediawiki.org/wiki/Manual:Hooks/LinksUpdateAfterInsert
|
|
|
|
* @param $linksUpdate LinksUpdate
|
|
|
|
* @param $table string
|
|
|
|
* @param $insertions array
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
public static function onLinksUpdateAfterInsert( $linksUpdate, $table, $insertions ) {
|
2015-10-27 23:17:25 +00:00
|
|
|
global $wgRequest;
|
2013-03-18 21:48:33 +00:00
|
|
|
|
2015-10-27 23:17:25 +00:00
|
|
|
// FIXME: This doesn't work in 1.27+
|
2013-03-18 21:48:33 +00:00
|
|
|
// Rollback or undo should not trigger link notification
|
|
|
|
// @Todo Implement a better solution so it doesn't depend on the checking of
|
|
|
|
// a specific set of request variables
|
|
|
|
if ( $wgRequest->getVal( 'wpUndidRevision' ) || $wgRequest->getVal( 'action' ) == 'rollback' ) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-01-16 00:13:58 +00:00
|
|
|
// Handle only
|
2013-05-01 20:48:12 +00:00
|
|
|
// 1. inserts to pagelinks table &&
|
2013-01-16 00:13:58 +00:00
|
|
|
// 2. content namespace pages &&
|
|
|
|
// 3. non-transcluding pages &&
|
|
|
|
// 4. non-redirect pages
|
|
|
|
if ( $table !== 'pagelinks' || !MWNamespace::isContent( $linksUpdate->mTitle->getNamespace() )
|
2015-10-01 13:48:52 +00:00
|
|
|
|| !$linksUpdate->mRecursive || $linksUpdate->mTitle->isRedirect()
|
|
|
|
) {
|
2013-01-04 01:36:12 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-12-05 18:51:07 +00:00
|
|
|
if ( is_callable( [ $linksUpdate, 'getTriggeringUser' ] ) ) {
|
2015-10-27 23:17:25 +00:00
|
|
|
$user = $linksUpdate->getTriggeringUser();
|
|
|
|
} else {
|
|
|
|
global $wgUser;
|
|
|
|
$user = $wgUser;
|
|
|
|
}
|
|
|
|
|
2016-05-23 19:22:41 +00:00
|
|
|
$revid = $linksUpdate->getRevision() ? $linksUpdate->getRevision()->getId() : null;
|
|
|
|
|
2013-01-15 23:21:39 +00:00
|
|
|
// link notification is boundless as you can include infinite number of links in a page
|
|
|
|
// db insert is expensive, limit it to a reasonable amount, we can increase this limit
|
|
|
|
// once the storage is on Redis
|
|
|
|
$max = 10;
|
2013-01-08 23:57:28 +00:00
|
|
|
// Only create notifications for links to content namespace pages
|
2013-01-15 23:21:39 +00:00
|
|
|
// @Todo - use one big insert instead of individual insert inside foreach loop
|
2014-05-27 18:28:37 +00:00
|
|
|
foreach ( $insertions as $page ) {
|
2013-01-15 23:21:39 +00:00
|
|
|
if ( MWNamespace::isContent( $page['pl_namespace'] ) ) {
|
|
|
|
$title = Title::makeTitle( $page['pl_namespace'], $page['pl_title'] );
|
2013-05-03 16:30:29 +00:00
|
|
|
if ( $title->isRedirect() ) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2016-07-27 14:52:18 +00:00
|
|
|
$linkFromPageId = $linksUpdate->mTitle->getArticleId();
|
2016-12-05 18:51:07 +00:00
|
|
|
EchoEvent::create( [
|
2013-01-15 23:21:39 +00:00
|
|
|
'type' => 'page-linked',
|
|
|
|
'title' => $title,
|
2015-10-27 23:17:25 +00:00
|
|
|
'agent' => $user,
|
2016-12-05 18:51:07 +00:00
|
|
|
'extra' => [
|
2016-07-27 14:52:18 +00:00
|
|
|
'target-page' => $linkFromPageId,
|
|
|
|
'link-from-page-id' => $linkFromPageId,
|
2016-05-23 19:22:41 +00:00
|
|
|
'revid' => $revid,
|
2016-12-05 18:51:07 +00:00
|
|
|
]
|
|
|
|
] );
|
2013-01-15 23:21:39 +00:00
|
|
|
$max--;
|
|
|
|
}
|
|
|
|
if ( $max < 0 ) {
|
|
|
|
break;
|
2013-01-04 01:36:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-12-26 22:05:29 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-05-17 00:29:37 +00:00
|
|
|
/**
|
|
|
|
* Handler for BeforePageDisplay hook.
|
|
|
|
* @see http://www.mediawiki.org/wiki/Manual:Hooks/BeforePageDisplay
|
|
|
|
* @param $out OutputPage object
|
|
|
|
* @param $skin Skin being used.
|
2012-09-02 09:30:38 +00:00
|
|
|
* @return bool true in all cases
|
2012-05-17 00:29:37 +00:00
|
|
|
*/
|
2016-05-20 10:10:51 +00:00
|
|
|
public static function beforePageDisplay( $out, $skin ) {
|
2015-09-14 21:12:59 +00:00
|
|
|
if ( $out->getUser()->isLoggedIn() && $skin->getSkinName() !== 'minerva' ) {
|
2012-11-01 21:59:53 +00:00
|
|
|
// Load the module for the Notifications flyout
|
2016-12-05 18:51:07 +00:00
|
|
|
$out->addModules( [ 'ext.echo.init' ] );
|
2013-05-15 23:10:49 +00:00
|
|
|
// Load the styles for the Notifications badge
|
2016-12-05 18:51:07 +00:00
|
|
|
$out->addModuleStyles( [
|
2015-09-16 19:48:56 +00:00
|
|
|
'ext.echo.styles.badge',
|
2015-09-14 19:52:01 +00:00
|
|
|
'ext.echo.badgeicons'
|
2016-12-05 18:51:07 +00:00
|
|
|
] );
|
2012-06-01 10:57:09 +00:00
|
|
|
}
|
2013-11-13 03:17:04 +00:00
|
|
|
|
2012-06-01 10:57:09 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-05-17 00:29:37 +00:00
|
|
|
/**
|
|
|
|
* Handler for PersonalUrls hook.
|
2012-11-01 21:59:53 +00:00
|
|
|
* Add a "Notifications" item to the user toolbar ('personal URLs').
|
2012-05-17 00:29:37 +00:00
|
|
|
* @see http://www.mediawiki.org/wiki/Manual:Hooks/PersonalUrls
|
|
|
|
* @param &$personal_urls Array of URLs to append to.
|
|
|
|
* @param &$title Title of page being visited.
|
2013-11-13 03:17:04 +00:00
|
|
|
* @param SkinTemplate $sk
|
2012-09-02 09:30:38 +00:00
|
|
|
* @return bool true in all cases
|
2012-05-17 00:29:37 +00:00
|
|
|
*/
|
2016-05-20 10:10:51 +00:00
|
|
|
public static function onPersonalUrls( &$personal_urls, &$title, $sk ) {
|
2016-04-26 01:33:01 +00:00
|
|
|
global $wgEchoNewMsgAlert, $wgEchoShowFooterNotice;
|
2013-11-13 03:17:04 +00:00
|
|
|
$user = $sk->getUser();
|
2015-06-01 23:22:48 +00:00
|
|
|
if ( $user->isAnon() ) {
|
2012-06-01 10:57:09 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-03-09 19:01:42 +00:00
|
|
|
// Attempt to mark a notification as read when visiting a page
|
|
|
|
$subtractAlerts = 0;
|
|
|
|
$subtractMessages = 0;
|
2016-12-05 18:51:07 +00:00
|
|
|
$eventIds = [];
|
2014-08-07 00:07:34 +00:00
|
|
|
if ( $title->getArticleID() ) {
|
2016-03-04 19:23:02 +00:00
|
|
|
$eventMapper = new EchoEventMapper();
|
|
|
|
$events = $eventMapper->fetchUnreadByUserAndPage( $user, $title->getArticleID() );
|
|
|
|
|
|
|
|
if ( $events ) {
|
|
|
|
/* @var EchoEvent $event */
|
|
|
|
foreach ( $events as $event ) {
|
|
|
|
if ( $event->getSection() === EchoAttributeManager::MESSAGE ) {
|
2016-05-03 01:56:13 +00:00
|
|
|
$subtractMessages++;
|
2016-03-09 19:01:42 +00:00
|
|
|
} else {
|
|
|
|
// ALERT
|
2016-05-03 01:56:13 +00:00
|
|
|
$subtractAlerts++;
|
2016-03-09 19:01:42 +00:00
|
|
|
}
|
2016-03-04 19:23:02 +00:00
|
|
|
$eventIds[] = $event->getId();
|
2016-03-09 19:01:42 +00:00
|
|
|
}
|
2014-08-07 00:07:34 +00:00
|
|
|
}
|
|
|
|
}
|
2016-06-13 20:41:12 +00:00
|
|
|
|
|
|
|
// Attempt to mark as read the event IDs in the ?markasread= parameter, if present
|
|
|
|
$markAsReadIds = explode( '|', $sk->getOutput()->getRequest()->getText( 'markasread' ) );
|
|
|
|
if ( $markAsReadIds ) {
|
|
|
|
// gather the IDs that we didn't already find with target_pages
|
2016-12-05 18:51:07 +00:00
|
|
|
$eventsToMarkAsRead = [];
|
2016-06-13 20:41:12 +00:00
|
|
|
foreach ( $markAsReadIds as $markAsReadId ) {
|
|
|
|
$markAsReadId = intval( $markAsReadId );
|
|
|
|
if ( $markAsReadId !== 0 && !in_array( $markAsReadId, $eventIds ) ) {
|
|
|
|
$eventsToMarkAsRead[] = $markAsReadId;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( $eventsToMarkAsRead ) {
|
|
|
|
// fetch the notifications to adjust the counters
|
|
|
|
$notifMapper = new EchoNotificationMapper();
|
|
|
|
$notifs = $notifMapper->fetchByUserEvents( $user, $eventsToMarkAsRead );
|
|
|
|
|
|
|
|
foreach ( $notifs as $notif ) {
|
|
|
|
if ( !$notif->getReadTimestamp() ) {
|
|
|
|
if ( $notif->getEvent()->getSection() === EchoAttributeManager::MESSAGE ) {
|
|
|
|
$subtractMessages++;
|
|
|
|
} else {
|
|
|
|
$subtractAlerts++;
|
|
|
|
}
|
|
|
|
$eventIds[] = intval( $notif->getEvent()->getId() );
|
|
|
|
}
|
2016-05-03 01:56:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-06-13 20:41:12 +00:00
|
|
|
|
2016-05-03 01:56:13 +00:00
|
|
|
if ( $eventIds ) {
|
|
|
|
DeferredUpdates::addCallableUpdate( function () use ( $user, $eventIds ) {
|
|
|
|
$notifUser = MWEchoNotifUser::newFromUser( $user );
|
|
|
|
$notifUser->markRead( $eventIds );
|
|
|
|
} );
|
|
|
|
}
|
2014-08-07 00:07:34 +00:00
|
|
|
|
2013-11-13 03:17:04 +00:00
|
|
|
// Add a "My notifications" item to personal URLs
|
2015-07-08 00:10:49 +00:00
|
|
|
$notifUser = MWEchoNotifUser::newFromUser( $user );
|
2016-03-09 19:01:42 +00:00
|
|
|
$msgCount = $notifUser->getMessageCount() - $subtractMessages;
|
|
|
|
$alertCount = $notifUser->getAlertCount() - $subtractAlerts;
|
2016-04-11 08:20:08 +00:00
|
|
|
// But make sure we never show a negative number (T130853)
|
|
|
|
$msgCount = max( 0, $msgCount );
|
|
|
|
$alertCount = max( 0, $alertCount );
|
|
|
|
|
2015-08-13 00:54:16 +00:00
|
|
|
$msgNotificationTimestamp = $notifUser->getLastUnreadMessageTime();
|
|
|
|
$alertNotificationTimestamp = $notifUser->getLastUnreadAlertTime();
|
|
|
|
|
2016-09-15 21:16:06 +00:00
|
|
|
$seenTime = EchoSeenTime::newFromUser( $user );
|
|
|
|
if ( $title->isSpecial( 'Notifications' ) ) {
|
|
|
|
// If this is the Special:Notifications page, seenTime to now
|
|
|
|
$seenTime->setTime( wfTimestamp( TS_MW ), EchoAttributeManager::ALL );
|
|
|
|
}
|
2016-09-23 18:46:44 +00:00
|
|
|
$seenAlertTime = $seenTime->getTime( 'alert', TS_ISO_8601 );
|
|
|
|
$seenMsgTime = $seenTime->getTime( 'message', TS_ISO_8601 );
|
2016-09-13 20:00:35 +00:00
|
|
|
|
2016-12-05 18:51:07 +00:00
|
|
|
$sk->getOutput()->addJsConfigVars( 'wgEchoSeenTime', [
|
2015-09-03 01:11:10 +00:00
|
|
|
'alert' => $seenAlertTime,
|
2016-07-22 18:59:10 +00:00
|
|
|
'notice' => $seenMsgTime,
|
2016-12-05 18:51:07 +00:00
|
|
|
] );
|
2015-08-13 00:54:16 +00:00
|
|
|
|
2016-04-26 01:33:01 +00:00
|
|
|
if (
|
|
|
|
$wgEchoShowFooterNotice &&
|
2016-08-01 22:09:24 +00:00
|
|
|
!$user->getOption( 'echo-dismiss-special-page-invitation' )
|
2016-04-26 01:33:01 +00:00
|
|
|
) {
|
2016-08-01 22:09:24 +00:00
|
|
|
$sk->getOutput()->addJsConfigVars( 'wgEchoShowSpecialPageInvitation', true );
|
2016-04-26 01:33:01 +00:00
|
|
|
}
|
|
|
|
|
2017-03-12 13:04:20 +00:00
|
|
|
$msgFormattedCount = EchoNotificationController::formatNotificationCount( $msgCount );
|
|
|
|
$alertFormattedCount = EchoNotificationController::formatNotificationCount( $alertCount );
|
|
|
|
|
|
|
|
$msgText = wfMessage( 'echo-notification-notice', $msgCount );
|
|
|
|
$alertText = wfMessage( 'echo-notification-alert', $alertCount );
|
2015-08-13 00:54:16 +00:00
|
|
|
|
2015-07-08 00:10:49 +00:00
|
|
|
$url = SpecialPage::getTitleFor( 'Notifications' )->getLocalURL();
|
|
|
|
|
2015-09-10 17:15:30 +00:00
|
|
|
// HACK: inverted icons only work in the "MediaWiki" OOUI theme
|
|
|
|
// Avoid flashes in skins that don't use it (T111821)
|
2015-09-14 10:38:44 +00:00
|
|
|
$sk->getOutput()->setupOOUI( strtolower( $sk->getSkinName() ), $sk->getOutput()->getLanguage()->getDir() );
|
2015-09-10 17:15:30 +00:00
|
|
|
|
2016-12-05 18:51:07 +00:00
|
|
|
$msgLinkClasses = [ "mw-echo-notifications-badge", "mw-echo-notification-badge-nojs" ];
|
|
|
|
$alertLinkClasses = [ "mw-echo-notifications-badge", "mw-echo-notification-badge-nojs" ];
|
2015-08-13 00:54:16 +00:00
|
|
|
|
2015-09-15 22:58:13 +00:00
|
|
|
$hasUnseen = false;
|
2015-07-08 00:10:49 +00:00
|
|
|
if (
|
2015-08-13 00:54:16 +00:00
|
|
|
$msgCount != 0 && // no unread notifications
|
|
|
|
$msgNotificationTimestamp !== false && // should already always be false if count === 0
|
2016-09-13 20:00:35 +00:00
|
|
|
( $seenMsgTime === null || $seenMsgTime < $msgNotificationTimestamp->getTimestamp( TS_ISO_8601 ) ) // there are no unseen notifications
|
2015-07-08 00:10:49 +00:00
|
|
|
) {
|
2015-08-13 00:54:16 +00:00
|
|
|
$msgLinkClasses[] = 'mw-echo-unseen-notifications';
|
2015-09-15 22:58:13 +00:00
|
|
|
$hasUnseen = true;
|
2016-07-20 00:24:17 +00:00
|
|
|
} elseif ( $msgCount === 0 ) {
|
|
|
|
$msgLinkClasses[] = 'mw-echo-notifications-badge-all-read';
|
2013-11-13 03:17:04 +00:00
|
|
|
}
|
2015-08-13 00:54:16 +00:00
|
|
|
|
2016-12-20 18:40:54 +00:00
|
|
|
if ( $msgCount > MWEchoNotifUser::MAX_BADGE_COUNT ) {
|
|
|
|
$msgLinkClasses[] = 'mw-echo-notifications-badge-long-label';
|
|
|
|
}
|
|
|
|
|
2015-08-13 00:54:16 +00:00
|
|
|
if (
|
|
|
|
$alertCount != 0 && // no unread notifications
|
|
|
|
$alertNotificationTimestamp !== false && // should already always be false if count === 0
|
2016-09-13 20:00:35 +00:00
|
|
|
( $seenAlertTime === null || $seenAlertTime < $alertNotificationTimestamp->getTimestamp( TS_ISO_8601 ) ) // all notifications have already been seen
|
2015-08-13 00:54:16 +00:00
|
|
|
) {
|
|
|
|
$alertLinkClasses[] = 'mw-echo-unseen-notifications';
|
2015-09-15 22:58:13 +00:00
|
|
|
$hasUnseen = true;
|
2016-07-20 00:24:17 +00:00
|
|
|
} elseif ( $alertCount === 0 ) {
|
|
|
|
$alertLinkClasses[] = 'mw-echo-notifications-badge-all-read';
|
2015-08-13 00:54:16 +00:00
|
|
|
}
|
|
|
|
|
2016-12-22 21:08:27 +00:00
|
|
|
if ( $alertCount > MWEchoNotifUser::MAX_BADGE_COUNT ) {
|
2016-12-20 18:40:54 +00:00
|
|
|
$alertLinkClasses[] = 'mw-echo-notifications-badge-long-label';
|
|
|
|
}
|
|
|
|
|
2016-12-05 18:51:07 +00:00
|
|
|
$alertLink = [
|
2015-07-08 00:10:49 +00:00
|
|
|
'href' => $url,
|
2015-08-13 00:54:16 +00:00
|
|
|
'text' => $alertText,
|
2015-07-08 00:10:49 +00:00
|
|
|
'active' => ( $url == $title->getLocalUrl() ),
|
2015-08-13 00:54:16 +00:00
|
|
|
'class' => $alertLinkClasses,
|
2016-12-05 18:51:07 +00:00
|
|
|
'data' => [
|
2016-07-20 00:24:17 +00:00
|
|
|
'counter-num' => $alertCount,
|
2017-03-12 13:04:20 +00:00
|
|
|
'counter-text' => $alertFormattedCount,
|
2016-12-05 18:51:07 +00:00
|
|
|
],
|
|
|
|
];
|
2015-08-13 00:54:16 +00:00
|
|
|
|
2016-12-05 18:51:07 +00:00
|
|
|
$insertUrls = [
|
2015-09-07 23:54:47 +00:00
|
|
|
'notifications-alert' => $alertLink,
|
2016-12-05 18:51:07 +00:00
|
|
|
];
|
2015-07-08 00:10:49 +00:00
|
|
|
|
2016-12-05 18:51:07 +00:00
|
|
|
$msgLink = [
|
2016-02-23 09:20:24 +00:00
|
|
|
'href' => $url,
|
|
|
|
'text' => $msgText,
|
|
|
|
'active' => ( $url == $title->getLocalUrl() ),
|
|
|
|
'class' => $msgLinkClasses,
|
2016-12-05 18:51:07 +00:00
|
|
|
'data' => [
|
2016-07-20 00:24:17 +00:00
|
|
|
'counter-num' => $msgCount,
|
2017-03-12 13:04:20 +00:00
|
|
|
'counter-text' => $msgFormattedCount,
|
2016-12-05 18:51:07 +00:00
|
|
|
],
|
|
|
|
];
|
2015-08-13 00:54:16 +00:00
|
|
|
|
2016-07-21 18:19:17 +00:00
|
|
|
$insertUrls['notifications-notice'] = $msgLink;
|
2015-08-13 00:54:16 +00:00
|
|
|
|
2015-07-08 00:10:49 +00:00
|
|
|
$personal_urls = wfArrayInsertAfter( $personal_urls, $insertUrls, 'userpage' );
|
2013-11-13 03:17:04 +00:00
|
|
|
|
2015-09-15 22:58:13 +00:00
|
|
|
if ( $hasUnseen ) {
|
|
|
|
// Record that the user is going to see an indicator that they have unread notifications
|
2017-03-17 11:16:28 +00:00
|
|
|
MediaWikiServices::getInstance()->getStatsdDataFactory()->increment( 'echo.unseen' );
|
2015-09-15 22:58:13 +00:00
|
|
|
}
|
|
|
|
|
2013-11-13 03:17:04 +00:00
|
|
|
// If the user has new messages, display a talk page alert
|
2015-09-14 17:34:44 +00:00
|
|
|
// We need to check:
|
|
|
|
// * Orange alert is enabled in configuration
|
|
|
|
// * Enabled in user preferences
|
|
|
|
// * User actually has new messages
|
|
|
|
// * User is not viewing their user talk page, as user_newtalk
|
2017-06-20 02:41:30 +00:00
|
|
|
// will not have been cleared yet. (bug T107655).
|
2015-09-14 17:34:44 +00:00
|
|
|
if ( $wgEchoNewMsgAlert && $user->getOption( 'echo-show-alert' )
|
2015-10-01 13:48:52 +00:00
|
|
|
&& $user->getNewtalk() && !$user->getTalkPage()->equals( $title )
|
2015-09-14 17:34:44 +00:00
|
|
|
) {
|
2016-12-05 18:51:07 +00:00
|
|
|
if ( Hooks::run( 'BeforeDisplayOrangeAlert', [ $user, $title ] ) ) {
|
2016-10-13 18:55:34 +00:00
|
|
|
$personal_urls['mytalk']['text'] = $sk->msg( 'echo-new-messages' )->text();
|
2016-12-05 18:51:07 +00:00
|
|
|
$personal_urls['mytalk']['class'] = [ 'mw-echo-alert' ];
|
2016-10-13 18:55:34 +00:00
|
|
|
$sk->getOutput()->addModuleStyles( 'ext.echo.styles.alert' );
|
|
|
|
}
|
2012-12-12 02:18:51 +00:00
|
|
|
}
|
2012-06-01 10:57:09 +00:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2012-07-17 22:19:32 +00:00
|
|
|
|
2012-05-17 00:29:37 +00:00
|
|
|
/**
|
2013-06-08 01:05:35 +00:00
|
|
|
* Handler for AbortTalkPageEmailNotification hook.
|
|
|
|
* @see http://www.mediawiki.org/wiki/Manual:Hooks/AbortTalkPageEmailNotification
|
|
|
|
* @param $targetUser User
|
|
|
|
* @param $title Title
|
|
|
|
* @return bool
|
2012-05-17 00:29:37 +00:00
|
|
|
*/
|
2016-05-20 10:10:51 +00:00
|
|
|
public static function onAbortTalkPageEmailNotification( $targetUser, $title ) {
|
2013-06-08 01:05:35 +00:00
|
|
|
global $wgEchoNotifications;
|
|
|
|
|
|
|
|
// Send legacy talk page email notification if
|
|
|
|
// 1. echo is disabled for them or
|
|
|
|
// 2. echo talk page notification is disabled
|
2015-06-01 23:22:48 +00:00
|
|
|
if ( !isset( $wgEchoNotifications['edit-user-talk'] ) ) {
|
2013-06-08 01:05:35 +00:00
|
|
|
// Legacy talk page email notification
|
|
|
|
return true;
|
2012-11-16 21:03:57 +00:00
|
|
|
}
|
2013-06-08 01:05:35 +00:00
|
|
|
|
|
|
|
// Echo talk page email notification
|
|
|
|
return false;
|
2012-07-17 22:19:32 +00:00
|
|
|
}
|
2012-07-31 21:18:16 +00:00
|
|
|
|
2014-02-21 01:48:08 +00:00
|
|
|
/**
|
|
|
|
* Handler for AbortWatchlistEmailNotification hook.
|
|
|
|
* @see http://www.mediawiki.org/wiki/Manual:Hooks/AbortWatchlistEmailNotification
|
|
|
|
* @param $targetUser User
|
|
|
|
* @param $title Title
|
|
|
|
* @param $emailNotification EmailNotification The email notification object that sends non-echo notifications
|
|
|
|
* @return bool
|
|
|
|
*/
|
2016-05-20 10:10:51 +00:00
|
|
|
public static function onSendWatchlistEmailNotification( $targetUser, $title, $emailNotification ) {
|
2014-02-21 01:48:08 +00:00
|
|
|
// If a user is watching his/her own talk page, do not send talk page watchlist
|
|
|
|
// email notification if the user is receiving Echo talk page notification
|
|
|
|
if ( $title->isTalkPage() && $targetUser->getTalkPage()->equals( $title ) ) {
|
2014-07-22 21:33:22 +00:00
|
|
|
$attributeManager = EchoAttributeManager::newFromGlobalVars();
|
|
|
|
$events = $attributeManager->getUserEnabledEvents( $targetUser, 'email' );
|
2015-06-01 23:22:48 +00:00
|
|
|
if ( in_array( 'edit-user-talk', $events ) ) {
|
2014-02-21 01:48:08 +00:00
|
|
|
// Do not send watchlist email notification, the user will receive an Echo notification
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2015-10-01 13:48:52 +00:00
|
|
|
|
2014-02-21 01:48:08 +00:00
|
|
|
// Proceed to send watchlist email notification
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-11-01 21:59:53 +00:00
|
|
|
/**
|
|
|
|
* Handler for MakeGlobalVariablesScript hook.
|
|
|
|
* @see http://www.mediawiki.org/wiki/Manual:Hooks/MakeGlobalVariablesScript
|
2013-02-28 23:52:12 +00:00
|
|
|
* @param &$vars array Variables to be added into the output
|
2012-11-01 21:59:53 +00:00
|
|
|
* @param $outputPage OutputPage instance calling the hook
|
|
|
|
* @return bool true in all cases
|
|
|
|
*/
|
2012-09-02 09:30:38 +00:00
|
|
|
public static function makeGlobalVariablesScript( &$vars, OutputPage $outputPage ) {
|
2016-12-01 16:50:18 +00:00
|
|
|
global $wgEchoEventLoggingSchemas, $wgEchoEventLoggingVersion;
|
2012-07-31 21:18:16 +00:00
|
|
|
$user = $outputPage->getUser();
|
|
|
|
|
2012-08-01 19:53:05 +00:00
|
|
|
// Provide info for the Overlay
|
|
|
|
|
2015-11-12 18:38:04 +00:00
|
|
|
if ( $user->isLoggedIn() ) {
|
2016-12-01 16:50:18 +00:00
|
|
|
$vars['wgEchoEventLoggingSchemas'] = $wgEchoEventLoggingSchemas;
|
|
|
|
$vars['wgEchoEventLoggingVersion'] = $wgEchoEventLoggingVersion;
|
2015-06-02 00:59:05 +00:00
|
|
|
} elseif (
|
2015-03-21 02:41:54 +00:00
|
|
|
$outputPage->getTitle()->equals( SpecialPage::getTitleFor( 'JavaScriptTest', 'qunit' ) ) ||
|
|
|
|
// Also if running from /plain or /export
|
|
|
|
$outputPage->getTitle()->isSubpageOf( SpecialPage::getTitleFor( 'JavaScriptTest', 'qunit' ) )
|
|
|
|
) {
|
2014-08-05 22:18:38 +00:00
|
|
|
// For testing purposes
|
2016-12-05 18:51:07 +00:00
|
|
|
$vars['wgEchoEventLoggingSchemas'] = [
|
|
|
|
'EchoInteraction' => [],
|
|
|
|
];
|
2012-07-31 21:18:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2012-07-31 00:29:49 +00:00
|
|
|
|
2016-05-11 00:16:14 +00:00
|
|
|
public static function onOutputPageCheckLastModified( array &$modifiedTimes, OutputPage $out ) {
|
|
|
|
$user = $out->getUser();
|
2016-05-10 23:56:07 +00:00
|
|
|
if ( $user->isLoggedIn() ) {
|
|
|
|
$notifUser = MWEchoNotifUser::newFromUser( $user );
|
|
|
|
$lastUpdate = $notifUser->getGlobalUpdateTime();
|
|
|
|
if ( $lastUpdate !== false ) {
|
2016-08-15 23:41:44 +00:00
|
|
|
$modifiedTimes['notifications-global'] = $lastUpdate;
|
2016-05-10 23:56:07 +00:00
|
|
|
}
|
2016-09-06 17:38:01 +00:00
|
|
|
|
2016-09-13 20:00:35 +00:00
|
|
|
$modifiedTimes['notifications-seen-alert'] = EchoSeenTime::newFromUser( $user )->getTime( 'alert' );
|
|
|
|
$modifiedTimes['notifications-seen-message'] = EchoSeenTime::newFromUser( $user )->getTime( 'message' );
|
2016-05-10 23:56:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-11-01 21:59:53 +00:00
|
|
|
/**
|
2013-05-05 00:49:08 +00:00
|
|
|
* Handler for GetNewMessagesAlert hook.
|
|
|
|
* We're using the GetNewMessagesAlert hook instead of the
|
|
|
|
* ArticleEditUpdateNewTalk hook since we still want the user_newtalk data
|
|
|
|
* to be updated and availble to client-side tools and the API.
|
|
|
|
* @see http://www.mediawiki.org/wiki/Manual:Hooks/GetNewMessagesAlert
|
|
|
|
* @param &$newMessagesAlert String An alert that the user has new messages
|
|
|
|
* or an empty string if the user does not (empty by default)
|
|
|
|
* @param $newtalks Array This will be empty if the user has no new messages
|
|
|
|
* or an Array containing links and revisions if there are new messages
|
|
|
|
* @param $user User The user who is loading the page
|
|
|
|
* @param $out Output object
|
|
|
|
* @return bool Should return false to prevent the new messages alert (OBOD)
|
|
|
|
* or true to allow the new messages alert
|
2012-11-01 21:59:53 +00:00
|
|
|
*/
|
2016-05-20 10:10:51 +00:00
|
|
|
public static function abortNewMessagesAlert( &$newMessagesAlert, $newtalks, $user, $out ) {
|
2013-03-20 01:10:23 +00:00
|
|
|
global $wgEchoNotifications;
|
2013-06-08 01:05:35 +00:00
|
|
|
|
2012-11-01 21:59:53 +00:00
|
|
|
// If the user has the notifications flyout turned on and is receiving
|
2013-05-05 00:49:08 +00:00
|
|
|
// notifications for talk page messages, disable the new messages alert.
|
|
|
|
if ( $user->isLoggedIn()
|
2013-03-20 01:10:23 +00:00
|
|
|
&& isset( $wgEchoNotifications['edit-user-talk'] )
|
|
|
|
) {
|
2013-05-05 00:49:08 +00:00
|
|
|
// hide new messages alert
|
2012-11-01 21:59:53 +00:00
|
|
|
return false;
|
|
|
|
} else {
|
2013-05-05 00:49:08 +00:00
|
|
|
// show new messages alert
|
2012-11-01 21:59:53 +00:00
|
|
|
return true;
|
|
|
|
}
|
2012-07-31 20:44:43 +00:00
|
|
|
}
|
2012-07-18 19:39:33 +00:00
|
|
|
|
2012-11-01 21:59:53 +00:00
|
|
|
/**
|
|
|
|
* Handler for ArticleRollbackComplete hook.
|
|
|
|
* @see http://www.mediawiki.org/wiki/Manual:Hooks/ArticleRollbackComplete
|
2013-02-28 23:52:12 +00:00
|
|
|
* @param $page WikiPage The article that was edited
|
|
|
|
* @param $agent User The user who did the rollback
|
|
|
|
* @param $newRevision Revision The revision the page was reverted back to
|
|
|
|
* @param $oldRevision Revision The revision of the top edit that was reverted
|
2012-11-01 21:59:53 +00:00
|
|
|
* @return bool true in all cases
|
|
|
|
*/
|
2016-05-20 10:10:51 +00:00
|
|
|
public static function onRollbackComplete( $page, $agent, $newRevision, $oldRevision ) {
|
2012-07-18 19:39:33 +00:00
|
|
|
$victimId = $oldRevision->getUser();
|
|
|
|
|
2013-11-16 19:24:04 +00:00
|
|
|
if (
|
|
|
|
$victimId && // No notifications for anonymous users
|
|
|
|
!$oldRevision->getContent()->equals( $newRevision->getContent() ) // No notifications for null rollbacks
|
|
|
|
) {
|
2016-12-05 18:51:07 +00:00
|
|
|
EchoEvent::create( [
|
2012-07-18 19:39:33 +00:00
|
|
|
'type' => 'reverted',
|
|
|
|
'title' => $page->getTitle(),
|
2016-12-05 18:51:07 +00:00
|
|
|
'extra' => [
|
2012-07-18 19:39:33 +00:00
|
|
|
'revid' => $page->getRevision()->getId(),
|
|
|
|
'reverted-user-id' => $victimId,
|
|
|
|
'reverted-revision-id' => $oldRevision->getId(),
|
|
|
|
'method' => 'rollback',
|
2016-12-05 18:51:07 +00:00
|
|
|
],
|
2012-07-18 19:39:33 +00:00
|
|
|
'agent' => $agent,
|
2016-12-05 18:51:07 +00:00
|
|
|
] );
|
2012-07-18 19:39:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2013-04-12 22:12:22 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Handler for UserSaveSettings hook.
|
|
|
|
* @see http://www.mediawiki.org/wiki/Manual:Hooks/UserSaveSettings
|
|
|
|
* @param $user User whose settings were saved
|
|
|
|
* @return bool true in all cases
|
|
|
|
*/
|
2016-05-20 10:10:51 +00:00
|
|
|
public static function onUserSaveSettings( $user ) {
|
2013-11-21 00:31:01 +00:00
|
|
|
// Extensions like AbuseFilter might create an account, but
|
|
|
|
// the tables we need might not exist. Bug 57335
|
|
|
|
if ( !defined( 'MW_UPDATER' ) ) {
|
|
|
|
// Reset the notification count since it may have changed due to user
|
|
|
|
// option changes. This covers both explicit changes in the preferences
|
|
|
|
// and changes made through the options API (since both call this hook).
|
2016-12-08 20:50:03 +00:00
|
|
|
DeferredUpdates::addCallableUpdate( function () use ( $user ) {
|
|
|
|
MWEchoNotifUser::newFromUser( $user )->resetNotificationCount();
|
|
|
|
} );
|
2013-11-21 00:31:01 +00:00
|
|
|
}
|
2015-10-01 13:48:52 +00:00
|
|
|
|
2013-04-12 22:12:22 +00:00
|
|
|
return true;
|
|
|
|
}
|
2013-04-26 16:08:40 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Handler for UserLoadOptions hook.
|
|
|
|
* @see http://www.mediawiki.org/wiki/Manual:Hooks/UserLoadOptions
|
|
|
|
* @param $user User whose options were loaded
|
|
|
|
* @param $options Options can be modified
|
|
|
|
* @return bool true in all cases
|
|
|
|
*/
|
|
|
|
public static function onUserLoadOptions( $user, &$options ) {
|
2013-05-09 21:23:15 +00:00
|
|
|
// Use existing enotifusertalkpages option for echo-subscriptions-email-edit-user-talk
|
|
|
|
if ( isset( $options['enotifusertalkpages'] ) ) {
|
2015-10-01 13:48:52 +00:00
|
|
|
$options['echo-subscriptions-email-edit-user-talk'] = $options['enotifusertalkpages'];
|
2013-04-26 16:08:40 +00:00
|
|
|
}
|
2015-10-01 13:48:52 +00:00
|
|
|
|
2013-04-26 16:08:40 +00:00
|
|
|
return true;
|
|
|
|
}
|
2013-04-30 02:53:33 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Handler for UserSaveOptions hook.
|
|
|
|
* @see http://www.mediawiki.org/wiki/Manual:Hooks/UserSaveOptions
|
|
|
|
* @param $user User whose options are being saved
|
|
|
|
* @param $options Options can be modified
|
|
|
|
* @return bool true in all cases
|
|
|
|
*/
|
|
|
|
public static function onUserSaveOptions( $user, &$options ) {
|
2013-05-09 21:23:15 +00:00
|
|
|
// echo-subscriptions-email-edit-user-talk is just a virtual option,
|
|
|
|
// save the value in the real option enotifusertalkpages
|
|
|
|
if ( isset( $options['echo-subscriptions-email-edit-user-talk'] ) ) {
|
|
|
|
$options['enotifusertalkpages'] = $options['echo-subscriptions-email-edit-user-talk'];
|
|
|
|
unset( $options['echo-subscriptions-email-edit-user-talk'] );
|
2013-04-30 02:53:33 +00:00
|
|
|
}
|
2013-05-16 21:14:26 +00:00
|
|
|
|
2013-04-30 02:53:33 +00:00
|
|
|
return true;
|
|
|
|
}
|
2013-05-24 22:51:47 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Handler for UserClearNewTalkNotification hook.
|
|
|
|
* @see http://www.mediawiki.org/wiki/Manual:Hooks/UserClearNewTalkNotification
|
|
|
|
* @param $user User whose talk page notification should be marked as read
|
|
|
|
* @return bool true in all cases
|
|
|
|
*/
|
|
|
|
public static function onUserClearNewTalkNotification( User $user ) {
|
|
|
|
if ( !$user->isAnon() ) {
|
2015-10-01 13:48:52 +00:00
|
|
|
DeferredUpdates::addCallableUpdate( function () use ( $user ) {
|
2015-08-26 23:31:58 +00:00
|
|
|
MWEchoNotifUser::newFromUser( $user )->clearTalkNotification();
|
|
|
|
} );
|
2013-05-24 22:51:47 +00:00
|
|
|
}
|
2015-08-26 23:31:58 +00:00
|
|
|
|
2013-05-24 22:51:47 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-06-26 01:04:31 +00:00
|
|
|
/**
|
|
|
|
* Handler for ParserTestTables hook, makes sure that Echo's tables are present during tests
|
|
|
|
* @see http://www.mediawiki.org/wiki/Manual:Hooks/UserClearNewTalkNotification
|
|
|
|
* @param array $tables List of DB tables to be used for parser tests
|
|
|
|
* @return bool true in all cases
|
|
|
|
*/
|
|
|
|
public static function onParserTestTables( &$tables ) {
|
|
|
|
$tables[] = 'echo_event';
|
|
|
|
$tables[] = 'echo_notification';
|
|
|
|
$tables[] = 'echo_email_batch';
|
2015-10-01 13:48:52 +00:00
|
|
|
|
2014-06-26 01:04:31 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-09-19 20:49:34 +00:00
|
|
|
/**
|
|
|
|
* Handler for EmailUserComplete hook.
|
|
|
|
* @see https://www.mediawiki.org/wiki/Manual:Hooks/EmailUserComplete
|
|
|
|
* @param $address MailAddress Adress of receiving user
|
|
|
|
* @param $from MailAddress Adress of sending user
|
|
|
|
* @param $subject string Subject of the mail
|
|
|
|
* @param $text string Text of the mail
|
|
|
|
* @return bool true in all cases
|
|
|
|
*/
|
|
|
|
public static function onEmailUserComplete( $address, $from, $subject, $text ) {
|
2016-02-10 22:09:41 +00:00
|
|
|
global $wgContLang;
|
|
|
|
|
2015-09-19 20:49:34 +00:00
|
|
|
if ( $from->name === $address->name ) {
|
|
|
|
// nothing to notify
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
$userTo = User::newFromName( $address->name );
|
|
|
|
$userFrom = User::newFromName( $from->name );
|
|
|
|
|
2016-02-10 22:09:41 +00:00
|
|
|
$autoSubject = wfMessage( 'defemailsubject', $from->name )->inContentLanguage()->text();
|
|
|
|
if ( $subject === $autoSubject ) {
|
2016-02-23 21:26:55 +00:00
|
|
|
$autoFooter = "\n\n-- \n" . wfMessage( 'emailuserfooter', $from->name, $address->name )->inContentLanguage()->text();
|
|
|
|
$textWithoutFooter = preg_replace( '/' . preg_quote( $autoFooter, '/' ) . '$/', '', $text );
|
|
|
|
$preview = $wgContLang->truncate( $textWithoutFooter, 125 );
|
2016-02-10 22:09:41 +00:00
|
|
|
} else {
|
|
|
|
$preview = $subject;
|
|
|
|
}
|
|
|
|
|
2016-12-05 18:51:07 +00:00
|
|
|
EchoEvent::create( [
|
2015-09-19 20:49:34 +00:00
|
|
|
'type' => 'emailuser',
|
2016-12-05 18:51:07 +00:00
|
|
|
'extra' => [
|
2015-09-19 20:49:34 +00:00
|
|
|
'to-user-id' => $userTo->getId(),
|
2016-02-10 22:09:41 +00:00
|
|
|
'preview' => $preview,
|
2016-12-05 18:51:07 +00:00
|
|
|
],
|
2015-09-19 20:49:34 +00:00
|
|
|
'agent' => $userFrom,
|
2016-12-05 18:51:07 +00:00
|
|
|
] );
|
2015-09-19 20:49:34 +00:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-08-26 03:09:46 +00:00
|
|
|
/**
|
|
|
|
* For integration with the UserMerge extension.
|
|
|
|
*
|
|
|
|
* @param array $updateFields
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
public static function onUserMergeAccountFields( &$updateFields ) {
|
|
|
|
// array( tableName, idField, textField )
|
2014-10-24 19:16:37 +00:00
|
|
|
$dbw = MWEchoDbFactory::newFromDefault()->getEchoDb( DB_MASTER );
|
2016-12-05 18:51:07 +00:00
|
|
|
$updateFields[] = [ 'echo_event', 'event_agent_id', 'db' => $dbw ];
|
|
|
|
$updateFields[] = [ 'echo_notification', 'notification_user', 'db' => $dbw, 'options' => [ 'IGNORE' ] ];
|
|
|
|
$updateFields[] = [ 'echo_email_batch', 'eeb_user_id', 'db' => $dbw, 'options' => [ 'IGNORE' ] ];
|
2014-08-26 03:09:46 +00:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static function onMergeAccountFromTo( User &$oldUser, User &$newUser ) {
|
2016-12-08 20:50:03 +00:00
|
|
|
DeferredUpdates::addCallableUpdate( function () use ( $oldUser, $newUser ) {
|
|
|
|
MWEchoNotifUser::newFromUser( $oldUser )->resetNotificationCount( DB_MASTER );
|
|
|
|
if ( !$newUser->isAnon() ) {
|
|
|
|
MWEchoNotifUser::newFromUser( $newUser )->resetNotificationCount( DB_MASTER );
|
|
|
|
}
|
|
|
|
} );
|
2014-08-26 03:09:46 +00:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static function onUserMergeAccountDeleteTables( &$tables ) {
|
2014-10-24 19:16:37 +00:00
|
|
|
$dbw = MWEchoDbFactory::newFromDefault()->getEchoDb( DB_MASTER );
|
2016-12-05 18:51:07 +00:00
|
|
|
$tables['echo_notification'] = [ 'notification_user', 'db' => $dbw ];
|
|
|
|
$tables['echo_email_batch'] = [ 'eeb_user_id', 'db' => $dbw ];
|
2014-08-26 03:09:46 +00:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2015-12-08 18:54:44 +00:00
|
|
|
|
2017-06-22 08:03:10 +00:00
|
|
|
/**
|
|
|
|
* Sets custom login message for redirect from notification page
|
|
|
|
*
|
|
|
|
* @param array $messages
|
|
|
|
* @return bool
|
|
|
|
*/
|
2015-12-08 18:54:44 +00:00
|
|
|
public static function onLoginFormValidErrorMessages( &$messages ) {
|
|
|
|
$messages[] = 'echo-notification-loginrequired';
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-02-18 22:36:58 +00:00
|
|
|
public static function onResourceLoaderGetConfigVars( &$vars ) {
|
2016-05-20 10:09:51 +00:00
|
|
|
global $wgEchoFooterNoticeURL;
|
2016-03-18 00:00:05 +00:00
|
|
|
|
2016-03-09 04:50:31 +00:00
|
|
|
$vars['wgEchoMaxNotificationCount'] = MWEchoNotifUser::MAX_BADGE_COUNT;
|
2016-03-18 00:00:05 +00:00
|
|
|
$vars['wgEchoFooterNoticeURL'] = $wgEchoFooterNoticeURL;
|
|
|
|
|
2016-02-18 22:36:58 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-07-27 14:52:18 +00:00
|
|
|
public static function onArticleDeleteComplete( WikiPage &$article, User &$user, $reason, $articleId, Content $content = null, LogEntry $logEntry ) {
|
|
|
|
\DeferredUpdates::addCallableUpdate( function () use ( $articleId ) {
|
|
|
|
$eventMapper = new EchoEventMapper();
|
|
|
|
$eventIds = $eventMapper->fetchIdsByPage( $articleId );
|
|
|
|
EchoModerationController::moderate( $eventIds, true );
|
|
|
|
} );
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static function onArticleUndelete( Title $title, $create, $comment, $oldPageId ) {
|
|
|
|
if ( $create ) {
|
|
|
|
\DeferredUpdates::addCallableUpdate( function () use ( $oldPageId ) {
|
|
|
|
$eventMapper = new EchoEventMapper();
|
|
|
|
$eventIds = $eventMapper->fetchIdsByPage( $oldPageId );
|
|
|
|
EchoModerationController::moderate( $eventIds, false );
|
|
|
|
} );
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-07-18 16:28:41 +00:00
|
|
|
}
|