summaryrefslogtreecommitdiff
path: root/extensions/WikiEditor/WikiEditor.hooks.php
diff options
context:
space:
mode:
Diffstat (limited to 'extensions/WikiEditor/WikiEditor.hooks.php')
-rw-r--r--extensions/WikiEditor/WikiEditor.hooks.php320
1 files changed, 274 insertions, 46 deletions
diff --git a/extensions/WikiEditor/WikiEditor.hooks.php b/extensions/WikiEditor/WikiEditor.hooks.php
index 8c6800c1..6dfdb691 100644
--- a/extensions/WikiEditor/WikiEditor.hooks.php
+++ b/extensions/WikiEditor/WikiEditor.hooks.php
@@ -7,6 +7,9 @@
*/
class WikiEditorHooks {
+ // ID used for grouping entries all of a session's entries together in
+ // EventLogging.
+ private static $statsId = false;
/* Protected Static Members */
@@ -84,21 +87,6 @@ class WikiEditorHooks {
'ext.wikiEditor.preview',
),
),
- 'previewDialog' => array(
- 'preferences' => array(
- 'wikieditor-previewDialog' => array(
- 'type' => 'toggle',
- 'label-message' => 'wikieditor-previewDialog-preference',
- 'section' => 'editing/labs',
- ),
- ),
- 'requirements' => array(
- 'wikieditor-previewDialog' => true,
- ),
- 'modules' => array(
- 'ext.wikiEditor.previewDialog',
- ),
- ),
'publish' => array(
'preferences' => array(
'wikieditor-publish' => array(
@@ -119,6 +107,45 @@ class WikiEditorHooks {
/* Static Methods */
/**
+ * T99257: Extension registration does not properly support 2d arrays so set it as a global for now
+ */
+ public static function onRegistration() {
+ // Each module may be configured individually to be globally on/off or user preference based
+ $features = array(
+
+ /* Textarea / i-frame compatible (OK to deploy) */
+
+ 'toolbar' => array( 'global' => false, 'user' => true ),
+ // Provides interactive tools
+ 'dialogs' => array( 'global' => false, 'user' => true ),
+ // Hide signature button from main namespace
+ 'hidesig' => array( 'global' => true, 'user' => false ),
+
+ /* Textarea / i-frame compatible, but still experimental and unstable (do not deploy!) */
+
+ // Adds a tab for previewing in-line
+ 'preview' => array( 'global' => false, 'user' => true ),
+ // Adds a button and dialog for step-by-step publishing
+ 'publish' => array( 'global' => false, 'user' => true ),
+ );
+
+ // Eww, do a 2d array merge so we don't wipe out settings
+ global $wgWikiEditorFeatures;
+ if ( $wgWikiEditorFeatures ) {
+ foreach ( $features as $name => $settings ) {
+ if ( isset( $wgWikiEditorFeatures[$name] ) ) {
+ $wgWikiEditorFeatures[$name] += $settings;
+ } else {
+ $wgWikiEditorFeatures[$name] = $settings;
+ }
+ }
+ } else {
+ $wgWikiEditorFeatures = $features;
+ }
+
+ }
+
+ /**
* Checks if a certain option is enabled
*
* This method is public to allow other extensions that use WikiEditor to use the
@@ -134,7 +161,8 @@ class WikiEditorHooks {
if ( !isset( $wgWikiEditorFeatures[$name] ) || $wgWikiEditorFeatures[$name]['global'] ) {
return true;
}
- // Features with user preference control can have any number of preferences to be specific values to be enabled
+ // Features with user preference control can have any number of preferences
+ // to be specific values to be enabled
if ( $wgWikiEditorFeatures[$name]['user'] ) {
if ( isset( self::$features[$name]['requirements'] ) ) {
foreach ( self::$features[$name]['requirements'] as $requirement => $value ) {
@@ -146,17 +174,60 @@ class WikiEditorHooks {
}
return true;
}
- // Features controlled by $wgWikiEditorFeatures with both global and user set to false are awlways disabled
+ // Features controlled by $wgWikiEditorFeatures with both global and user
+ // set to false are always disabled
return false;
}
/**
+ * Log stuff to EventLogging's Schema:Edit - see https://meta.wikimedia.org/wiki/Schema:Edit
+ * 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.
+ */
+ public static function doEventLogging( $action, $article, $data = array() ) {
+ global $wgVersion;
+ if ( !class_exists( 'EventLogging' ) ) {
+ return false;
+ }
+
+ $user = $article->getContext()->getUser();
+ $page = $article->getPage();
+ $title = $article->getTitle();
+
+ $data = array(
+ 'action' => $action,
+ 'version' => 1,
+ 'editor' => 'wikitext',
+ 'platform' => 'desktop', // FIXME
+ 'integration' => 'page',
+ 'page.length' => -1, // FIXME
+ 'page.id' => $page->getId(),
+ 'page.title' => $title->getPrefixedText(),
+ 'page.ns' => $title->getNamespace(),
+ 'page.revid' => $page->getRevision() ? $page->getRevision()->getId() : 0,
+ 'user.id' => $user->getId(),
+ 'user.editCount' => $user->getEditCount() ?: 0,
+ 'mediawiki.version' => $wgVersion
+ ) + $data;
+
+ if ( $user->isAnon() ) {
+ $data['user.class'] = 'IP';
+ }
+
+ return EventLogging::logEvent( 'Edit', 11448630, $data );
+ }
+
+ /**
* EditPage::showEditForm:initial hook
*
* Adds the modules to the edit form
*
- * @param $editPage EditPage the current EditPage object.
- * @param $output OutputPage object.
+ * @param EditPage $editPage the current EditPage object.
+ * @param OutputPage $outputPage object.
* @return bool
*/
public static function editPageShowEditFormInitial( $editPage, $outputPage ) {
@@ -176,6 +247,66 @@ class WikiEditorHooks {
$outputPage->addModules( $feature['modules'] );
}
}
+
+ $article = $editPage->getArticle();
+ $request = $article->getContext()->getRequest();
+ // 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.
+ if ( class_exists( 'EventLogging' ) && !$request->wasPosted() ) {
+ $data = array();
+ $data['editingSessionId'] = self::getEditingStatsId();
+ if ( $request->getVal( 'section' ) ) {
+ $data['action.init.type'] = 'section';
+ } else {
+ $data['action.init.type'] = 'page';
+ }
+ if ( $request->getHeader( 'Referer' ) ) {
+ if ( $request->getVal( 'section' ) === 'new' || !$article->exists() ) {
+ $data['action.init.mechanism'] = 'new';
+ } else {
+ $data['action.init.mechanism'] = 'click';
+ }
+ } else {
+ $data['action.init.mechanism'] = 'url';
+ }
+
+ self::doEventLogging( 'init', $article, $data );
+ }
+
+ return true;
+ }
+
+ /**
+ * EditPage::showEditForm:fields hook
+ *
+ * Adds the event fields to the edit form
+ *
+ * @param EditPage $editPage the current EditPage object.
+ * @param OutputPage $outputPage object.
+ * @return bool
+ */
+ public static function editPageShowEditFormFields( $editPage, $outputPage ) {
+ if ( $editPage->contentModel !== CONTENT_MODEL_WIKITEXT ) {
+ return true;
+ }
+
+ $req = $outputPage->getContext()->getRequest();
+ $editingStatsId = $req->getVal( 'editingStatsId' );
+ if ( !$editingStatsId ) {
+ $editingStatsId = self::getEditingStatsId();
+ }
+ $outputPage->addHTML(
+ Xml::element(
+ 'input',
+ array(
+ 'type' => 'hidden',
+ 'name' => 'editingStatsId',
+ 'id' => 'editingStatsId',
+ 'value' => $editingStatsId
+ )
+ )
+ );
return true;
}
@@ -202,10 +333,10 @@ class WikiEditorHooks {
/**
* GetPreferences hook
*
- * Adds WikiEditor-releated items to the preferences
+ * Adds WikiEditor-related items to the preferences
*
- * @param $user User current user
- * @param $defaultPreferences array list of default user preference controls
+ * @param User $user current user
+ * @param array $defaultPreferences list of default user preference controls
* @return bool
*/
public static function getPreferences( $user, &$defaultPreferences ) {
@@ -225,36 +356,39 @@ class WikiEditorHooks {
}
/**
- * MakeGlobalVariablesScript hook
- *
- * Adds enabled/disabled switches for WikiEditor modules
* @param $vars array
* @return bool
*/
public static function resourceLoaderGetConfigVars( &$vars ) {
- global $wgWikiEditorFeatures;
-
- $configurations = array();
- foreach ( self::$features as $name => $feature ) {
- if (
- isset( $feature['configurations'] ) &&
- ( !isset( $wgWikiEditorFeatures[$name] ) || self::isEnabled( $name ) )
- ) {
- foreach ( $feature['configurations'] as $configuration ) {
- global $$configuration;
- $configurations[$configuration] = $$configuration;
- }
- }
- }
- if ( count( $configurations ) ) {
- $vars = array_merge( $vars, $configurations );
- }
- //expose magic words for use by the wikieditor toolbar
+ // expose magic words for use by the wikieditor toolbar
WikiEditorHooks::getMagicWords( $vars );
return true;
}
/**
+ * ResourceLoaderTestModules hook
+ *
+ * Registers JavaScript test modules
+ *
+ * @param $testModules array of javascript testing modules. 'qunit' is fed using
+ * tests/qunit/QUnitTestResources.php.
+ * @param $resourceLoader object
+ * @return bool
+ */
+ public static function resourceLoaderTestModules( &$testModules, &$resourceLoader ) {
+ $testModules['qunit']['ext.wikiEditor.toolbar.test'] = array(
+ 'scripts' => array( 'tests/qunit/ext.wikiEditor.toolbar.test.js' ),
+ 'dependencies' => array( 'ext.wikiEditor.toolbar' ),
+ 'localBasePath' => __DIR__,
+ 'remoteExtPath' => 'WikiEditor',
+ );
+ return true;
+ }
+
+ /**
+ * MakeGlobalVariablesScript hook
+ *
+ * Adds enabled/disabled switches for WikiEditor modules
* @param $vars array
* @return bool
*/
@@ -274,7 +408,7 @@ class WikiEditorHooks {
* @param $vars array
* @return bool
*/
- private static function getMagicWords( &$vars ){
+ private static function getMagicWords( &$vars ) {
$requiredMagicWords = array(
'redirect',
'img_right',
@@ -285,10 +419,104 @@ class WikiEditorHooks {
'img_framed',
'img_frameless',
);
+ $magicWords = array();
foreach ( $requiredMagicWords as $name ) {
- $magicWords[$name] = MagicWord::get( $name )->getSynonym( 0 );
- }
+ $magicWords[$name] = MagicWord::get( $name )->getSynonym( 0 );
+ }
$vars['wgWikiEditorMagicWords'] = $magicWords;
}
+
+ /**
+ * Adds WikiEditor JS to the output.
+ *
+ * This is attached to the MediaWiki 'BeforePageDisplay' hook.
+ *
+ * @param OutputPage $output
+ * @param Skin $skin
+ * @return boolean
+ */
+ public static function onBeforePageDisplay( OutputPage &$output, Skin &$skin ) {
+ $output->addModules( array( 'ext.wikiEditor.init' ) );
+ return true;
+ }
+
+ /**
+ * Gets a 32 character alphanumeric random string to be used for stats.
+ * @return string
+ */
+ private static function getEditingStatsId() {
+ if ( self::$statsId ) {
+ return self::$statsId;
+ }
+ return self::$statsId = MWCryptRand::generateHex( 32 );
+ }
+
+ /**
+ * This is attached to the MediaWiki 'EditPage::attemptSave' hook.
+ *
+ * @param EditPage $editPage
+ * @param Status $status
+ * @return boolean
+ */
+ public static function editPageAttemptSave( EditPage $editPage ) {
+ $article = $editPage->getArticle();
+ $request = $article->getContext()->getRequest();
+ if ( $request->getVal( 'editingStatsId' ) ) {
+ self::doEventLogging(
+ 'saveAttempt',
+ $article,
+ array( 'editingSessionId' => $request->getVal( 'editingStatsId' ) )
+ );
+ }
+
+ return true;
+ }
+
+ /**
+ * This is attached to the MediaWiki 'EditPage::attemptSave:after' hook.
+ *
+ * @param EditPage $editPage
+ * @param Status $status
+ * @return boolean
+ */
+ public static function editPageAttemptSaveAfter( EditPage $editPage, Status $status ) {
+ $article = $editPage->getArticle();
+ $request = $article->getContext()->getRequest();
+ if ( $request->getVal( 'editingStatsId' ) ) {
+ $data = array();
+ $data['editingSessionId'] = $request->getVal( 'editingStatsId' );
+
+ if ( $status->isOK() ) {
+ $action = 'saveSuccess';
+ } else {
+ $action = 'saveFailure';
+ $errors = $status->getErrorsArray();
+
+ if ( isset( $errors[0][0] ) ) {
+ $data['action.saveFailure.message'] = $errors[0][0];
+ }
+
+ if ( $status->value === EditPage::AS_CONFLICT_DETECTED ) {
+ $data['action.saveFailure.type'] = 'editConflict';
+ } elseif ( $status->value === EditPage::AS_ARTICLE_WAS_DELETED ) {
+ $data['action.saveFailure.type'] = 'editPageDeleted';
+ } elseif ( isset( $errors[0][0] ) && $errors[0][0] === 'abusefilter-disallowed' ) {
+ $data['action.saveFailure.type'] = 'extensionAbuseFilter';
+ } elseif ( isset( $editPage->getArticle()->getPage()->ConfirmEdit_ActivateCaptcha ) ) {
+ // TODO: :(
+ $data['action.saveFailure.type'] = 'extensionCaptcha';
+ } elseif ( isset( $errors[0][0] ) && $errors[0][0] === 'spamprotectiontext' ) {
+ $data['action.saveFailure.type'] = 'extensionSpamBlacklist';
+ } else {
+ // Catch everything else... We don't seem to get userBadToken or
+ // userNewUser through this hook.
+ $data['action.saveFailure.type'] = 'responseUnknown';
+ }
+ }
+ self::doEventLogging( $action, $article, $data );
+ }
+
+ return true;
+ }
}