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
|
|
|
|
2020-12-10 20:00:44 +00:00
|
|
|
class ConfirmEditHooks implements
|
|
|
|
\MediaWiki\Hook\AlternateEditPreviewHook,
|
|
|
|
\MediaWiki\Hook\EditPageBeforeEditButtonsHook,
|
|
|
|
\MediaWiki\Hook\EmailUserFormHook,
|
|
|
|
\MediaWiki\Hook\EmailUserHook,
|
|
|
|
\MediaWiki\Permissions\Hook\TitleReadWhitelistHook,
|
|
|
|
\MediaWiki\ResourceLoader\Hook\ResourceLoaderRegisterModulesHook,
|
|
|
|
\MediaWiki\Storage\Hook\PageSaveCompleteHook
|
|
|
|
{
|
|
|
|
|
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-12-10 20:00:44 +00:00
|
|
|
* @see https://www.mediawiki.org/wiki/Manual:Hooks/PageSaveComplete
|
2016-03-07 16:29:53 +00:00
|
|
|
*
|
2017-02-17 13:09:34 +00:00
|
|
|
* @param WikiPage $wikiPage
|
2020-12-10 20:00:44 +00:00
|
|
|
* @param UserIdentity $user
|
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
|
2020-12-10 20:00:44 +00:00
|
|
|
* @return bool|void
|
2016-03-07 16:29:53 +00:00
|
|
|
*/
|
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 ) {
|
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
|
|
|
/**
|
2020-12-10 20:00:44 +00:00
|
|
|
* @see https://www.mediawiki.org/wiki/Manual:Hooks/EditPageBeforeEditButtons
|
|
|
|
*
|
|
|
|
* @param EditPage $editpage Current EditPage object
|
|
|
|
* @param array &$buttons Array of edit buttons, "Save", "Preview", "Live", and "Diff"
|
|
|
|
* @param string &$tabindex HTML tabindex of the last edit check/button
|
|
|
|
* @return bool|void True or no return value to continue or false to abort
|
2018-06-15 21:08:14 +00:00
|
|
|
*/
|
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 );
|
|
|
|
}
|
|
|
|
|
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
|
|
|
/**
|
2020-12-10 20:00:44 +00:00
|
|
|
* @see https://www.mediawiki.org/wiki/Manual:Hooks/EmailUserForm
|
|
|
|
*
|
|
|
|
* @param HTMLForm &$form HTMLForm object
|
|
|
|
* @return bool|void True or no return value to continue or false to abort
|
2018-06-15 21:08:14 +00:00
|
|
|
*/
|
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 );
|
|
|
|
}
|
|
|
|
|
2018-06-15 21:08:14 +00:00
|
|
|
/**
|
2020-12-10 20:00:44 +00:00
|
|
|
* @see https://www.mediawiki.org/wiki/Manual:Hooks/EmailUser
|
|
|
|
*
|
|
|
|
* @param MailAddress &$to MailAddress object of receiving user
|
|
|
|
* @param MailAddress &$from MailAddress object of sending user
|
|
|
|
* @param MailAddress &$subject subject of the mail
|
|
|
|
* @param string &$text text of the mail
|
|
|
|
* @param bool|Status|MessageSpecifier|array &$error Out-param for an error.
|
|
|
|
* Should be set to a Status object or boolean false.
|
|
|
|
* @return bool|void True or no return value to continue or false to abort
|
2018-06-15 21:08:14 +00:00
|
|
|
*/
|
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
|
|
|
|
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
|
|
|
/**
|
2020-12-10 20:00:44 +00:00
|
|
|
* @see https://www.mediawiki.org/wiki/Manual:Hooks/TitleReadWhitelist
|
2016-08-18 18:08:51 +00:00
|
|
|
*
|
|
|
|
* @param Title $title
|
|
|
|
* @param User $user
|
2017-09-09 18:10:12 +00:00
|
|
|
* @param bool &$whitelisted
|
2020-12-10 20:00:44 +00:00
|
|
|
* @return bool|void
|
2016-08-18 18:08:51 +00:00
|
|
|
*/
|
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
|
|
|
/**
|
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
|
|
|
/**
|
2020-12-10 20:00:44 +00:00
|
|
|
* @see https://www.mediawiki.org/wiki/Manual:Hooks/AlternateEditPreview
|
2016-03-12 21:35:42 +00:00
|
|
|
*
|
2020-12-10 20:00:44 +00:00
|
|
|
* @param EditPage $editPage
|
|
|
|
* @param Content &$content
|
|
|
|
* @param string &$previewHTML
|
|
|
|
* @param ParserOutput &$parserOutput
|
|
|
|
* @return bool|void
|
2016-03-12 21:35:42 +00:00
|
|
|
*/
|
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() );
|
2020-12-10 20:00:44 +00:00
|
|
|
$previewHTML .= Html::rawElement(
|
2016-03-12 21:35:42 +00:00
|
|
|
'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';
|
|
|
|
}
|
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
|
|
|
|
|
|
|
/**
|
2020-12-10 20:00:44 +00:00
|
|
|
* @see https://www.mediawiki.org/wiki/Manual:Hooks/ResourceLoaderRegisterModules
|
2020-04-15 11:32:58 +00:00
|
|
|
*
|
|
|
|
* @param ResourceLoader $resourceLoader
|
2020-12-10 20:00:44 +00:00
|
|
|
* @return void
|
2020-04-15 11:32:58 +00:00
|
|
|
*/
|
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();
|
|
|
|
$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
|
|
|
}
|