2010-09-15 03:02:35 +00:00
|
|
|
<?php
|
2010-09-15 22:40:50 +00:00
|
|
|
/**
|
2010-10-01 18:15:10 +00:00
|
|
|
* Hooks for WikiEditor extension
|
2011-09-13 08:56:32 +00:00
|
|
|
*
|
2010-09-15 22:40:50 +00:00
|
|
|
* @file
|
|
|
|
* @ingroup Extensions
|
|
|
|
*/
|
|
|
|
|
2021-11-08 18:23:04 +00:00
|
|
|
namespace MediaWiki\Extension\WikiEditor;
|
|
|
|
|
|
|
|
use ApiMessage;
|
|
|
|
use Article;
|
|
|
|
use Content;
|
|
|
|
use ExtensionRegistry;
|
2021-11-08 13:07:57 +00:00
|
|
|
use MediaWiki\Cache\CacheKeyHelper;
|
2020-04-07 10:50:44 +00:00
|
|
|
use MediaWiki\ChangeTags\Hook\ChangeTagsListActiveHook;
|
|
|
|
use MediaWiki\ChangeTags\Hook\ListDefinedTagsHook;
|
2023-10-03 06:36:46 +00:00
|
|
|
use MediaWiki\Config\Config;
|
2023-05-06 21:21:49 +00:00
|
|
|
use MediaWiki\EditPage\EditPage;
|
2022-05-19 06:08:37 +00:00
|
|
|
use MediaWiki\Extension\DiscussionTools\Hooks as DiscussionToolsHooks;
|
2022-03-06 16:15:32 +00:00
|
|
|
use MediaWiki\Extension\EventLogging\EventLogging;
|
2021-11-04 19:42:41 +00:00
|
|
|
use MediaWiki\Hook\EditPage__attemptSave_afterHook;
|
|
|
|
use MediaWiki\Hook\EditPage__attemptSaveHook;
|
|
|
|
use MediaWiki\Hook\EditPage__showEditForm_fieldsHook;
|
|
|
|
use MediaWiki\Hook\EditPage__showEditForm_initialHook;
|
2021-11-04 07:58:20 +00:00
|
|
|
use MediaWiki\Hook\EditPageGetPreviewContentHook;
|
2020-04-07 10:50:44 +00:00
|
|
|
use MediaWiki\Hook\RecentChange_saveHook;
|
2023-10-03 06:36:46 +00:00
|
|
|
use MediaWiki\Html\Html;
|
2018-07-30 16:28:56 +00:00
|
|
|
use MediaWiki\MediaWikiServices;
|
2023-10-03 06:36:46 +00:00
|
|
|
use MediaWiki\Output\OutputPage;
|
2021-11-04 19:42:41 +00:00
|
|
|
use MediaWiki\Preferences\Hook\GetPreferencesHook;
|
2023-10-03 06:36:46 +00:00
|
|
|
use MediaWiki\Request\WebRequest;
|
2022-05-20 02:11:32 +00:00
|
|
|
use MediaWiki\ResourceLoader as RL;
|
2023-10-03 06:36:46 +00:00
|
|
|
use MediaWiki\Status\Status;
|
|
|
|
use MediaWiki\User\User;
|
2021-11-04 19:42:41 +00:00
|
|
|
use MediaWiki\User\UserEditTracker;
|
|
|
|
use MediaWiki\User\UserOptionsLookup;
|
2023-07-20 21:16:52 +00:00
|
|
|
use MediaWiki\WikiMap\WikiMap;
|
2021-11-08 18:23:04 +00:00
|
|
|
use MessageLocalizer;
|
|
|
|
use MWCryptRand;
|
2020-04-07 10:50:44 +00:00
|
|
|
use RecentChange;
|
|
|
|
use RequestContext;
|
2020-04-19 23:24:13 +00:00
|
|
|
use WikimediaEvents\WikimediaEventsHooks;
|
2018-07-30 16:28:56 +00:00
|
|
|
|
2021-11-04 19:42:41 +00:00
|
|
|
/**
|
|
|
|
* @phpcs:disable MediaWiki.NamingConventions.LowerCamelFunctionsName.FunctionName
|
|
|
|
*/
|
|
|
|
class Hooks implements
|
|
|
|
EditPage__showEditForm_initialHook,
|
|
|
|
EditPage__showEditForm_fieldsHook,
|
|
|
|
GetPreferencesHook,
|
|
|
|
EditPage__attemptSaveHook,
|
|
|
|
EditPage__attemptSave_afterHook,
|
2020-04-07 10:50:44 +00:00
|
|
|
EditPageGetPreviewContentHook,
|
|
|
|
ListDefinedTagsHook,
|
|
|
|
ChangeTagsListActiveHook,
|
|
|
|
RecentChange_saveHook
|
2021-11-04 19:42:41 +00:00
|
|
|
{
|
2021-11-02 05:48:27 +00:00
|
|
|
|
|
|
|
/** @var string|bool ID used for grouping entries all of a session's entries together in EventLogging. */
|
2015-02-05 16:47:49 +00:00
|
|
|
private static $statsId = false;
|
2011-09-13 08:56:32 +00:00
|
|
|
|
2020-04-07 10:50:44 +00:00
|
|
|
/** @var string[] */
|
|
|
|
private static $tags = [ 'wikieditor' ];
|
|
|
|
|
2021-11-04 19:42:41 +00:00
|
|
|
/** @var Config */
|
|
|
|
private $config;
|
|
|
|
|
|
|
|
/** @var UserEditTracker */
|
|
|
|
private $userEditTracker;
|
|
|
|
|
|
|
|
/** @var UserOptionsLookup */
|
|
|
|
private $userOptionsLookup;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param Config $config
|
|
|
|
* @param UserEditTracker $userEditTracker
|
|
|
|
* @param UserOptionsLookup $userOptionsLookup
|
|
|
|
*/
|
|
|
|
public function __construct(
|
|
|
|
Config $config,
|
|
|
|
UserEditTracker $userEditTracker,
|
|
|
|
UserOptionsLookup $userOptionsLookup
|
|
|
|
) {
|
|
|
|
$this->config = $config;
|
|
|
|
$this->userEditTracker = $userEditTracker;
|
|
|
|
$this->userOptionsLookup = $userOptionsLookup;
|
|
|
|
}
|
2011-09-13 08:56:32 +00:00
|
|
|
|
2021-04-30 22:45:29 +00:00
|
|
|
/**
|
|
|
|
* Should the current session be sampled for EventLogging?
|
|
|
|
*
|
|
|
|
* @param string $sessionId
|
|
|
|
* @return bool Whether to sample the session
|
|
|
|
*/
|
2021-11-04 19:42:41 +00:00
|
|
|
protected function inEventSample( $sessionId ) {
|
2021-04-30 22:45:29 +00:00
|
|
|
// Sample 6.25%
|
2021-11-04 19:42:41 +00:00
|
|
|
$samplingRate = $this->config->has( 'WMESchemaEditAttemptStepSamplingRate' ) ?
|
|
|
|
$this->config->get( 'WMESchemaEditAttemptStepSamplingRate' ) : 0.0625;
|
2022-08-09 21:39:53 +00:00
|
|
|
|
|
|
|
// (T314896) Convert whatever we've been given to a string of hex, as that's what EL needs
|
|
|
|
$hexValue = hash( 'md5', $sessionId, false );
|
|
|
|
|
2021-04-30 22:45:29 +00:00
|
|
|
$inSample = EventLogging::sessionInSample(
|
2022-08-09 21:39:53 +00:00
|
|
|
(int)( 1 / $samplingRate ), $hexValue
|
2021-04-30 22:45:29 +00:00
|
|
|
);
|
|
|
|
return $inSample;
|
|
|
|
}
|
|
|
|
|
2015-02-05 16:47:49 +00:00
|
|
|
/**
|
2023-03-23 17:18:20 +00:00
|
|
|
* Log stuff to the eventlogging_EditAttemptStep stream in a shape that conforms to the
|
|
|
|
* analytics/legacy/editattemptstep schema.
|
|
|
|
*
|
|
|
|
* If the EventLogging extension is not loaded, then this is a NOP.
|
|
|
|
*
|
|
|
|
* @see https://meta.wikimedia.org/wiki/Schema:EditAttemptStep
|
2015-02-05 16:47:49 +00:00
|
|
|
*
|
|
|
|
* @param string $action
|
|
|
|
* @param Article $article Which article (with full context, page, title, etc.)
|
|
|
|
* @param array $data Data to log for this action
|
2023-03-23 17:18:20 +00:00
|
|
|
* @return void
|
2015-02-05 16:47:49 +00:00
|
|
|
*/
|
2021-11-04 19:42:41 +00:00
|
|
|
public function doEventLogging( $action, $article, $data = [] ) {
|
2023-07-28 14:44:29 +00:00
|
|
|
if ( defined( 'MW_PHPUNIT_TEST' ) ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-10-17 05:43:12 +00:00
|
|
|
$extensionRegistry = ExtensionRegistry::getInstance();
|
2023-04-06 19:12:04 +00:00
|
|
|
if ( !$extensionRegistry->isLoaded( 'EventLogging' ) || !$extensionRegistry->isLoaded( 'WikimediaEvents' ) ) {
|
2023-03-23 17:18:20 +00:00
|
|
|
return;
|
2015-02-05 16:47:49 +00:00
|
|
|
}
|
2023-06-09 19:49:54 +00:00
|
|
|
if ( $extensionRegistry->isLoaded( 'MobileFrontend' ) ) {
|
|
|
|
$mobFrontContext = MediaWikiServices::getInstance()->getService( 'MobileFrontend.Context' );
|
|
|
|
if ( $mobFrontContext->shouldDisplayMobileView() ) {
|
|
|
|
// on a MobileFrontend page the logging should be handled by it
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2021-11-04 19:42:41 +00:00
|
|
|
$inSample = $this->inEventSample( $data['editing_session_id'] );
|
2023-04-06 19:12:04 +00:00
|
|
|
$shouldOversample = WikimediaEventsHooks::shouldSchemaEditAttemptStepOversample( $article->getContext() );
|
2015-02-05 16:47:49 +00:00
|
|
|
|
|
|
|
$user = $article->getContext()->getUser();
|
|
|
|
$page = $article->getPage();
|
|
|
|
$title = $article->getTitle();
|
2020-04-07 23:37:09 +00:00
|
|
|
$revisionRecord = $page->getRevisionRecord();
|
2023-07-20 21:16:52 +00:00
|
|
|
$skin = $article->getContext()->getSkin();
|
2015-02-05 16:47:49 +00:00
|
|
|
|
2016-05-08 22:57:53 +00:00
|
|
|
$data = [
|
2015-02-05 16:47:49 +00:00
|
|
|
'action' => $action,
|
|
|
|
'version' => 1,
|
2018-10-26 20:48:55 +00:00
|
|
|
'is_oversample' => !$inSample,
|
|
|
|
'editor_interface' => 'wikitext',
|
2021-11-02 05:48:27 +00:00
|
|
|
// @todo FIXME for other than 'desktop'. T249944
|
|
|
|
'platform' => 'desktop',
|
2015-02-05 16:47:49 +00:00
|
|
|
'integration' => 'page',
|
2018-10-26 20:48:55 +00:00
|
|
|
'page_id' => $page->getId(),
|
|
|
|
'page_title' => $title->getPrefixedText(),
|
|
|
|
'page_ns' => $title->getNamespace(),
|
2020-04-07 23:37:09 +00:00
|
|
|
'revision_id' => $revisionRecord ? $revisionRecord->getId() : 0,
|
2018-10-26 20:48:55 +00:00
|
|
|
'user_id' => $user->getId(),
|
2023-04-11 19:58:42 +00:00
|
|
|
'user_is_temp' => $user->isTemp(),
|
2021-11-04 19:42:41 +00:00
|
|
|
'user_editcount' => $this->userEditTracker->getUserEditCount( $user ) ?: 0,
|
2020-05-13 21:55:37 +00:00
|
|
|
'mw_version' => MW_VERSION,
|
2023-07-28 14:44:29 +00:00
|
|
|
'skin' => $skin ? $skin->getSkinName() : null,
|
2023-07-20 21:16:52 +00:00
|
|
|
'is_bot' => $user->isRegistered() && $user->isBot(),
|
|
|
|
'is_anon' => $user->isAnon(),
|
|
|
|
'wiki' => WikiMap::getCurrentWikiId(),
|
2016-05-08 22:57:53 +00:00
|
|
|
] + $data;
|
2015-02-05 16:47:49 +00:00
|
|
|
|
2022-05-19 06:08:37 +00:00
|
|
|
$bucket = ExtensionRegistry::getInstance()->isLoaded( 'DiscussionTools' ) ?
|
|
|
|
// @phan-suppress-next-line PhanUndeclaredClassMethod
|
|
|
|
DiscussionToolsHooks\HookUtils::determineUserABTestBucket( $user ) : false;
|
|
|
|
if ( $bucket ) {
|
|
|
|
$data['bucket'] = $bucket;
|
2021-01-29 23:25:19 +00:00
|
|
|
}
|
|
|
|
|
2015-02-05 16:47:49 +00:00
|
|
|
if ( $user->isAnon() ) {
|
2018-10-26 20:48:55 +00:00
|
|
|
$data['user_class'] = 'IP';
|
2015-02-05 16:47:49 +00:00
|
|
|
}
|
|
|
|
|
2023-03-23 17:18:20 +00:00
|
|
|
$this->doMetricsPlatformLogging( $action, $data );
|
|
|
|
|
|
|
|
if ( !$inSample && !$shouldOversample ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
EventLogging::submit(
|
|
|
|
'eventlogging_EditAttemptStep',
|
|
|
|
[
|
2023-10-30 19:39:10 +00:00
|
|
|
'$schema' => '/analytics/legacy/editattemptstep/2.0.2',
|
2023-03-23 17:18:20 +00:00
|
|
|
'event' => $data,
|
|
|
|
]
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @see https://phabricator.wikimedia.org/T309013
|
|
|
|
* @see https://phabricator.wikimedia.org/T309985
|
|
|
|
*/
|
|
|
|
private function doMetricsPlatformLogging( string $action, array $data ): void {
|
|
|
|
unset( $data['version'] );
|
|
|
|
unset( $data['action'] );
|
|
|
|
|
|
|
|
// Sampling rate (and therefore whether a stream should oversample) is captured in
|
|
|
|
// the stream config ($wgEventStreams).
|
|
|
|
unset( $data['is_oversample'] );
|
|
|
|
unset( $data['session_token'] );
|
|
|
|
|
|
|
|
// Platform can be derived from the agent_client_platform_family context attribute
|
|
|
|
// mixed in by the JavaScript Metrics Platform Client. The context attribute will be
|
|
|
|
// "desktop_browser" or "mobile_browser" depending on whether the MobileFrontend
|
|
|
|
// extension has signalled that it is enabled.
|
|
|
|
unset( $data['platform'] );
|
|
|
|
|
|
|
|
unset( $data['page_id'] );
|
|
|
|
unset( $data['page_title'] );
|
|
|
|
unset( $data['page_ns'] );
|
|
|
|
|
|
|
|
// If the revision ID can be fetched (i.e. it is a positive integer), then it will be
|
|
|
|
//mixed in by the Metrics Platform Client.
|
|
|
|
if ( $data['revision_id'] ) {
|
|
|
|
unset( $data['revision_id'] );
|
|
|
|
}
|
|
|
|
|
|
|
|
unset( $data['user_id'] );
|
|
|
|
unset( $data['user_editcount'] );
|
|
|
|
unset( $data['mw_version'] );
|
|
|
|
|
|
|
|
EventLogging::submitMetricsEvent( 'eas.wt.' . $action, $data );
|
2015-02-05 16:47:49 +00:00
|
|
|
}
|
|
|
|
|
2021-04-30 22:45:29 +00:00
|
|
|
/**
|
|
|
|
* Log stuff to EventLogging's Schema:VisualEditorFeatureUse -
|
|
|
|
* see https://meta.wikimedia.org/wiki/Schema:VisualEditorFeatureUse
|
2023-04-06 19:12:04 +00:00
|
|
|
* If you don't have EventLogging and WikimediaEvents installed, does nothing.
|
2021-04-30 22:45:29 +00:00
|
|
|
*
|
|
|
|
* @param string $feature
|
|
|
|
* @param string $action
|
|
|
|
* @param Article $article Which article (with full context, page, title, etc.)
|
|
|
|
* @param string $sessionId Session identifier
|
|
|
|
* @return bool Whether the event was logged or not.
|
|
|
|
*/
|
2021-11-04 19:42:41 +00:00
|
|
|
public function doVisualEditorFeatureUseLogging( $feature, $action, $article, $sessionId ) {
|
2021-04-30 22:45:29 +00:00
|
|
|
$extensionRegistry = ExtensionRegistry::getInstance();
|
2023-04-06 19:12:04 +00:00
|
|
|
if ( !$extensionRegistry->isLoaded( 'EventLogging' ) || !$extensionRegistry->isLoaded( 'WikimediaEvents' ) ) {
|
2021-04-30 22:45:29 +00:00
|
|
|
return false;
|
|
|
|
}
|
2021-11-04 19:42:41 +00:00
|
|
|
$inSample = $this->inEventSample( $sessionId );
|
2023-04-06 19:12:04 +00:00
|
|
|
$shouldOversample = WikimediaEventsHooks::shouldSchemaEditAttemptStepOversample( $article->getContext() );
|
2021-04-30 22:45:29 +00:00
|
|
|
if ( !$inSample && !$shouldOversample ) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
$user = $article->getContext()->getUser();
|
2021-11-04 19:42:41 +00:00
|
|
|
$editCount = $this->userEditTracker->getUserEditCount( $user );
|
2021-04-30 22:45:29 +00:00
|
|
|
$data = [
|
|
|
|
'feature' => $feature,
|
|
|
|
'action' => $action,
|
2021-05-12 16:45:38 +00:00
|
|
|
'editingSessionId' => $sessionId,
|
2021-11-02 05:48:27 +00:00
|
|
|
// @todo FIXME for other than 'desktop'. T249944
|
|
|
|
'platform' => 'desktop',
|
2021-04-30 22:45:29 +00:00
|
|
|
'integration' => 'page',
|
|
|
|
'editor_interface' => 'wikitext',
|
|
|
|
'user_id' => $user->getId(),
|
2023-04-11 19:58:42 +00:00
|
|
|
'user_is_temp' => $user->isTemp(),
|
2021-11-04 08:04:38 +00:00
|
|
|
'user_editcount' => $editCount ?: 0,
|
2021-04-30 22:45:29 +00:00
|
|
|
];
|
|
|
|
|
2022-05-19 06:08:37 +00:00
|
|
|
$bucket = ExtensionRegistry::getInstance()->isLoaded( 'DiscussionTools' ) ?
|
|
|
|
// @phan-suppress-next-line PhanUndeclaredClassMethod
|
|
|
|
DiscussionToolsHooks\HookUtils::determineUserABTestBucket( $user ) : false;
|
2021-11-04 08:04:38 +00:00
|
|
|
if ( $bucket ) {
|
|
|
|
$data['bucket'] = $bucket;
|
2021-04-30 22:45:29 +00:00
|
|
|
}
|
|
|
|
|
2022-09-23 13:00:39 +00:00
|
|
|
// NOTE: The 'VisualEditorFeatureUse' event was migrated to the Event Platform and is no
|
|
|
|
// longer using the legacy EventLogging schema from metawiki. $revId is actually
|
|
|
|
// overridden by the EventLoggingSchemas extension attribute in
|
|
|
|
// WikimediaEvents/extension.json.
|
|
|
|
return EventLogging::logEvent( 'VisualEditorFeatureUse', -1, $data );
|
2021-04-30 22:45:29 +00:00
|
|
|
}
|
|
|
|
|
2010-09-15 22:40:50 +00:00
|
|
|
/**
|
2010-09-16 21:11:46 +00:00
|
|
|
* EditPage::showEditForm:initial hook
|
2011-09-13 08:56:32 +00:00
|
|
|
*
|
2010-09-15 22:40:50 +00:00
|
|
|
* Adds the modules to the edit form
|
2011-09-13 08:56:32 +00:00
|
|
|
*
|
2014-09-22 02:50:01 +00:00
|
|
|
* @param EditPage $editPage the current EditPage object.
|
|
|
|
* @param OutputPage $outputPage object.
|
2010-09-15 22:40:50 +00:00
|
|
|
*/
|
2021-11-04 19:42:41 +00:00
|
|
|
public function onEditPage__showEditForm_initial( $editPage, $outputPage ) {
|
2014-06-22 14:30:30 +00:00
|
|
|
if ( $editPage->contentModel !== CONTENT_MODEL_WIKITEXT ) {
|
2019-02-02 20:17:15 +00:00
|
|
|
return;
|
2014-06-22 14:30:30 +00:00
|
|
|
}
|
2011-09-13 08:56:32 +00:00
|
|
|
|
2015-02-05 16:47:49 +00:00
|
|
|
$article = $editPage->getArticle();
|
|
|
|
$request = $article->getContext()->getRequest();
|
2017-11-22 19:59:00 +00:00
|
|
|
|
|
|
|
// Add modules if enabled
|
|
|
|
$user = $article->getContext()->getUser();
|
2021-11-04 19:42:41 +00:00
|
|
|
if ( $this->userOptionsLookup->getBoolOption( $user, 'usebetatoolbar' ) ) {
|
2017-11-22 19:59:00 +00:00
|
|
|
$outputPage->addModuleStyles( 'ext.wikiEditor.styles' );
|
|
|
|
$outputPage->addModules( 'ext.wikiEditor' );
|
2023-03-17 07:22:32 +00:00
|
|
|
if ( $this->config->get( 'WikiEditorRealtimePreview' ) ) {
|
2021-11-18 10:50:17 +00:00
|
|
|
$outputPage->addModules( 'ext.wikiEditor.realtimepreview' );
|
|
|
|
}
|
2017-11-22 19:59:00 +00:00
|
|
|
}
|
|
|
|
|
2015-02-05 16:47:49 +00:00
|
|
|
// Don't run this if the request was posted - we don't want to log 'init' when the
|
|
|
|
// user just pressed 'Show preview' or 'Show changes', or switched from VE keeping
|
|
|
|
// changes.
|
2018-08-19 15:17:03 +00:00
|
|
|
if ( ExtensionRegistry::getInstance()->isLoaded( 'EventLogging' ) && !$request->wasPosted() ) {
|
2016-05-08 22:57:53 +00:00
|
|
|
$data = [];
|
2019-11-13 19:45:40 +00:00
|
|
|
$data['editing_session_id'] = self::getEditingStatsId( $request );
|
2021-11-05 12:28:26 +00:00
|
|
|
$section = $request->getRawVal( 'section' );
|
|
|
|
if ( $section !== null ) {
|
2018-10-26 20:48:55 +00:00
|
|
|
$data['init_type'] = 'section';
|
2015-02-05 16:47:49 +00:00
|
|
|
} else {
|
2018-10-26 20:48:55 +00:00
|
|
|
$data['init_type'] = 'page';
|
2015-02-05 16:47:49 +00:00
|
|
|
}
|
|
|
|
if ( $request->getHeader( 'Referer' ) ) {
|
2020-04-28 15:58:14 +00:00
|
|
|
if (
|
2021-11-05 12:28:26 +00:00
|
|
|
$section === 'new'
|
2020-04-28 15:58:14 +00:00
|
|
|
|| !$article->getPage()->exists()
|
|
|
|
) {
|
2018-10-26 20:48:55 +00:00
|
|
|
$data['init_mechanism'] = 'new';
|
2015-02-05 16:47:49 +00:00
|
|
|
} else {
|
2018-10-26 20:48:55 +00:00
|
|
|
$data['init_mechanism'] = 'click';
|
2015-02-05 16:47:49 +00:00
|
|
|
}
|
|
|
|
} else {
|
2021-01-30 01:58:17 +00:00
|
|
|
if (
|
2021-11-05 12:28:26 +00:00
|
|
|
$section === 'new'
|
2021-01-30 01:58:17 +00:00
|
|
|
|| !$article->getPage()->exists()
|
|
|
|
) {
|
|
|
|
$data['init_mechanism'] = 'url-new';
|
|
|
|
} else {
|
|
|
|
$data['init_mechanism'] = 'url';
|
|
|
|
}
|
2015-02-05 16:47:49 +00:00
|
|
|
}
|
2022-06-15 07:13:36 +00:00
|
|
|
if ( $request->getRawVal( 'wvprov' ) === 'sticky-header' ) {
|
|
|
|
$data['init_mechanism'] .= '-sticky-header';
|
|
|
|
}
|
2015-02-05 16:47:49 +00:00
|
|
|
|
2021-11-04 19:42:41 +00:00
|
|
|
$this->doEventLogging( 'init', $article, $data );
|
2015-02-05 16:47:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-04 19:42:41 +00:00
|
|
|
/**
|
|
|
|
* Deprecated static alias for onEditPage__showEditForm_initial
|
|
|
|
*
|
|
|
|
* Adds the modules to the edit form
|
|
|
|
*
|
|
|
|
* @deprecated since 1.38
|
|
|
|
* @param EditPage $editPage the current EditPage object.
|
|
|
|
* @param OutputPage $outputPage object.
|
|
|
|
*/
|
|
|
|
public static function editPageShowEditFormInitial( EditPage $editPage, OutputPage $outputPage ) {
|
2023-10-02 20:07:53 +00:00
|
|
|
wfDeprecated( __METHOD__, '1.38' );
|
2021-11-04 19:42:41 +00:00
|
|
|
$services = MediaWikiServices::getInstance();
|
|
|
|
( new self(
|
|
|
|
$services->getMainConfig(),
|
|
|
|
$services->getUserEditTracker(),
|
|
|
|
$services->getUserOptionsLookup()
|
|
|
|
) )->onEditPage__showEditForm_initial( $editPage, $outputPage );
|
|
|
|
}
|
|
|
|
|
2015-02-05 16:47:49 +00:00
|
|
|
/**
|
|
|
|
* EditPage::showEditForm:fields hook
|
|
|
|
*
|
|
|
|
* Adds the event fields to the edit form
|
|
|
|
*
|
|
|
|
* @param EditPage $editPage the current EditPage object.
|
|
|
|
* @param OutputPage $outputPage object.
|
|
|
|
*/
|
2021-11-04 19:42:41 +00:00
|
|
|
public function onEditPage__showEditForm_fields( $editPage, $outputPage ) {
|
2020-04-07 10:50:44 +00:00
|
|
|
$outputPage->addHTML(
|
|
|
|
Html::hidden(
|
|
|
|
'wikieditorUsed',
|
|
|
|
'',
|
|
|
|
[ 'id' => 'wikieditorUsed' ]
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
2021-11-03 05:40:19 +00:00
|
|
|
if ( $editPage->contentModel !== CONTENT_MODEL_WIKITEXT
|
|
|
|
|| !ExtensionRegistry::getInstance()->isLoaded( 'EventLogging' ) ) {
|
2019-02-02 20:17:15 +00:00
|
|
|
return;
|
2015-02-05 16:47:49 +00:00
|
|
|
}
|
|
|
|
|
2017-09-01 21:15:26 +00:00
|
|
|
$req = $outputPage->getRequest();
|
2019-11-13 19:45:40 +00:00
|
|
|
$editingStatsId = self::getEditingStatsId( $req );
|
|
|
|
|
|
|
|
$shouldOversample = ExtensionRegistry::getInstance()->isLoaded( 'WikimediaEvents' ) &&
|
|
|
|
WikimediaEventsHooks::shouldSchemaEditAttemptStepOversample( $outputPage->getContext() );
|
2015-03-23 21:32:28 +00:00
|
|
|
|
2015-02-05 16:47:49 +00:00
|
|
|
$outputPage->addHTML(
|
2021-11-05 18:12:38 +00:00
|
|
|
Html::hidden(
|
|
|
|
'editingStatsId',
|
|
|
|
$editingStatsId,
|
|
|
|
[ 'id' => 'editingStatsId' ]
|
2015-02-05 16:47:49 +00:00
|
|
|
)
|
|
|
|
);
|
2019-11-13 19:45:40 +00:00
|
|
|
|
|
|
|
if ( $shouldOversample ) {
|
|
|
|
$outputPage->addHTML(
|
2021-11-05 18:12:38 +00:00
|
|
|
Html::hidden(
|
|
|
|
'editingStatsOversample',
|
|
|
|
1,
|
|
|
|
[ 'id' => 'editingStatsOversample' ]
|
2019-11-13 19:45:40 +00:00
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
2010-09-15 22:40:50 +00:00
|
|
|
}
|
2011-09-13 08:56:32 +00:00
|
|
|
|
2010-09-15 22:40:50 +00:00
|
|
|
/**
|
|
|
|
* GetPreferences hook
|
2011-09-13 08:56:32 +00:00
|
|
|
*
|
2014-09-22 02:50:01 +00:00
|
|
|
* Adds WikiEditor-related items to the preferences
|
2011-09-13 08:56:32 +00:00
|
|
|
*
|
2014-09-22 02:50:01 +00:00
|
|
|
* @param User $user current user
|
2017-07-26 09:15:17 +00:00
|
|
|
* @param array &$defaultPreferences list of default user preference controls
|
2010-09-15 22:40:50 +00:00
|
|
|
*/
|
2021-11-04 19:42:41 +00:00
|
|
|
public function onGetPreferences( $user, &$defaultPreferences ) {
|
2017-11-22 19:59:00 +00:00
|
|
|
// Ideally this key would be 'wikieditor-toolbar'
|
|
|
|
$defaultPreferences['usebetatoolbar'] = [
|
|
|
|
'type' => 'toggle',
|
|
|
|
'label-message' => 'wikieditor-toolbar-preference',
|
2018-10-25 21:29:54 +00:00
|
|
|
'help-message' => 'wikieditor-toolbar-preference-help',
|
2017-11-22 19:59:00 +00:00
|
|
|
'section' => 'editing/editor',
|
|
|
|
];
|
2022-03-31 05:03:18 +00:00
|
|
|
$defaultPreferences['wikieditor-realtimepreview'] = [
|
|
|
|
'type' => 'api',
|
|
|
|
];
|
2010-09-15 22:40:50 +00:00
|
|
|
}
|
2011-09-13 08:56:32 +00:00
|
|
|
|
2010-09-15 22:40:50 +00:00
|
|
|
/**
|
2022-05-20 02:11:32 +00:00
|
|
|
* @param RL\Context $context
|
2019-07-20 08:09:04 +00:00
|
|
|
* @param Config $config
|
|
|
|
* @return array
|
2010-09-15 22:40:50 +00:00
|
|
|
*/
|
2022-05-20 02:11:32 +00:00
|
|
|
public static function getModuleData( RL\Context $context, Config $config ) {
|
2019-07-20 08:09:04 +00:00
|
|
|
return [
|
|
|
|
// expose magic words for use by the wikieditor toolbar
|
|
|
|
'magicWords' => self::getMagicWords(),
|
2022-03-18 18:08:32 +00:00
|
|
|
'signature' => self::getSignatureMessage( $context ),
|
|
|
|
'realtimeDebounce' => $config->get( 'WikiEditorRealtimePreviewDebounce' ),
|
2022-03-25 00:19:30 +00:00
|
|
|
'realtimeDisableDuration' => $config->get( 'WikiEditorRealtimeDisableDuration' ),
|
2019-07-20 08:09:04 +00:00
|
|
|
];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2022-05-20 02:11:32 +00:00
|
|
|
* @param RL\Context $context
|
2019-07-20 08:09:04 +00:00
|
|
|
* @param Config $config
|
|
|
|
* @return array
|
|
|
|
*/
|
2022-05-20 02:11:32 +00:00
|
|
|
public static function getModuleDataSummary( RL\Context $context, Config $config ) {
|
2019-07-20 08:09:04 +00:00
|
|
|
return [
|
|
|
|
'magicWords' => self::getMagicWords(),
|
2022-03-30 20:54:15 +00:00
|
|
|
'signature' => self::getSignatureMessage( $context, true ),
|
|
|
|
'realtimeDebounce' => $config->get( 'WikiEditorRealtimePreviewDebounce' ),
|
|
|
|
'realtimeDisableDuration' => $config->get( 'WikiEditorRealtimeDisableDuration' ),
|
2019-07-20 08:09:04 +00:00
|
|
|
];
|
|
|
|
}
|
2015-08-05 03:59:00 +00:00
|
|
|
|
2021-11-02 05:48:27 +00:00
|
|
|
/**
|
|
|
|
* @param MessageLocalizer $ml
|
|
|
|
* @param bool $raw
|
|
|
|
* @return string
|
|
|
|
*/
|
2019-07-20 08:09:04 +00:00
|
|
|
private static function getSignatureMessage( MessageLocalizer $ml, $raw = false ) {
|
|
|
|
$msg = $ml->msg( 'sig-text' )->params( '~~~~' )->inContentLanguage();
|
|
|
|
return $raw ? $msg->plain() : $msg->text();
|
2010-09-15 22:40:50 +00:00
|
|
|
}
|
2011-09-13 08:56:32 +00:00
|
|
|
|
2012-05-27 19:14:34 +00:00
|
|
|
/**
|
|
|
|
* Expose useful magic words which are used by the wikieditor toolbar
|
2021-01-22 20:36:31 +00:00
|
|
|
* @return string[][]
|
2012-05-27 19:14:34 +00:00
|
|
|
*/
|
2019-07-20 08:09:04 +00:00
|
|
|
private static function getMagicWords() {
|
2016-05-08 22:57:53 +00:00
|
|
|
$requiredMagicWords = [
|
2012-05-27 19:14:34 +00:00
|
|
|
'redirect',
|
2020-10-18 07:23:19 +00:00
|
|
|
'img_alt',
|
2012-05-27 19:14:34 +00:00
|
|
|
'img_right',
|
|
|
|
'img_left',
|
|
|
|
'img_none',
|
|
|
|
'img_center',
|
|
|
|
'img_thumbnail',
|
|
|
|
'img_framed',
|
|
|
|
'img_frameless',
|
2016-05-08 22:57:53 +00:00
|
|
|
];
|
|
|
|
$magicWords = [];
|
2020-01-09 22:26:25 +00:00
|
|
|
$factory = MediaWikiServices::getInstance()->getMagicWordFactory();
|
2012-05-27 19:14:34 +00:00
|
|
|
foreach ( $requiredMagicWords as $name ) {
|
2021-01-22 20:36:31 +00:00
|
|
|
$magicWords[$name] = $factory->get( $name )->getSynonyms();
|
2014-09-22 02:50:01 +00:00
|
|
|
}
|
2019-07-20 08:09:04 +00:00
|
|
|
return $magicWords;
|
2012-05-27 19:14:34 +00:00
|
|
|
}
|
|
|
|
|
2015-02-05 16:47:49 +00:00
|
|
|
/**
|
|
|
|
* Gets a 32 character alphanumeric random string to be used for stats.
|
2019-11-13 19:45:40 +00:00
|
|
|
* @param WebRequest $request
|
2015-02-05 16:47:49 +00:00
|
|
|
* @return string
|
|
|
|
*/
|
2019-11-13 19:45:40 +00:00
|
|
|
private static function getEditingStatsId( WebRequest $request ) {
|
2021-11-05 12:28:26 +00:00
|
|
|
$fromRequest = $request->getRawVal( 'editingStatsId' );
|
|
|
|
if ( $fromRequest !== null ) {
|
2019-11-13 19:45:40 +00:00
|
|
|
return $fromRequest;
|
|
|
|
}
|
2018-02-06 20:16:40 +00:00
|
|
|
if ( !self::$statsId ) {
|
|
|
|
self::$statsId = MWCryptRand::generateHex( 32 );
|
2015-02-05 16:47:49 +00:00
|
|
|
}
|
2018-02-06 20:16:40 +00:00
|
|
|
return self::$statsId;
|
2015-02-05 16:47:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This is attached to the MediaWiki 'EditPage::attemptSave' hook.
|
|
|
|
*
|
|
|
|
* @param EditPage $editPage
|
|
|
|
*/
|
2021-11-04 19:42:41 +00:00
|
|
|
public function onEditPage__attemptSave( $editPage ) {
|
2015-02-05 16:47:49 +00:00
|
|
|
$article = $editPage->getArticle();
|
|
|
|
$request = $article->getContext()->getRequest();
|
2021-11-05 12:28:26 +00:00
|
|
|
$statsId = $request->getRawVal( 'editingStatsId' );
|
|
|
|
if ( $statsId !== null ) {
|
2021-11-04 19:42:41 +00:00
|
|
|
$this->doEventLogging(
|
2015-02-05 16:47:49 +00:00
|
|
|
'saveAttempt',
|
|
|
|
$article,
|
2021-11-05 12:28:26 +00:00
|
|
|
[ 'editing_session_id' => $statsId ]
|
2015-02-05 16:47:49 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This is attached to the MediaWiki 'EditPage::attemptSave:after' hook.
|
|
|
|
*
|
|
|
|
* @param EditPage $editPage
|
|
|
|
* @param Status $status
|
2021-11-04 19:42:41 +00:00
|
|
|
* @param array $resultDetails
|
2015-02-05 16:47:49 +00:00
|
|
|
*/
|
2021-11-04 19:42:41 +00:00
|
|
|
public function onEditPage__attemptSave_after( $editPage, $status, $resultDetails ) {
|
2015-02-05 16:47:49 +00:00
|
|
|
$article = $editPage->getArticle();
|
|
|
|
$request = $article->getContext()->getRequest();
|
2021-11-05 12:28:26 +00:00
|
|
|
$statsId = $request->getRawVal( 'editingStatsId' );
|
|
|
|
if ( $statsId !== null ) {
|
2016-05-08 22:57:53 +00:00
|
|
|
$data = [];
|
2021-11-05 12:28:26 +00:00
|
|
|
$data['editing_session_id'] = $statsId;
|
2015-02-05 16:47:49 +00:00
|
|
|
|
|
|
|
if ( $status->isOK() ) {
|
|
|
|
$action = 'saveSuccess';
|
2021-04-30 22:45:29 +00:00
|
|
|
|
2020-04-07 10:50:44 +00:00
|
|
|
if ( $request->getRawVal( 'wikieditorUsed' ) === 'yes' ) {
|
2021-11-04 19:42:41 +00:00
|
|
|
$this->doVisualEditorFeatureUseLogging(
|
2021-11-05 12:28:26 +00:00
|
|
|
'mwSave', 'source-has-js', $article, $statsId
|
2021-04-30 22:45:29 +00:00
|
|
|
);
|
|
|
|
}
|
2015-02-05 16:47:49 +00:00
|
|
|
} else {
|
|
|
|
$action = 'saveFailure';
|
|
|
|
|
2021-01-15 19:17:25 +00:00
|
|
|
// Compare to ve.init.mw.ArticleTargetEvents.js in VisualEditor.
|
|
|
|
$typeMap = [
|
|
|
|
'badtoken' => 'userBadToken',
|
|
|
|
'assertanonfailed' => 'userNewUser',
|
|
|
|
'assertuserfailed' => 'userNewUser',
|
|
|
|
'assertnameduserfailed' => 'userNewUser',
|
|
|
|
'abusefilter-disallowed' => 'extensionAbuseFilter',
|
|
|
|
'abusefilter-warning' => 'extensionAbuseFilter',
|
|
|
|
'captcha' => 'extensionCaptcha',
|
|
|
|
'spamblacklist' => 'extensionSpamBlacklist',
|
|
|
|
'titleblacklist-forbidden' => 'extensionTitleBlacklist',
|
|
|
|
'pagedeleted' => 'editPageDeleted',
|
|
|
|
'editconflict' => 'editConflict'
|
|
|
|
];
|
|
|
|
|
|
|
|
$errors = $status->getErrorsArray();
|
|
|
|
// Replicate how the API generates error codes, in order to log data that is consistent with
|
|
|
|
// all other tools (which save changes via the API)
|
|
|
|
if ( isset( $errors[0] ) ) {
|
|
|
|
$code = ApiMessage::create( $errors[0] )->getApiCode();
|
|
|
|
} else {
|
|
|
|
$code = 'unknown';
|
2015-02-05 16:47:49 +00:00
|
|
|
}
|
|
|
|
|
2020-04-28 15:58:14 +00:00
|
|
|
$wikiPage = $editPage->getArticle()->getPage();
|
2021-11-08 13:07:57 +00:00
|
|
|
|
|
|
|
if ( ExtensionRegistry::getInstance()->isLoaded( 'ConfirmEdit' ) ) {
|
|
|
|
$key = CacheKeyHelper::getKeyForPage( $wikiPage );
|
|
|
|
/** @var SimpleCaptcha $captcha */
|
2021-11-23 11:09:44 +00:00
|
|
|
$captcha = \ConfirmEditHooks::getInstance();
|
2021-11-08 13:07:57 +00:00
|
|
|
$activatedCaptchas = $captcha->getActivatedCaptchas();
|
|
|
|
if ( isset( $activatedCaptchas[$key] ) ) {
|
|
|
|
// TODO: :(
|
|
|
|
$code = 'captcha';
|
|
|
|
}
|
2015-02-05 16:47:49 +00:00
|
|
|
}
|
2021-01-15 19:17:25 +00:00
|
|
|
|
|
|
|
$data['save_failure_message'] = $code;
|
|
|
|
$data['save_failure_type'] = $typeMap[ $code ] ?? 'responseUnknown';
|
2015-02-05 16:47:49 +00:00
|
|
|
}
|
2021-01-15 19:17:25 +00:00
|
|
|
|
2021-11-04 19:42:41 +00:00
|
|
|
$this->doEventLogging( $action, $article, $data );
|
2015-02-05 16:47:49 +00:00
|
|
|
}
|
|
|
|
}
|
2021-11-04 07:58:20 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Log a 'preview-nonlive' action when a page is previewed via the non-ajax full-page preview.
|
|
|
|
*
|
|
|
|
* @param EditPage $editPage
|
|
|
|
* @param Content &$content Content object to be previewed (may be replaced by hook function)
|
|
|
|
* @return bool|void True or no return value to continue or false to abort
|
|
|
|
*/
|
|
|
|
public function onEditPageGetPreviewContent( $editPage, &$content ) {
|
|
|
|
// This hook is only called for non-live previews, so we don't need to check the uselivepreview user option.
|
|
|
|
$editingStatsId = $editPage->getContext()->getRequest()->getRawVal( 'editingStatsId' );
|
|
|
|
if ( $editingStatsId !== null ) {
|
|
|
|
$article = $editPage->getArticle();
|
2021-11-04 19:42:41 +00:00
|
|
|
$this->doVisualEditorFeatureUseLogging( 'preview', 'preview-nonlive', $article, $editingStatsId );
|
2021-11-04 07:58:20 +00:00
|
|
|
}
|
|
|
|
}
|
2020-04-07 10:50:44 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @param string[] &$tags
|
|
|
|
* @return bool|void
|
|
|
|
*/
|
|
|
|
public function onChangeTagsListActive( &$tags ) {
|
|
|
|
$this->registerTags( $tags );
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param string[] &$tags
|
|
|
|
* @return bool|void
|
|
|
|
*/
|
|
|
|
public function onListDefinedTags( &$tags ) {
|
|
|
|
$this->registerTags( $tags );
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param string[] &$tags
|
|
|
|
*/
|
|
|
|
protected function registerTags( &$tags ) {
|
|
|
|
$tags = array_merge( $tags, static::$tags );
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param RecentChange $recentChange
|
|
|
|
* @return bool|void
|
|
|
|
*/
|
|
|
|
public function onRecentChange_save( $recentChange ) {
|
|
|
|
$request = RequestContext::getMain()->getRequest();
|
|
|
|
if ( $request->getRawVal( 'wikieditorUsed' ) === 'yes' ) {
|
|
|
|
$recentChange->addTags( 'wikieditor' );
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
2010-11-19 13:59:34 +00:00
|
|
|
}
|