Support dependency injection in log formatters

Use ObjectFactory specifications instead of class names in
$wgLogActionHandlers, like in most other places. Class name
support is retained for B/C.

Also remove old LogPage code that assumed $wgLogActionsHandlers
is an array of callables. This is how $wgLogActionsHandlers was
defined in b6d72cfeb2, but then
4ac56c2466 redefined it to an
array of classnames (and I6846ce0 adjusted the documentation),
and the old functionality has been broken since.

Make LogFormatter::__construct() public so subclasses can be
created in the service wiring.

Technically this is a breaking change since there is no
guarantee existing formatter classes are publicly creatable,
but there is no way to issue a deprecation warning about that,
and it isn't really covered by the stable interface policy.
All non-public constructors found by codesearch are fixed in
companion patches.

Bug: T54220
Change-Id: I72427a4b3f4be1c3c1eb3522a8b58fe9445a3397
Depends-On: Ie691e8d59141f696619dce8f756645c45a3e943a
Depends-On: I41562247d51f4f9fe8dafed37ba2ad81c881d99d
This commit is contained in:
Gergő Tisza 2023-12-17 20:50:29 -08:00 committed by Bartosz Dziewoński
parent 1d40c4039f
commit 9de0e23b48
5 changed files with 46 additions and 39 deletions

View file

@ -37,6 +37,7 @@ For notes on 1.41.x and older releases, see HISTORY.
==== Changed configuration ====
* $wgAutoCreateTempUser: 'matchPattern' now supports multiple match patterns.
* $wgLogActionsHandlers is now ObjectFactory compatible.
* …
==== Removed configuration ====
@ -295,6 +296,8 @@ because of Phabricator reports.
* The module `jquery.cookie` (deprecated as of 1.41) has been removed. Please
use `mediawiki.cookie` instead
* The no-op module `es6-polyfills` (deprecated as of 1.41) has been removed.
* Log formatters (as defined in $wgLogActionsHandlers) are now created by
ObjectFactory, which means their constructors must be public.
* …
=== Deprecations in 1.42 ===

View file

@ -7435,8 +7435,10 @@ config-schema:
upload/upload: UploadLogFormatter
type: object
description: |-
The same as above, but here values are names of classes,
not messages.
The same as above, but here values are class names or ObjectFactory specifications,
not messages. The specification must resolve to a LogFormatter subclass,
and will receive the LogEntry object as its first constructor argument.
The type can be specified as '*' (e.g. 'block/*') to handle all types.
@see \LogPage::actionText
@see \LogFormatter
ActionFilteredLogs:

View file

@ -11802,8 +11802,10 @@ class MainConfigSchema {
];
/**
* The same as above, but here values are names of classes,
* not messages.
* The same as above, but here values are class names or ObjectFactory specifications,
* not messages. The specification must resolve to a LogFormatter subclass,
* and will receive the LogEntry object as its first constructor argument.
* The type can be specified as '*' (e.g. 'block/*') to handle all types.
*
* @see \LogPage::actionText
* @see \LogFormatter

View file

@ -61,12 +61,18 @@ class LogFormatter {
public static function newFromEntry( LogEntry $entry ) {
$logActionsHandlers = MediaWikiServices::getInstance()->getMainConfig()
->get( MainConfigNames::LogActionsHandlers );
$objectFactory = MediaWikiServices::getInstance()->getObjectFactory();
$fulltype = $entry->getFullType();
$wildcard = $entry->getType() . '/*';
$handler = $logActionsHandlers[$fulltype] ?? $logActionsHandlers[$wildcard] ?? '';
if ( $handler !== '' && is_string( $handler ) && class_exists( $handler ) ) {
return new $handler( $entry );
if ( $handler !== '' ) {
return $objectFactory->createObject( $handler, [
'extraArgs' => [ $entry ],
'allowClassName' => true,
'assertClass' => self::class,
] );
}
return new LegacyLogFormatter( $entry );
@ -125,7 +131,7 @@ class LogFormatter {
*
* @param LogEntry $entry
*/
protected function __construct( LogEntry $entry ) {
public function __construct( LogEntry $entry ) {
$this->entry = $entry;
$this->context = RequestContext::getMain();
}

View file

@ -245,46 +245,40 @@ class LogPage {
) {
global $wgLang;
$config = MediaWikiServices::getInstance()->getMainConfig();
$logActionsHandlers = $config->get( MainConfigNames::LogActionsHandlers );
$key = "$type/$action";
if ( isset( $logActionsHandlers[$key] ) ) {
$args = func_get_args();
$rv = call_user_func_array( $logActionsHandlers[$key], $args );
$logActions = $config->get( MainConfigNames::LogActions );
if ( isset( $logActions[$key] ) ) {
$message = $logActions[$key];
} else {
$logActions = $config->get( MainConfigNames::LogActions );
wfDebug( "LogPage::actionText - unknown action $key" );
$message = "log-unknown-action";
$params = [ $key ];
}
if ( isset( $logActions[$key] ) ) {
$message = $logActions[$key];
} else {
wfDebug( "LogPage::actionText - unknown action $key" );
$message = "log-unknown-action";
$params = [ $key ];
}
if ( $skin === null ) {
$langObj = MediaWikiServices::getInstance()->getContentLanguage();
$langObjOrNull = null;
} else {
// TODO Is $skin->getLanguage() safe here?
StubUserLang::unstub( $wgLang );
$langObj = $wgLang;
$langObjOrNull = $wgLang;
}
if ( $title === null ) {
$rv = wfMessage( $message )->inLanguage( $langObj )->escaped();
} else {
$titleLink = self::getTitleLink( $title, $langObjOrNull );
if ( $skin === null ) {
$langObj = MediaWikiServices::getInstance()->getContentLanguage();
$langObjOrNull = null;
if ( count( $params ) == 0 ) {
$rv = wfMessage( $message )->rawParams( $titleLink )
->inLanguage( $langObj )->escaped();
} else {
// TODO Is $skin->getLanguage() safe here?
StubUserLang::unstub( $wgLang );
$langObj = $wgLang;
$langObjOrNull = $wgLang;
}
if ( $title === null ) {
$rv = wfMessage( $message )->inLanguage( $langObj )->escaped();
} else {
$titleLink = self::getTitleLink( $title, $langObjOrNull );
array_unshift( $params, $titleLink );
if ( count( $params ) == 0 ) {
$rv = wfMessage( $message )->rawParams( $titleLink )
$rv = wfMessage( $message )->rawParams( $params )
->inLanguage( $langObj )->escaped();
} else {
array_unshift( $params, $titleLink );
$rv = wfMessage( $message )->rawParams( $params )
->inLanguage( $langObj )->escaped();
}
}
}