2011-04-23 11:44:47 +00:00
|
|
|
<?php
|
|
|
|
|
2016-04-25 20:58:18 +00:00
|
|
|
use MediaWiki\Auth\AuthManager;
|
|
|
|
|
2011-04-23 11:44:47 +00:00
|
|
|
class ConfirmEditHooks {
|
2016-04-25 20:58:18 +00:00
|
|
|
protected static $instanceCreated = false;
|
|
|
|
|
2011-04-24 11:41:49 +00:00
|
|
|
/**
|
|
|
|
* Get the global Captcha instance
|
|
|
|
*
|
2014-02-18 02:13:12 +00:00
|
|
|
* @return SimpleCaptcha
|
2011-04-24 11:41:49 +00:00
|
|
|
*/
|
2016-04-25 20:58:18 +00:00
|
|
|
public static function getInstance() {
|
2011-04-23 11:44:47 +00:00
|
|
|
global $wgCaptcha, $wgCaptchaClass;
|
2012-01-12 08:58:40 +00:00
|
|
|
|
2016-04-25 20:58:18 +00:00
|
|
|
if ( !static::$instanceCreated ) {
|
|
|
|
static::$instanceCreated = true;
|
2011-04-23 11:44:47 +00:00
|
|
|
$wgCaptcha = new $wgCaptchaClass;
|
|
|
|
}
|
2012-01-12 08:58:40 +00:00
|
|
|
|
2011-04-23 11:44:47 +00:00
|
|
|
return $wgCaptcha;
|
|
|
|
}
|
|
|
|
|
2016-04-25 20:58:18 +00:00
|
|
|
/**
|
|
|
|
* Registers conditional hooks.
|
|
|
|
*/
|
|
|
|
public static function onRegistration() {
|
2016-05-03 16:42:00 +00:00
|
|
|
global $wgDisableAuthManager, $wgAuthManagerAutoConfig;
|
2016-04-25 20:58:18 +00:00
|
|
|
|
2016-05-03 16:42:00 +00:00
|
|
|
if ( class_exists( AuthManager::class ) && !$wgDisableAuthManager ) {
|
2016-04-25 20:58:18 +00:00
|
|
|
$wgAuthManagerAutoConfig['preauth'][CaptchaPreAuthenticationProvider::class] = [
|
|
|
|
'class' => CaptchaPreAuthenticationProvider::class,
|
|
|
|
'sort'=> 10, // run after preauth providers not requiring user input
|
|
|
|
];
|
|
|
|
Hooks::register( 'AuthChangeFormFields', 'ConfirmEditHooks::onAuthChangeFormFields' );
|
|
|
|
} else {
|
|
|
|
Hooks::register( 'UserCreateForm', 'ConfirmEditHooks::injectUserCreate' );
|
|
|
|
Hooks::register( 'AbortNewAccount', 'ConfirmEditHooks::confirmUserCreate' );
|
|
|
|
Hooks::register( 'LoginAuthenticateAudit', 'ConfirmEditHooks::triggerUserLogin' );
|
|
|
|
Hooks::register( 'UserLoginForm', 'ConfirmEditHooks::injectUserLogin' );
|
|
|
|
Hooks::register( 'AbortLogin', 'ConfirmEditHooks::confirmUserLogin' );
|
|
|
|
Hooks::register( 'AddNewAccountApiForm', 'ConfirmEditHooks::addNewAccountApiForm' );
|
|
|
|
Hooks::register( 'AddNewAccountApiResult', 'ConfirmEditHooks::addNewAccountApiResult' );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Use the shared parse on API edit
ConfirmEdit was tripling the API save time, because it was parsing the
entire content twice to evaluate whether the addurl trigger is hit.
While I was here, I stopped using the deprecated non-Content hooks. The
new hook, EditEditFilterMergedContent, does not pass an EditPage object,
which means that Title or WikiPage objects need to be passed around
instead. Also, since EditPage::showEditForm() cannot be called with no
EditPage object, use a EditPage::showEditForm:fields hook instead.
If non-wikitext content is edited, assume that the regex trigger is not
hit.
For further architectural details, see the associated core change:
I4b4270dd868a . MW_EDITFILTERMERGED_SUPPORTS_API is a constant
introduced to detect the presence of the associated core change.
Also, in APIGetAllowedParams, set the allowed parameters even if we are
not on the help screen. This allows API users to submit their CAPTCHA
answer without it failing with an "unrecognized parameter" error.
Compatibility with MediaWiki 1.21 is retained, compatibility before that
is dropped.
Change-Id: I9529b7e8d3fc9301c754b28fda185aa3ab36f13e
2014-12-05 04:48:13 +00:00
|
|
|
static function confirmEditMerged( $context, $content, $status, $summary, $user, $minorEdit ) {
|
|
|
|
return self::getInstance()->confirmEditMerged( $context, $content, $status, $summary,
|
|
|
|
$user, $minorEdit );
|
2011-04-23 11:44:47 +00:00
|
|
|
}
|
|
|
|
|
2016-03-07 16:29:53 +00:00
|
|
|
/**
|
|
|
|
* PageContentSaveComplete hook handler.
|
|
|
|
* Clear IP whitelist cache on page saves for [[MediaWiki:captcha-ip-whitelist]].
|
|
|
|
*
|
|
|
|
* @param Page $wikiPage
|
|
|
|
* @param User $user
|
|
|
|
* @param Content $content
|
|
|
|
* @param string $summary
|
|
|
|
* @param bool $isMinor
|
|
|
|
* @param bool $isWatch
|
|
|
|
* @param string $section
|
|
|
|
* @param int $flags
|
|
|
|
* @param int $revision
|
|
|
|
* @param Status $status
|
|
|
|
* @param int $baseRevId
|
|
|
|
*
|
|
|
|
* @return bool true
|
|
|
|
*/
|
|
|
|
static function onPageContentSaveComplete( Page $wikiPage, User $user, Content $content, $summary,
|
|
|
|
$isMinor, $isWatch, $section, $flags, $revision, Status $status, $baseRevId
|
|
|
|
) {
|
|
|
|
$title = $wikiPage->getTitle();
|
|
|
|
if ( $title->getText() === 'Captcha-ip-whitelist' && $title->getNamespace() === NS_MEDIAWIKI ) {
|
|
|
|
$cache = ObjectCache::getMainWANInstance();
|
|
|
|
$cache->delete( $cache->makeKey( 'confirmedit', 'ipwhitelist' ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-05-30 14:33:05 +00:00
|
|
|
static function confirmEditPage( $editpage, $buttons, $tabindex ) {
|
|
|
|
self::getInstance()->editShowCaptcha( $editpage );
|
|
|
|
}
|
|
|
|
|
Use the shared parse on API edit
ConfirmEdit was tripling the API save time, because it was parsing the
entire content twice to evaluate whether the addurl trigger is hit.
While I was here, I stopped using the deprecated non-Content hooks. The
new hook, EditEditFilterMergedContent, does not pass an EditPage object,
which means that Title or WikiPage objects need to be passed around
instead. Also, since EditPage::showEditForm() cannot be called with no
EditPage object, use a EditPage::showEditForm:fields hook instead.
If non-wikitext content is edited, assume that the regex trigger is not
hit.
For further architectural details, see the associated core change:
I4b4270dd868a . MW_EDITFILTERMERGED_SUPPORTS_API is a constant
introduced to detect the presence of the associated core change.
Also, in APIGetAllowedParams, set the allowed parameters even if we are
not on the help screen. This allows API users to submit their CAPTCHA
answer without it failing with an "unrecognized parameter" error.
Compatibility with MediaWiki 1.21 is retained, compatibility before that
is dropped.
Change-Id: I9529b7e8d3fc9301c754b28fda185aa3ab36f13e
2014-12-05 04:48:13 +00:00
|
|
|
static function showEditFormFields( &$editPage, &$out ) {
|
|
|
|
return self::getInstance()->showEditFormFields( $editPage, $out );
|
|
|
|
}
|
2015-05-21 16:08:17 +00:00
|
|
|
|
2014-01-10 23:16:34 +00:00
|
|
|
static function addNewAccountApiForm( $apiModule, $loginForm ) {
|
|
|
|
return self::getInstance()->addNewAccountApiForm( $apiModule, $loginForm );
|
|
|
|
}
|
2014-05-30 14:33:05 +00:00
|
|
|
|
2014-01-10 23:16:34 +00:00
|
|
|
static function addNewAccountApiResult( $apiModule, $loginPage, &$result ) {
|
|
|
|
return self::getInstance()->addNewAccountApiResult( $apiModule, $loginPage, $result );
|
|
|
|
}
|
2011-04-23 11:44:47 +00:00
|
|
|
|
|
|
|
static function injectUserCreate( &$template ) {
|
|
|
|
return self::getInstance()->injectUserCreate( $template );
|
|
|
|
}
|
|
|
|
|
2014-01-17 20:56:56 +00:00
|
|
|
static function confirmUserCreate( $u, &$message, &$status = null ) {
|
|
|
|
return self::getInstance()->confirmUserCreate( $u, $message, $status );
|
2011-04-23 11:44:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static function triggerUserLogin( $user, $password, $retval ) {
|
|
|
|
return self::getInstance()->triggerUserLogin( $user, $password, $retval );
|
|
|
|
}
|
|
|
|
|
|
|
|
static function injectUserLogin( &$template ) {
|
|
|
|
return self::getInstance()->injectUserLogin( $template );
|
|
|
|
}
|
|
|
|
|
|
|
|
static function confirmUserLogin( $u, $pass, &$retval ) {
|
|
|
|
return self::getInstance()->confirmUserLogin( $u, $pass, $retval );
|
|
|
|
}
|
|
|
|
|
|
|
|
static function injectEmailUser( &$form ) {
|
|
|
|
return self::getInstance()->injectEmailUser( $form );
|
|
|
|
}
|
|
|
|
|
|
|
|
static function confirmEmailUser( $from, $to, $subject, $text, &$error ) {
|
|
|
|
return self::getInstance()->confirmEmailUser( $from, $to, $subject, $text, $error );
|
|
|
|
}
|
2011-11-23 19:09:57 +00:00
|
|
|
|
2013-03-16 15:42:51 +00:00
|
|
|
// Default $flags to 1 for backwards-compatible behavior
|
|
|
|
public static function APIGetAllowedParams( &$module, &$params, $flags = 1 ) {
|
|
|
|
return self::getInstance()->APIGetAllowedParams( $module, $params, $flags );
|
2011-11-23 19:09:57 +00:00
|
|
|
}
|
|
|
|
|
2016-04-25 20:58:18 +00:00
|
|
|
public static function onAuthChangeFormFields(
|
|
|
|
array $requests, array $fieldInfo, array &$formDescriptor, $action
|
|
|
|
) {
|
|
|
|
self::getInstance()->onAuthChangeFormFields( $requests, $fieldInfo, $formDescriptor, $action );
|
|
|
|
}
|
|
|
|
|
2015-05-21 15:41:49 +00:00
|
|
|
/**
|
|
|
|
* Set up $wgWhitelistRead
|
|
|
|
*/
|
2015-05-21 16:08:17 +00:00
|
|
|
public static function confirmEditSetup() {
|
2016-08-18 18:08:51 +00:00
|
|
|
global $wgCaptchaTriggers, $wgWikimediaJenkinsCI;
|
2012-01-12 08:58:40 +00:00
|
|
|
|
2015-05-21 15:41:49 +00:00
|
|
|
// There is no need to run (core) tests with enabled ConfirmEdit - bug T44145
|
|
|
|
if ( isset( $wgWikimediaJenkinsCI ) && $wgWikimediaJenkinsCI === true ) {
|
2016-04-25 20:58:18 +00:00
|
|
|
$wgCaptchaTriggers = array_fill_keys( array_keys( $wgCaptchaTriggers ), false );
|
2015-05-21 15:41:49 +00:00
|
|
|
}
|
2016-08-18 18:08:51 +00:00
|
|
|
}
|
2012-01-12 08:58:40 +00:00
|
|
|
|
2016-08-18 18:08:51 +00:00
|
|
|
/**
|
|
|
|
* TitleReadWhitelist hook handler.
|
|
|
|
*
|
|
|
|
* @param Title $title
|
|
|
|
* @param User $user
|
|
|
|
* @param $whitelisted
|
|
|
|
*/
|
|
|
|
public static function onTitleReadWhitelist( Title $title, User $user, &$whitelisted ) {
|
|
|
|
$image = SpecialPage::getTitleFor( 'Captcha', 'image' );
|
|
|
|
$help = SpecialPage::getTitleFor( 'Captcha', 'help' );
|
|
|
|
if ( $title->equals( $image ) || $title->equals( $help ) ) {
|
|
|
|
$whitelisted = true;
|
2011-04-23 11:44:47 +00:00
|
|
|
}
|
|
|
|
}
|
2016-08-18 18:08:51 +00:00
|
|
|
|
2015-05-23 08:46:40 +00:00
|
|
|
/**
|
2016-08-18 18:08:51 +00:00
|
|
|
*
|
2015-05-23 08:46:40 +00:00
|
|
|
* Callback for extension.json of FancyCaptcha to set a default captcha directory,
|
|
|
|
* which depends on wgUploadDirectory
|
|
|
|
*/
|
|
|
|
public static function onFancyCaptchaSetup() {
|
|
|
|
global $wgCaptchaDirectory, $wgUploadDirectory;
|
|
|
|
if ( !$wgCaptchaDirectory ) {
|
|
|
|
$wgCaptchaDirectory = "$wgUploadDirectory/captcha";
|
|
|
|
}
|
|
|
|
}
|
2015-05-29 14:02:54 +00:00
|
|
|
|
2015-05-23 09:32:16 +00:00
|
|
|
/**
|
|
|
|
* Callback for extension.json of ReCaptcha to require the recaptcha library php file.
|
|
|
|
* FIXME: This should be done in a better way, e.g. only load the libraray, if really needed.
|
|
|
|
*/
|
|
|
|
public static function onReCaptchaSetup() {
|
2015-10-28 15:52:04 +00:00
|
|
|
require_once ( __DIR__ . '/../ReCaptcha/recaptchalib.php' );
|
2015-05-29 14:02:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Extension function, moved from ReCaptcha.php when that was decimated.
|
|
|
|
* Make sure the keys are defined.
|
|
|
|
*/
|
|
|
|
public static function efReCaptcha() {
|
|
|
|
global $wgReCaptchaPublicKey, $wgReCaptchaPrivateKey;
|
2015-10-28 15:52:04 +00:00
|
|
|
// @codingStandardsIgnoreStart
|
2015-05-29 14:02:54 +00:00
|
|
|
global $recaptcha_public_key, $recaptcha_private_key;
|
2015-10-28 15:52:04 +00:00
|
|
|
// @codingStandardsIgnoreEnd
|
2015-05-29 14:02:54 +00:00
|
|
|
global $wgServerName;
|
|
|
|
|
|
|
|
// Backwards compatibility
|
|
|
|
if ( $wgReCaptchaPublicKey == '' ) {
|
|
|
|
$wgReCaptchaPublicKey = $recaptcha_public_key;
|
|
|
|
}
|
|
|
|
if ( $wgReCaptchaPrivateKey == '' ) {
|
|
|
|
$wgReCaptchaPrivateKey = $recaptcha_private_key;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( $wgReCaptchaPublicKey == '' || $wgReCaptchaPrivateKey == '' ) {
|
2015-10-28 15:52:04 +00:00
|
|
|
die (
|
|
|
|
'You need to set $wgReCaptchaPrivateKey and $wgReCaptchaPublicKey in LocalSettings.php to ' .
|
2015-05-29 14:02:54 +00:00
|
|
|
"use the reCAPTCHA plugin. You can sign up for a key <a href='" .
|
2015-10-28 15:52:04 +00:00
|
|
|
htmlentities( recaptcha_get_signup_url( $wgServerName, "mediawiki" ) ) . "'>here</a>." );
|
2015-05-29 14:02:54 +00:00
|
|
|
}
|
2015-05-23 09:32:16 +00:00
|
|
|
}
|
2016-03-12 21:35:42 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* AlternateEditPreview hook handler.
|
|
|
|
*
|
|
|
|
* Replaces the preview with a check of all lines for the [[MediaWiki:Captcha-ip-whitelist]]
|
|
|
|
* interface message, if it validates as an IP address.
|
|
|
|
*
|
|
|
|
* @param EditPage $editor
|
|
|
|
* @param Content &$content
|
|
|
|
* @param string &$html
|
|
|
|
* @param ParserOutput &$po
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
public static function onAlternateEditPreview( EditPage $editor, &$content, &$html, &$po ) {
|
|
|
|
$title = $editor->getTitle();
|
|
|
|
$exceptionTitle = Title::makeTitle( NS_MEDIAWIKI, 'Captcha-ip-whitelist' );
|
|
|
|
|
|
|
|
if ( !$title->equals( $exceptionTitle ) ) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
$ctx = $editor->getArticle()->getContext();
|
|
|
|
$out = $ctx->getOutput();
|
|
|
|
$lang = $ctx->getLanguage();
|
|
|
|
|
|
|
|
$lines = explode( "\n", $content->getNativeData() );
|
|
|
|
$html .= Html::rawElement(
|
|
|
|
'div',
|
|
|
|
[ 'class' => 'warningbox' ],
|
|
|
|
$ctx->msg( 'confirmedit-preview-description' )->parse()
|
|
|
|
) .
|
|
|
|
Html::openElement(
|
|
|
|
'table',
|
|
|
|
[ 'class' => 'wikitable sortable' ]
|
|
|
|
) .
|
|
|
|
Html::openElement( 'thead' ) .
|
|
|
|
Html::element( 'th', [], $ctx->msg( 'confirmedit-preview-line' )->text() ) .
|
|
|
|
Html::element( 'th', [], $ctx->msg( 'confirmedit-preview-content' )->text() ) .
|
|
|
|
Html::element( 'th', [], $ctx->msg( 'confirmedit-preview-validity' )->text() ) .
|
|
|
|
Html::closeElement( 'thead' );
|
|
|
|
|
|
|
|
foreach ( $lines as $count => $line ) {
|
|
|
|
$ip = trim( $line );
|
|
|
|
if ( $ip === '' || strpos( $ip, '#' ) !== false ) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if ( IP::isIPAddress( $ip ) ) {
|
|
|
|
$validity = $ctx->msg( 'confirmedit-preview-valid' )->escaped();
|
|
|
|
$css = 'valid';
|
|
|
|
} else {
|
|
|
|
$validity = $ctx->msg( 'confirmedit-preview-invalid' )->escaped();
|
|
|
|
$css = 'notvalid';
|
|
|
|
}
|
|
|
|
$html .= Html::openElement( 'tr' ) .
|
|
|
|
Html::element(
|
|
|
|
'td',
|
|
|
|
[],
|
|
|
|
$lang->formatNum( $count + 1 )
|
|
|
|
) .
|
|
|
|
Html::element(
|
|
|
|
'td',
|
|
|
|
[],
|
|
|
|
// IPv6 max length: 8 groups * 4 digits + 7 delimiter = 39
|
|
|
|
// + 11 chars for safety
|
|
|
|
$lang->truncate( $ip, 50 )
|
|
|
|
) .
|
|
|
|
Html::rawElement(
|
|
|
|
'td',
|
|
|
|
// possible values:
|
|
|
|
// mw-confirmedit-ip-valid
|
|
|
|
// mw-confirmedit-ip-notvalid
|
|
|
|
[ 'class' => 'mw-confirmedit-ip-' . $css ],
|
|
|
|
$validity
|
|
|
|
) .
|
|
|
|
Html::closeElement( 'tr' );
|
|
|
|
}
|
|
|
|
$html .= Html::closeElement( 'table' );
|
|
|
|
$out->addModuleStyles( 'ext.confirmEdit.editPreview.ipwhitelist.styles' );
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
2011-04-23 11:44:47 +00:00
|
|
|
}
|