2011-04-23 11:44:47 +00:00
|
|
|
<?php
|
|
|
|
|
2019-07-03 12:43:23 +00:00
|
|
|
use MediaWiki\MediaWikiServices;
|
2020-06-23 19:34:17 +00:00
|
|
|
use MediaWiki\Revision\RevisionRecord;
|
|
|
|
use MediaWiki\Storage\EditResult;
|
|
|
|
use MediaWiki\User\UserIdentity;
|
2020-01-25 05:38:50 +00:00
|
|
|
use Wikimedia\IPUtils;
|
2019-07-03 12:43:23 +00:00
|
|
|
|
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;
|
2020-02-03 14:36:31 +00:00
|
|
|
$class = $wgCaptchaClass ?: SimpleCaptcha::class;
|
2017-08-14 15:39:20 +00:00
|
|
|
$wgCaptcha = new $class;
|
2011-04-23 11:44:47 +00:00
|
|
|
}
|
2012-01-12 08:58:40 +00:00
|
|
|
|
2011-04-23 11:44:47 +00:00
|
|
|
return $wgCaptcha;
|
|
|
|
}
|
|
|
|
|
2018-06-15 21:08:14 +00:00
|
|
|
/**
|
|
|
|
* @param RequestContext $context
|
|
|
|
* @param Content $content
|
|
|
|
* @param Status $status
|
|
|
|
* @param string $summary
|
|
|
|
* @param User $user
|
|
|
|
* @param bool $minorEdit
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
public static function confirmEditMerged( $context, $content, $status, $summary, $user,
|
|
|
|
$minorEdit
|
|
|
|
) {
|
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
|
|
|
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
|
|
|
/**
|
2020-06-23 19:34:17 +00:00
|
|
|
* PageSaveComplete hook handler.
|
2017-04-29 01:57:52 +00:00
|
|
|
* Clear IP whitelist cache on page saves for [[MediaWiki:Captcha-ip-whitelist]].
|
2016-03-07 16:29:53 +00:00
|
|
|
*
|
2017-02-17 13:09:34 +00:00
|
|
|
* @param WikiPage $wikiPage
|
2020-06-23 19:34:17 +00:00
|
|
|
* @param UserIdentity $userIdentity
|
2017-09-01 04:47:36 +00:00
|
|
|
* @param string $summary
|
|
|
|
* @param int $flags
|
2020-06-23 19:34:17 +00:00
|
|
|
* @param RevisionRecord $revisionRecord
|
|
|
|
* @param EditResult $editResult
|
2016-03-07 16:29:53 +00:00
|
|
|
*
|
|
|
|
* @return bool true
|
|
|
|
*/
|
2020-06-23 19:34:17 +00:00
|
|
|
public static function onPageSaveComplete(
|
|
|
|
WikiPage $wikiPage,
|
|
|
|
UserIdentity $userIdentity,
|
|
|
|
string $summary,
|
|
|
|
int $flags,
|
|
|
|
RevisionRecord $revisionRecord,
|
|
|
|
EditResult $editResult
|
2016-03-07 16:29:53 +00:00
|
|
|
) {
|
|
|
|
$title = $wikiPage->getTitle();
|
|
|
|
if ( $title->getText() === 'Captcha-ip-whitelist' && $title->getNamespace() === NS_MEDIAWIKI ) {
|
2019-07-03 12:43:23 +00:00
|
|
|
$cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
|
2016-03-07 16:29:53 +00:00
|
|
|
$cache->delete( $cache->makeKey( 'confirmedit', 'ipwhitelist' ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-06-15 21:08:14 +00:00
|
|
|
/**
|
|
|
|
* @param EditPage $editpage
|
|
|
|
*/
|
|
|
|
public static function confirmEditPage( EditPage $editpage ) {
|
2014-05-30 14:33:05 +00:00
|
|
|
self::getInstance()->editShowCaptcha( $editpage );
|
|
|
|
}
|
|
|
|
|
2018-06-15 21:08:14 +00:00
|
|
|
/**
|
2019-09-28 10:04:18 +00:00
|
|
|
* @param EditPage $editPage
|
2019-11-19 03:28:05 +00:00
|
|
|
* @param OutputPage $out
|
2018-06-15 21:08:14 +00:00
|
|
|
*/
|
2019-11-19 03:28:05 +00:00
|
|
|
public static function showEditFormFields( EditPage $editPage, OutputPage $out ) {
|
2017-02-17 13:09:34 +00:00
|
|
|
self::getInstance()->showEditFormFields( $editPage, $out );
|
2011-04-23 11:44:47 +00:00
|
|
|
}
|
|
|
|
|
2018-06-15 21:08:14 +00:00
|
|
|
/**
|
2019-11-19 03:28:05 +00:00
|
|
|
* @param HTMLForm $form
|
2018-06-15 21:08:14 +00:00
|
|
|
* @return bool
|
|
|
|
*/
|
2019-11-19 03:28:05 +00:00
|
|
|
public static function injectEmailUser( $form ) {
|
2011-04-23 11:44:47 +00:00
|
|
|
return self::getInstance()->injectEmailUser( $form );
|
|
|
|
}
|
|
|
|
|
2018-06-15 21:08:14 +00:00
|
|
|
/**
|
|
|
|
* @param MailAddress $from
|
|
|
|
* @param MailAddress $to
|
|
|
|
* @param string $subject
|
|
|
|
* @param string $text
|
|
|
|
* @param string &$error
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
public static function confirmEmailUser( $from, $to, $subject, $text, &$error ) {
|
2011-04-23 11:44:47 +00:00
|
|
|
return self::getInstance()->confirmEmailUser( $from, $to, $subject, $text, $error );
|
|
|
|
}
|
2011-11-23 19:09:57 +00:00
|
|
|
|
2018-06-15 21:08:14 +00:00
|
|
|
/**
|
|
|
|
* APIGetAllowedParams hook handler
|
|
|
|
* Default $flags to 1 for backwards-compatible behavior
|
2019-11-19 03:28:05 +00:00
|
|
|
* @param ApiBase $module
|
2018-06-15 21:08:14 +00:00
|
|
|
* @param array &$params
|
|
|
|
* @param int $flags
|
|
|
|
* @return bool
|
|
|
|
*/
|
2019-11-19 03:28:05 +00:00
|
|
|
public static function onAPIGetAllowedParams( ApiBase $module, &$params, $flags = 1 ) {
|
2018-06-15 21:08:14 +00:00
|
|
|
return self::getInstance()->apiGetAllowedParams( $module, $params, $flags );
|
2011-11-23 19:09:57 +00:00
|
|
|
}
|
|
|
|
|
2018-06-15 21:08:14 +00:00
|
|
|
/**
|
|
|
|
* @param array $requests
|
|
|
|
* @param array $fieldInfo
|
|
|
|
* @param array &$formDescriptor
|
|
|
|
* @param string $action
|
|
|
|
*/
|
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 16:08:17 +00:00
|
|
|
public static function confirmEditSetup() {
|
2020-03-08 16:15:06 +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
|
2017-09-09 18:10:12 +00:00
|
|
|
* @param bool &$whitelisted
|
2016-08-18 18:08:51 +00:00
|
|
|
*/
|
|
|
|
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
|
|
|
|
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
|
2019-11-19 03:28:05 +00:00
|
|
|
* @param Content $content
|
2016-03-12 21:35:42 +00:00
|
|
|
* @param string &$html
|
2019-11-19 03:28:05 +00:00
|
|
|
* @param ?ParserOutput $parserOutput
|
2016-03-12 21:35:42 +00:00
|
|
|
* @return bool
|
|
|
|
*/
|
2019-11-19 03:28:05 +00:00
|
|
|
public static function onAlternateEditPreview(
|
|
|
|
EditPage $editor,
|
|
|
|
Content $content,
|
|
|
|
&$html,
|
|
|
|
$parserOutput
|
|
|
|
) {
|
2016-03-12 21:35:42 +00:00
|
|
|
$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;
|
|
|
|
}
|
2020-01-25 05:38:50 +00:00
|
|
|
if ( IPUtils::isIPAddress( $ip ) ) {
|
2016-03-12 21:35:42 +00:00
|
|
|
$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
|
2018-06-15 22:58:02 +00:00
|
|
|
$lang->truncateForVisual( $ip, 50 )
|
2016-03-12 21:35:42 +00:00
|
|
|
) .
|
|
|
|
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;
|
|
|
|
}
|
2020-04-15 11:32:58 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* ResourceLoaderRegisterModules hook handler.
|
|
|
|
*
|
|
|
|
* @param ResourceLoader $resourceLoader
|
|
|
|
*/
|
|
|
|
public static function onResourceLoaderRegisterModules( ResourceLoader $resourceLoader ) {
|
|
|
|
$extensionRegistry = ExtensionRegistry::getInstance();
|
|
|
|
$messages = [];
|
|
|
|
|
|
|
|
$messages[] = 'colon-separator';
|
|
|
|
$messages[] = 'captcha-edit';
|
|
|
|
$messages[] = 'captcha-label';
|
|
|
|
|
|
|
|
if ( $extensionRegistry->isLoaded( 'QuestyCaptcha' ) ) {
|
|
|
|
$messages[] = 'questycaptcha-edit';
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( $extensionRegistry->isLoaded( 'FancyCaptcha' ) ) {
|
|
|
|
$messages[] = 'fancycaptcha-edit';
|
|
|
|
$messages[] = 'fancycaptcha-reload-text';
|
|
|
|
$messages[] = 'fancycaptcha-imgcaptcha-ph';
|
|
|
|
}
|
|
|
|
|
|
|
|
$resourceLoader->register( [
|
|
|
|
'ext.confirmEdit.CaptchaInputWidget' => [
|
|
|
|
'localBasePath' => dirname( __DIR__ ),
|
|
|
|
'remoteExtPath' => 'ConfirmEdit',
|
|
|
|
'scripts' => 'resources/libs/ext.confirmEdit.CaptchaInputWidget.js',
|
|
|
|
'styles' => 'resources/libs/ext.confirmEdit.CaptchaInputWidget.less',
|
|
|
|
'messages' => $messages,
|
|
|
|
'dependencies' => 'oojs-ui-core',
|
|
|
|
'targets' => [ 'desktop', 'mobile' ],
|
|
|
|
]
|
|
|
|
] );
|
|
|
|
}
|
|
|
|
|
2011-04-23 11:44:47 +00:00
|
|
|
}
|