2011-04-23 11:44:47 +00:00
|
|
|
<?php
|
|
|
|
|
2023-08-15 10:11:58 +00:00
|
|
|
// phpcs:disable MediaWiki.NamingConventions.LowerCamelFunctionsName.FunctionName
|
|
|
|
|
2022-04-08 16:40:15 +00:00
|
|
|
namespace MediaWiki\Extension\ConfirmEdit;
|
|
|
|
|
2023-08-15 10:11:58 +00:00
|
|
|
use MediaWiki\Api\Hook\APIGetAllowedParamsHook;
|
2024-10-19 21:34:41 +00:00
|
|
|
use MediaWiki\Content\Content;
|
2024-06-08 21:46:45 +00:00
|
|
|
use MediaWiki\Context\IContextSource;
|
2022-04-08 16:40:15 +00:00
|
|
|
use MediaWiki\Extension\ConfirmEdit\SimpleCaptcha\SimpleCaptcha;
|
|
|
|
use MediaWiki\Hook\AlternateEditPreviewHook;
|
2023-08-15 10:11:58 +00:00
|
|
|
use MediaWiki\Hook\EditFilterMergedContentHook;
|
|
|
|
use MediaWiki\Hook\EditPage__showEditForm_fieldsHook;
|
2022-04-08 16:40:15 +00:00
|
|
|
use MediaWiki\Hook\EditPageBeforeEditButtonsHook;
|
|
|
|
use MediaWiki\Hook\EmailUserFormHook;
|
|
|
|
use MediaWiki\Hook\EmailUserHook;
|
2023-12-10 23:07:55 +00:00
|
|
|
use MediaWiki\Html\Html;
|
2022-04-08 16:40:15 +00:00
|
|
|
use MediaWiki\Permissions\Hook\TitleReadWhitelistHook;
|
2024-10-19 21:34:41 +00:00
|
|
|
use MediaWiki\Registration\ExtensionRegistry;
|
2022-04-08 16:40:15 +00:00
|
|
|
use MediaWiki\ResourceLoader\Hook\ResourceLoaderRegisterModulesHook;
|
2022-06-13 07:29:56 +00:00
|
|
|
use MediaWiki\ResourceLoader\ResourceLoader;
|
2023-08-15 10:11:58 +00:00
|
|
|
use MediaWiki\SpecialPage\Hook\AuthChangeFormFieldsHook;
|
2023-12-10 23:07:55 +00:00
|
|
|
use MediaWiki\SpecialPage\SpecialPage;
|
|
|
|
use MediaWiki\Status\Status;
|
2022-04-08 16:40:15 +00:00
|
|
|
use MediaWiki\Storage\Hook\PageSaveCompleteHook;
|
2023-08-19 04:14:21 +00:00
|
|
|
use MediaWiki\Title\Title;
|
2023-12-10 23:07:55 +00:00
|
|
|
use MediaWiki\User\User;
|
2020-01-25 05:38:50 +00:00
|
|
|
use Wikimedia\IPUtils;
|
2024-10-19 21:34:41 +00:00
|
|
|
use Wikimedia\ObjectCache\WANObjectCache;
|
2019-07-03 12:43:23 +00:00
|
|
|
|
2022-04-08 16:40:15 +00:00
|
|
|
class Hooks implements
|
|
|
|
AlternateEditPreviewHook,
|
|
|
|
EditPageBeforeEditButtonsHook,
|
|
|
|
EmailUserFormHook,
|
|
|
|
EmailUserHook,
|
|
|
|
TitleReadWhitelistHook,
|
|
|
|
ResourceLoaderRegisterModulesHook,
|
2023-08-15 10:11:58 +00:00
|
|
|
PageSaveCompleteHook,
|
|
|
|
EditPage__showEditForm_fieldsHook,
|
|
|
|
EditFilterMergedContentHook,
|
|
|
|
APIGetAllowedParamsHook,
|
|
|
|
AuthChangeFormFieldsHook
|
2020-12-10 20:00:44 +00:00
|
|
|
{
|
|
|
|
|
2024-08-19 19:36:23 +00:00
|
|
|
/** @var bool */
|
2016-04-25 20:58:18 +00:00
|
|
|
protected static $instanceCreated = false;
|
|
|
|
|
2024-04-26 20:33:09 +00:00
|
|
|
private WANObjectCache $cache;
|
|
|
|
|
|
|
|
public function __construct(
|
|
|
|
WANObjectCache $cache
|
|
|
|
) {
|
|
|
|
$this->cache = $cache;
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2024-10-25 18:24:07 +00:00
|
|
|
/** @inheritDoc */
|
2023-08-15 10:11:58 +00:00
|
|
|
public function onEditFilterMergedContent( IContextSource $context, Content $content, Status $status,
|
|
|
|
$summary, User $user, $minorEdit
|
2018-06-15 21:08:14 +00:00
|
|
|
) {
|
2024-05-15 08:23:46 +00:00
|
|
|
$simpleCaptcha = self::getInstance();
|
|
|
|
// Set a flag indicating that ConfirmEdit's implementation of
|
2024-10-25 18:24:07 +00:00
|
|
|
// EditFilterMergedContent ran.
|
|
|
|
// This can be checked by other MediaWiki extensions, e.g. AbuseFilter.
|
2024-05-15 08:23:46 +00:00
|
|
|
$simpleCaptcha->setEditFilterMergedContentHandlerInvoked();
|
|
|
|
return $simpleCaptcha->confirmEditMerged( $context, $content, $status, $summary,
|
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
|
|
|
$user, $minorEdit );
|
2011-04-23 11:44:47 +00:00
|
|
|
}
|
|
|
|
|
2024-10-25 18:24:07 +00:00
|
|
|
/** @inheritDoc */
|
2020-12-10 20:00:44 +00:00
|
|
|
public function onPageSaveComplete(
|
|
|
|
$wikiPage,
|
|
|
|
$user,
|
|
|
|
$summary,
|
|
|
|
$flags,
|
|
|
|
$revisionRecord,
|
|
|
|
$editResult
|
2016-03-07 16:29:53 +00:00
|
|
|
) {
|
|
|
|
$title = $wikiPage->getTitle();
|
|
|
|
if ( $title->getText() === 'Captcha-ip-whitelist' && $title->getNamespace() === NS_MEDIAWIKI ) {
|
2024-11-06 23:56:11 +00:00
|
|
|
$this->cache->delete( $this->cache->makeKey( 'confirmedit', 'ipbypasslist' ) );
|
2016-03-07 16:29:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2024-10-25 18:24:07 +00:00
|
|
|
/** @inheritDoc */
|
2020-12-10 20:00:44 +00:00
|
|
|
public function onEditPageBeforeEditButtons( $editpage, &$buttons, &$tabindex ) {
|
2014-05-30 14:33:05 +00:00
|
|
|
self::getInstance()->editShowCaptcha( $editpage );
|
|
|
|
}
|
|
|
|
|
2024-10-25 18:24:07 +00:00
|
|
|
/** @inheritDoc */
|
2023-08-15 10:11:58 +00:00
|
|
|
public function onEditPage__showEditForm_fields( $editPage, $out ) {
|
2017-02-17 13:09:34 +00:00
|
|
|
self::getInstance()->showEditFormFields( $editPage, $out );
|
2011-04-23 11:44:47 +00:00
|
|
|
}
|
|
|
|
|
2024-10-25 18:24:07 +00:00
|
|
|
/** @inheritDoc */
|
2020-12-10 20:00:44 +00:00
|
|
|
public function onEmailUserForm( &$form ) {
|
2011-04-23 11:44:47 +00:00
|
|
|
return self::getInstance()->injectEmailUser( $form );
|
|
|
|
}
|
|
|
|
|
2024-10-25 18:24:07 +00:00
|
|
|
/** @inheritDoc */
|
2020-12-10 20:00:44 +00:00
|
|
|
public function onEmailUser( &$to, &$from, &$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
|
|
|
|
2024-10-25 18:24:07 +00:00
|
|
|
/** @inheritDoc */
|
2023-08-15 10:11:58 +00:00
|
|
|
public function onAPIGetAllowedParams( $module, &$params, $flags ) {
|
2018-06-15 21:08:14 +00:00
|
|
|
return self::getInstance()->apiGetAllowedParams( $module, $params, $flags );
|
2011-11-23 19:09:57 +00:00
|
|
|
}
|
|
|
|
|
2024-10-25 18:24:07 +00:00
|
|
|
/** @inheritDoc */
|
2023-08-15 10:11:58 +00:00
|
|
|
public function onAuthChangeFormFields(
|
|
|
|
$requests, $fieldInfo, &$formDescriptor, $action
|
2016-04-25 20:58:18 +00:00
|
|
|
) {
|
|
|
|
self::getInstance()->onAuthChangeFormFields( $requests, $fieldInfo, $formDescriptor, $action );
|
|
|
|
}
|
|
|
|
|
2015-05-21 16:08:17 +00:00
|
|
|
public static function confirmEditSetup() {
|
2023-08-16 02:15:10 +00:00
|
|
|
global $wgCaptchaTriggers;
|
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
|
2023-08-16 02:15:10 +00:00
|
|
|
if ( defined( 'MW_PHPUNIT_TEST' ) || defined( 'MW_QUIBBLE_CI' ) ) {
|
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
|
|
|
|
2024-10-25 18:24:07 +00:00
|
|
|
/** @inheritDoc */
|
2020-12-10 20:00:44 +00:00
|
|
|
public function onTitleReadWhitelist( $title, $user, &$whitelisted ) {
|
2016-08-18 18:08:51 +00:00
|
|
|
$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
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
2024-10-25 18:24:07 +00:00
|
|
|
/** @inheritDoc */
|
2020-12-10 20:00:44 +00:00
|
|
|
public function onAlternateEditPreview( $editPage, &$content, &$previewHTML,
|
|
|
|
&$parserOutput
|
2019-11-19 03:28:05 +00:00
|
|
|
) {
|
2020-12-10 20:00:44 +00:00
|
|
|
$title = $editPage->getTitle();
|
2016-03-12 21:35:42 +00:00
|
|
|
$exceptionTitle = Title::makeTitle( NS_MEDIAWIKI, 'Captcha-ip-whitelist' );
|
|
|
|
|
|
|
|
if ( !$title->equals( $exceptionTitle ) ) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-12-10 20:00:44 +00:00
|
|
|
$ctx = $editPage->getArticle()->getContext();
|
2016-03-12 21:35:42 +00:00
|
|
|
$out = $ctx->getOutput();
|
|
|
|
$lang = $ctx->getLanguage();
|
|
|
|
|
|
|
|
$lines = explode( "\n", $content->getNativeData() );
|
2022-03-24 18:03:39 +00:00
|
|
|
$previewHTML .= Html::warningBox(
|
2016-03-12 21:35:42 +00:00
|
|
|
$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';
|
|
|
|
}
|
2020-12-10 20:00:44 +00:00
|
|
|
$previewHTML .= Html::openElement( 'tr' ) .
|
2016-03-12 21:35:42 +00:00
|
|
|
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' );
|
|
|
|
}
|
2020-12-10 20:00:44 +00:00
|
|
|
$previewHTML .= Html::closeElement( 'table' );
|
2016-03-12 21:35:42 +00:00
|
|
|
$out->addModuleStyles( 'ext.confirmEdit.editPreview.ipwhitelist.styles' );
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
2020-04-15 11:32:58 +00:00
|
|
|
|
2024-10-25 18:24:07 +00:00
|
|
|
/** @inheritDoc */
|
2021-07-22 06:38:52 +00:00
|
|
|
public function onResourceLoaderRegisterModules( ResourceLoader $resourceLoader ): void {
|
2020-04-15 11:32:58 +00:00
|
|
|
$extensionRegistry = ExtensionRegistry::getInstance();
|
2024-12-11 15:40:27 +00:00
|
|
|
$messages = [
|
|
|
|
'colon-separator',
|
|
|
|
'captcha-edit',
|
|
|
|
'captcha-label'
|
|
|
|
];
|
2020-04-15 11:32:58 +00:00
|
|
|
|
|
|
|
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',
|
|
|
|
]
|
|
|
|
] );
|
|
|
|
}
|
|
|
|
|
2011-04-23 11:44:47 +00:00
|
|
|
}
|