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
|
|
|
|
*/
|
|
|
|
|
2018-07-30 16:28:56 +00:00
|
|
|
use MediaWiki\MediaWikiServices;
|
|
|
|
|
2010-09-15 22:40:50 +00:00
|
|
|
class WikiEditorHooks {
|
2015-02-05 16:47:49 +00:00
|
|
|
// ID used for grouping entries all of a session's entries together in
|
|
|
|
// EventLogging.
|
|
|
|
private static $statsId = false;
|
2011-09-13 08:56:32 +00:00
|
|
|
|
2011-02-25 00:01:57 +00:00
|
|
|
/* Static Methods */
|
2011-09-13 08:56:32 +00:00
|
|
|
|
2015-02-05 16:47:49 +00:00
|
|
|
/**
|
2018-10-26 20:48:55 +00:00
|
|
|
* Log stuff to EventLogging's Schema:EditAttemptStep -
|
|
|
|
* see https://meta.wikimedia.org/wiki/Schema:EditAttemptStep
|
2015-02-05 16:47:49 +00:00
|
|
|
* If you don't have EventLogging installed, does nothing.
|
|
|
|
*
|
|
|
|
* @param string $action
|
|
|
|
* @param Article $article Which article (with full context, page, title, etc.)
|
|
|
|
* @param array $data Data to log for this action
|
|
|
|
* @return bool Whether the event was logged or not.
|
|
|
|
*/
|
2016-05-08 22:57:53 +00:00
|
|
|
public static function doEventLogging( $action, $article, $data = [] ) {
|
2018-10-26 20:48:55 +00:00
|
|
|
global $wgVersion, $wgWMESchemaEditAttemptStepSamplingRate;
|
2018-10-17 05:43:12 +00:00
|
|
|
$extensionRegistry = ExtensionRegistry::getInstance();
|
|
|
|
if ( !$extensionRegistry->isLoaded( 'EventLogging' ) ) {
|
2015-02-05 16:47:49 +00:00
|
|
|
return false;
|
|
|
|
}
|
2018-10-15 22:53:59 +00:00
|
|
|
// Sample 6.25%
|
2018-10-26 20:48:55 +00:00
|
|
|
$samplingRate = $wgWMESchemaEditAttemptStepSamplingRate ?? 0.0625;
|
2019-11-05 19:08:41 +00:00
|
|
|
$inSample = EventLogging::sessionInSample(
|
|
|
|
(int)( 1 / $samplingRate ), $data['editing_session_id']
|
|
|
|
);
|
2018-10-17 05:43:12 +00:00
|
|
|
$shouldOversample = $extensionRegistry->isLoaded( 'WikimediaEvents' ) &&
|
2018-10-26 20:48:55 +00:00
|
|
|
WikimediaEventsHooks::shouldSchemaEditAttemptStepOversample( $article->getContext() );
|
2018-10-17 05:43:12 +00:00
|
|
|
if ( !$inSample && !$shouldOversample ) {
|
2015-03-23 21:32:28 +00:00
|
|
|
return false;
|
|
|
|
}
|
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();
|
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',
|
2015-02-05 16:47:49 +00:00
|
|
|
'platform' => 'desktop', // FIXME
|
|
|
|
'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(),
|
|
|
|
'user_editcount' => $user->getEditCount() ?: 0,
|
|
|
|
'mw_version' => $wgVersion,
|
2016-05-08 22:57:53 +00:00
|
|
|
] + $data;
|
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
|
|
|
}
|
|
|
|
|
2018-10-26 20:48:55 +00:00
|
|
|
return EventLogging::logEvent( 'EditAttemptStep', 18530416, $data );
|
2015-02-05 16:47:49 +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
|
|
|
*/
|
2017-09-01 21:15:26 +00:00
|
|
|
public static function editPageShowEditFormInitial( EditPage $editPage, OutputPage $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();
|
|
|
|
if ( $user->getOption( 'usebetatoolbar' ) ) {
|
|
|
|
$outputPage->addModuleStyles( 'ext.wikiEditor.styles' );
|
|
|
|
$outputPage->addModules( 'ext.wikiEditor' );
|
|
|
|
}
|
|
|
|
|
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 );
|
2015-03-18 19:40:30 +00:00
|
|
|
if ( $request->getVal( 'section' ) ) {
|
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' ) ) {
|
|
|
|
if ( $request->getVal( 'section' ) === 'new' || !$article->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 {
|
2018-10-26 20:48:55 +00:00
|
|
|
$data['init_mechanism'] = 'url';
|
2015-02-05 16:47:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
self::doEventLogging( 'init', $article, $data );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* EditPage::showEditForm:fields hook
|
|
|
|
*
|
|
|
|
* Adds the event fields to the edit form
|
|
|
|
*
|
|
|
|
* @param EditPage $editPage the current EditPage object.
|
|
|
|
* @param OutputPage $outputPage object.
|
|
|
|
*/
|
2017-09-01 21:15:26 +00:00
|
|
|
public static function editPageShowEditFormFields( EditPage $editPage, OutputPage $outputPage ) {
|
2015-02-05 16:47:49 +00:00
|
|
|
if ( $editPage->contentModel !== CONTENT_MODEL_WIKITEXT ) {
|
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(
|
|
|
|
Xml::element(
|
|
|
|
'input',
|
2016-05-08 22:57:53 +00:00
|
|
|
[
|
2015-02-05 16:47:49 +00:00
|
|
|
'type' => 'hidden',
|
|
|
|
'name' => 'editingStatsId',
|
|
|
|
'id' => 'editingStatsId',
|
|
|
|
'value' => $editingStatsId
|
2016-05-08 22:57:53 +00:00
|
|
|
]
|
2015-02-05 16:47:49 +00:00
|
|
|
)
|
|
|
|
);
|
2019-11-13 19:45:40 +00:00
|
|
|
|
|
|
|
if ( $shouldOversample ) {
|
|
|
|
$outputPage->addHTML(
|
|
|
|
Xml::element(
|
|
|
|
'input',
|
|
|
|
[
|
|
|
|
'type' => 'hidden',
|
|
|
|
'name' => 'editingStatsOversample',
|
|
|
|
'id' => 'editingStatsOversample',
|
|
|
|
'value' => 1
|
|
|
|
]
|
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
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
|
|
|
*/
|
|
|
|
public static function getPreferences( $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',
|
|
|
|
];
|
2010-09-15 22:40:50 +00:00
|
|
|
}
|
2011-09-13 08:56:32 +00:00
|
|
|
|
2010-09-15 22:40:50 +00:00
|
|
|
/**
|
2019-07-20 08:09:04 +00:00
|
|
|
* @param ResourceLoaderContext $context
|
|
|
|
* @param Config $config
|
|
|
|
* @return array
|
2010-09-15 22:40:50 +00:00
|
|
|
*/
|
2019-07-20 08:09:04 +00:00
|
|
|
public static function getModuleData( ResourceLoaderContext $context, Config $config ) {
|
|
|
|
return [
|
|
|
|
// expose magic words for use by the wikieditor toolbar
|
|
|
|
'magicWords' => self::getMagicWords(),
|
|
|
|
'signature' => self::getSignatureMessage( $context )
|
|
|
|
];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param ResourceLoaderContext $context
|
|
|
|
* @param Config $config
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
public static function getModuleDataSummary( ResourceLoaderContext $context, Config $config ) {
|
|
|
|
return [
|
|
|
|
'magicWords' => self::getMagicWords(),
|
|
|
|
'signature' => self::getSignatureMessage( $context, true )
|
|
|
|
];
|
|
|
|
}
|
2015-08-05 03:59:00 +00:00
|
|
|
|
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
|
2020-02-29 21:18:42 +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',
|
|
|
|
'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 ) {
|
2020-01-09 22:26:25 +00:00
|
|
|
$magicWords[$name] = $factory->get( $name )->getSynonym( 0 );
|
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 ) {
|
|
|
|
$fromRequest = $request->getVal( 'editingStatsId' );
|
|
|
|
if ( $fromRequest ) {
|
|
|
|
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
|
|
|
|
*/
|
|
|
|
public static function editPageAttemptSave( EditPage $editPage ) {
|
|
|
|
$article = $editPage->getArticle();
|
|
|
|
$request = $article->getContext()->getRequest();
|
2015-03-18 19:40:30 +00:00
|
|
|
if ( $request->getVal( 'editingStatsId' ) ) {
|
2015-02-05 16:47:49 +00:00
|
|
|
self::doEventLogging(
|
|
|
|
'saveAttempt',
|
|
|
|
$article,
|
2018-10-26 20:48:55 +00:00
|
|
|
[ 'editing_session_id' => $request->getVal( 'editingStatsId' ) ]
|
2015-02-05 16:47:49 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This is attached to the MediaWiki 'EditPage::attemptSave:after' hook.
|
|
|
|
*
|
|
|
|
* @param EditPage $editPage
|
|
|
|
* @param Status $status
|
|
|
|
*/
|
|
|
|
public static function editPageAttemptSaveAfter( EditPage $editPage, Status $status ) {
|
|
|
|
$article = $editPage->getArticle();
|
|
|
|
$request = $article->getContext()->getRequest();
|
2015-03-18 19:40:30 +00:00
|
|
|
if ( $request->getVal( 'editingStatsId' ) ) {
|
2016-05-08 22:57:53 +00:00
|
|
|
$data = [];
|
2018-10-26 20:48:55 +00:00
|
|
|
$data['editing_session_id'] = $request->getVal( 'editingStatsId' );
|
2015-02-05 16:47:49 +00:00
|
|
|
|
|
|
|
if ( $status->isOK() ) {
|
|
|
|
$action = 'saveSuccess';
|
|
|
|
} else {
|
|
|
|
$action = 'saveFailure';
|
|
|
|
$errors = $status->getErrorsArray();
|
|
|
|
|
|
|
|
if ( isset( $errors[0][0] ) ) {
|
2018-10-26 20:48:55 +00:00
|
|
|
$data['save_failure_message'] = $errors[0][0];
|
2015-02-05 16:47:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if ( $status->value === EditPage::AS_CONFLICT_DETECTED ) {
|
2018-10-26 20:48:55 +00:00
|
|
|
$data['save_failure_type'] = 'editConflict';
|
2015-03-14 15:49:04 +00:00
|
|
|
} elseif ( $status->value === EditPage::AS_ARTICLE_WAS_DELETED ) {
|
2018-10-26 20:48:55 +00:00
|
|
|
$data['save_failure_type'] = 'editPageDeleted';
|
2015-03-14 15:49:04 +00:00
|
|
|
} elseif ( isset( $errors[0][0] ) && $errors[0][0] === 'abusefilter-disallowed' ) {
|
2018-10-26 20:48:55 +00:00
|
|
|
$data['save_failure_type'] = 'extensionAbuseFilter';
|
2015-03-14 15:49:04 +00:00
|
|
|
} elseif ( isset( $editPage->getArticle()->getPage()->ConfirmEdit_ActivateCaptcha ) ) {
|
2015-02-05 16:47:49 +00:00
|
|
|
// TODO: :(
|
2018-10-26 20:48:55 +00:00
|
|
|
$data['save_failure_type'] = 'extensionCaptcha';
|
2019-09-12 22:47:51 +00:00
|
|
|
} elseif ( isset( $errors[0][0] ) && $errors[0][0] === 'spam-blacklisted-link' ) {
|
2018-10-26 20:48:55 +00:00
|
|
|
$data['save_failure_type'] = 'extensionSpamBlacklist';
|
2015-02-05 16:47:49 +00:00
|
|
|
} else {
|
2015-03-14 15:49:04 +00:00
|
|
|
// Catch everything else... We don't seem to get userBadToken or
|
|
|
|
// userNewUser through this hook.
|
2018-10-26 20:48:55 +00:00
|
|
|
$data['save_failure_type'] = 'responseUnknown';
|
2015-02-05 16:47:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
self::doEventLogging( $action, $article, $data );
|
|
|
|
}
|
|
|
|
}
|
2010-11-19 13:59:34 +00:00
|
|
|
}
|