2004-12-18 06:29:23 +00:00
|
|
|
<?php
|
2012-05-12 20:33:02 +00:00
|
|
|
/**
|
|
|
|
|
* Methods to make links and related items.
|
|
|
|
|
*
|
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
|
* (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU General Public License along
|
|
|
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
|
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
|
* http://www.gnu.org/copyleft/gpl.html
|
|
|
|
|
*
|
|
|
|
|
* @file
|
|
|
|
|
*/
|
2021-08-01 15:11:23 +00:00
|
|
|
|
2022-12-05 11:29:37 +00:00
|
|
|
namespace MediaWiki\Linker;
|
|
|
|
|
|
|
|
|
|
use Config;
|
|
|
|
|
use ContextSource;
|
|
|
|
|
use DerivativeContext;
|
|
|
|
|
use ExternalUserNames;
|
|
|
|
|
use File;
|
|
|
|
|
use Hooks;
|
|
|
|
|
use HtmlArmor;
|
|
|
|
|
use IContextSource;
|
|
|
|
|
use Language;
|
|
|
|
|
use MediaTransformOutput;
|
2023-02-16 19:27:21 +00:00
|
|
|
use MediaWiki\Html\Html;
|
|
|
|
|
use MediaWiki\Html\HtmlHelper;
|
2022-04-01 15:58:32 +00:00
|
|
|
use MediaWiki\MainConfigNames;
|
Add LinkRenderer (rewrite of Linker::link())
This is a rewrite of Linker::link() to a non-static, LinkTarget-based
interface. Users of plain Linker::link() with no options can use the
LinkRenderer instance provided by MediaWikiServices. Others that
have specific options should create and configure their own instance,
which can be used to create as many links as necessary.
The main entrypoints for making links are:
* ->makeLink( $target, $text, $attribs, $query );
* ->makeKnownLink( $target, $text, $attribs, $query );
* ->makeBrokenLink( $target, $text, $attribs, $query );
The order of the parameters are the same as Linker::link(), except
$options are now part of the LinkRenderer instance, and
known/broken status requires calling the function explicitly.
Additionally, instead of passing in raw $html for the link text, the
$text parameter will automatically be escaped unless it is specially
marked as safe HTML using the MediaWiki\Linker\HtmlArmor class.
The LinkBegin and LinkEnd hooks are now deprecated, but still function
for backwards-compatability. Clients should migrate to the nearly-
equivalent LinkRendererBegin and LinkRendererEnd hooks.
The main differences between the hooks are:
* Passing HtmlPageLinkRenderer object instead of deprecated DummyLinker
* Using LinkTarget instead of Title
* Begin hook can no longer change known/broken status of link. Use the
TitleIsAlwaysKnown hook for that.
* $options are no longer passed, they can be read (but shouldn't be
modified!) from the LinkRenderer object.
Bug: T469
Change-Id: I057cc86ae6404a080aa3c8e0e956ecbb10a897d5
2016-04-21 20:13:21 +00:00
|
|
|
use MediaWiki\MediaWikiServices;
|
2021-02-24 15:46:13 +00:00
|
|
|
use MediaWiki\Permissions\Authority;
|
2019-09-12 19:40:57 +00:00
|
|
|
use MediaWiki\Revision\RevisionRecord;
|
2023-03-01 20:33:26 +00:00
|
|
|
use MediaWiki\Title\Title;
|
2022-12-05 11:29:37 +00:00
|
|
|
use Message;
|
|
|
|
|
use MessageLocalizer;
|
|
|
|
|
use Parser;
|
|
|
|
|
use RequestContext;
|
|
|
|
|
use SpecialPage;
|
|
|
|
|
use TitleValue;
|
|
|
|
|
use User;
|
|
|
|
|
use WatchedItem;
|
2019-06-25 18:53:15 +00:00
|
|
|
use Wikimedia\IPUtils;
|
2023-01-26 22:05:10 +00:00
|
|
|
use Wikimedia\Parsoid\Core\TOCData;
|
2022-07-28 21:10:02 +00:00
|
|
|
use Wikimedia\Rdbms\SelectQueryBuilder;
|
2022-12-29 18:41:26 +00:00
|
|
|
use Wikimedia\RemexHtml\Serializer\SerializerNode;
|
2022-12-05 11:29:37 +00:00
|
|
|
use Xml;
|
2012-05-12 20:33:02 +00:00
|
|
|
|
2004-12-18 06:29:23 +00:00
|
|
|
/**
|
2011-04-03 11:44:11 +00:00
|
|
|
* Some internal bits split of from Skin.php. These functions are used
|
2008-07-01 00:40:53 +00:00
|
|
|
* for primarily page content: links, embedded images, table of contents. Links
|
2011-04-03 11:44:11 +00:00
|
|
|
* are also used in the skin.
|
2004-12-18 06:29:23 +00:00
|
|
|
*
|
2014-07-24 09:30:25 +00:00
|
|
|
* @todo turn this into a legacy interface for HtmlPageLinkRenderer and similar services.
|
2014-01-09 14:35:16 +00:00
|
|
|
*
|
WARNING: HUGE COMMIT
Doxygen documentation update:
* Changed alls @addtogroup to @ingroup. @addtogroup adds the comment to the group description, but doesn't add the file, class, function, ... to the group like @ingroup does. See for example http://svn.wikimedia.org/doc/group__SpecialPage.html where it's impossible to see related files, classes, ... that should belong to that group.
* Added @file to file description, it seems that it should be explicitely decalred for file descriptions, otherwise doxygen will think that the comment document the first class, variabled, function, ... that is in that file.
* Removed some empty comments
* Removed some ?>
Added following groups:
* ExternalStorage
* JobQueue
* MaintenanceLanguage
One more thing: there are still a lot of warnings when generating the doc.
2008-05-20 17:13:28 +00:00
|
|
|
* @ingroup Skins
|
2004-12-18 06:29:23 +00:00
|
|
|
*/
|
|
|
|
|
class Linker {
|
2007-08-06 07:09:59 +00:00
|
|
|
/**
|
|
|
|
|
* Flags for userToolLinks()
|
|
|
|
|
*/
|
2020-05-11 00:48:27 +00:00
|
|
|
public const TOOL_LINKS_NOBLOCK = 1;
|
|
|
|
|
public const TOOL_LINKS_EMAIL = 2;
|
2007-08-06 07:09:59 +00:00
|
|
|
|
2008-07-30 21:02:28 +00:00
|
|
|
/**
|
2008-07-30 21:11:17 +00:00
|
|
|
* This function returns an HTML link to the given target. It serves a few
|
|
|
|
|
* purposes:
|
2019-04-15 12:47:32 +00:00
|
|
|
* 1) If $target is a LinkTarget, the correct URL to link to will be figured
|
2008-07-30 21:11:17 +00:00
|
|
|
* out automatically.
|
|
|
|
|
* 2) It automatically adds the usual classes for various types of link
|
|
|
|
|
* targets: "new" for red links, "stub" for short articles, etc.
|
2008-07-30 21:02:28 +00:00
|
|
|
* 3) It escapes all attribute values safely so there's no risk of XSS.
|
2019-04-15 12:47:32 +00:00
|
|
|
* 4) It provides a default tooltip if the target is a LinkTarget (the page
|
2008-07-30 21:11:17 +00:00
|
|
|
* name of the target).
|
|
|
|
|
* link() replaces the old functions in the makeLink() family.
|
2008-07-30 21:02:28 +00:00
|
|
|
*
|
2011-12-13 20:45:35 +00:00
|
|
|
* @since 1.18 Method exists since 1.16 as non-static, made static in 1.18.
|
2016-07-12 22:26:53 +00:00
|
|
|
* @deprecated since 1.28, use MediaWiki\Linker\LinkRenderer instead
|
2011-12-13 05:25:06 +00:00
|
|
|
*
|
2017-09-10 22:19:24 +00:00
|
|
|
* @param LinkTarget $target Can currently only be a LinkTarget, but this may
|
2008-07-30 21:11:17 +00:00
|
|
|
* change to support Images, literal URLs, etc.
|
2019-12-12 18:57:11 +00:00
|
|
|
* @param string|null $html The HTML contents of the <a> element, i.e.,
|
2008-08-01 14:33:14 +00:00
|
|
|
* the link text. This is raw HTML and will not be escaped. If null,
|
2019-04-15 12:47:32 +00:00
|
|
|
* defaults to the prefixed text of the LinkTarget; or if the LinkTarget is just a
|
2008-08-01 01:37:07 +00:00
|
|
|
* fragment, the contents of the fragment.
|
2014-04-08 15:29:17 +00:00
|
|
|
* @param array $customAttribs A key => value array of extra HTML attributes,
|
2013-03-13 07:42:41 +00:00
|
|
|
* such as title and class. (href is ignored.) Classes will be
|
2008-07-30 21:11:17 +00:00
|
|
|
* merged with the default classes, while other attributes will replace
|
|
|
|
|
* default attributes. All passed attribute values will be HTML-escaped.
|
|
|
|
|
* A false attribute value means to suppress that attribute.
|
2014-04-08 15:29:17 +00:00
|
|
|
* @param array $query The query string to append to the URL
|
2008-08-06 11:41:51 +00:00
|
|
|
* you're linking to, in key => value array form. Query keys and values
|
|
|
|
|
* will be URL-encoded.
|
2014-04-08 15:29:17 +00:00
|
|
|
* @param string|array $options String or array of strings:
|
2012-01-03 21:20:35 +00:00
|
|
|
* 'known': Page is known to exist, so don't check if it does.
|
|
|
|
|
* 'broken': Page is known not to exist, so don't check if it does.
|
|
|
|
|
* 'noclasses': Don't add any classes automatically (includes "new",
|
2008-08-01 15:02:46 +00:00
|
|
|
* "stub", "mw-redirect", "extiw"). Only use the class attribute
|
|
|
|
|
* provided, if any, so you get a simple blue link with no funny i-
|
|
|
|
|
* cons.
|
2012-01-03 21:20:35 +00:00
|
|
|
* 'forcearticlepath': Use the article path always, even with a querystring.
|
2008-10-02 22:52:42 +00:00
|
|
|
* Has compatibility issues on some setups, so avoid wherever possible.
|
2012-09-27 17:47:42 +00:00
|
|
|
* 'http': Force a full URL with http:// as the scheme.
|
|
|
|
|
* 'https': Force a full URL with https:// as the scheme.
|
2008-07-30 21:02:28 +00:00
|
|
|
* @return string HTML <a> attribute
|
|
|
|
|
*/
|
2011-04-03 12:04:04 +00:00
|
|
|
public static function link(
|
2016-02-17 09:09:32 +00:00
|
|
|
$target, $html = null, $customAttribs = [], $query = [], $options = []
|
2011-03-21 16:15:56 +00:00
|
|
|
) {
|
2017-09-10 22:19:24 +00:00
|
|
|
if ( !$target instanceof LinkTarget ) {
|
|
|
|
|
wfWarn( __METHOD__ . ': Requires $target to be a LinkTarget object.', 2 );
|
2011-05-08 00:10:31 +00:00
|
|
|
return "<!-- ERROR -->$html";
|
2008-09-14 00:49:52 +00:00
|
|
|
}
|
2012-07-05 13:58:42 +00:00
|
|
|
|
Add LinkRenderer (rewrite of Linker::link())
This is a rewrite of Linker::link() to a non-static, LinkTarget-based
interface. Users of plain Linker::link() with no options can use the
LinkRenderer instance provided by MediaWikiServices. Others that
have specific options should create and configure their own instance,
which can be used to create as many links as necessary.
The main entrypoints for making links are:
* ->makeLink( $target, $text, $attribs, $query );
* ->makeKnownLink( $target, $text, $attribs, $query );
* ->makeBrokenLink( $target, $text, $attribs, $query );
The order of the parameters are the same as Linker::link(), except
$options are now part of the LinkRenderer instance, and
known/broken status requires calling the function explicitly.
Additionally, instead of passing in raw $html for the link text, the
$text parameter will automatically be escaped unless it is specially
marked as safe HTML using the MediaWiki\Linker\HtmlArmor class.
The LinkBegin and LinkEnd hooks are now deprecated, but still function
for backwards-compatability. Clients should migrate to the nearly-
equivalent LinkRendererBegin and LinkRendererEnd hooks.
The main differences between the hooks are:
* Passing HtmlPageLinkRenderer object instead of deprecated DummyLinker
* Using LinkTarget instead of Title
* Begin hook can no longer change known/broken status of link. Use the
TitleIsAlwaysKnown hook for that.
* $options are no longer passed, they can be read (but shouldn't be
modified!) from the LinkRenderer object.
Bug: T469
Change-Id: I057cc86ae6404a080aa3c8e0e956ecbb10a897d5
2016-04-21 20:13:21 +00:00
|
|
|
$services = MediaWikiServices::getInstance();
|
|
|
|
|
$options = (array)$options;
|
|
|
|
|
if ( $options ) {
|
|
|
|
|
// Custom options, create new LinkRenderer
|
|
|
|
|
$linkRenderer = $services->getLinkRendererFactory()
|
|
|
|
|
->createFromLegacyOptions( $options );
|
|
|
|
|
} else {
|
|
|
|
|
$linkRenderer = $services->getLinkRenderer();
|
2008-07-30 21:02:28 +00:00
|
|
|
}
|
|
|
|
|
|
Add LinkRenderer (rewrite of Linker::link())
This is a rewrite of Linker::link() to a non-static, LinkTarget-based
interface. Users of plain Linker::link() with no options can use the
LinkRenderer instance provided by MediaWikiServices. Others that
have specific options should create and configure their own instance,
which can be used to create as many links as necessary.
The main entrypoints for making links are:
* ->makeLink( $target, $text, $attribs, $query );
* ->makeKnownLink( $target, $text, $attribs, $query );
* ->makeBrokenLink( $target, $text, $attribs, $query );
The order of the parameters are the same as Linker::link(), except
$options are now part of the LinkRenderer instance, and
known/broken status requires calling the function explicitly.
Additionally, instead of passing in raw $html for the link text, the
$text parameter will automatically be escaped unless it is specially
marked as safe HTML using the MediaWiki\Linker\HtmlArmor class.
The LinkBegin and LinkEnd hooks are now deprecated, but still function
for backwards-compatability. Clients should migrate to the nearly-
equivalent LinkRendererBegin and LinkRendererEnd hooks.
The main differences between the hooks are:
* Passing HtmlPageLinkRenderer object instead of deprecated DummyLinker
* Using LinkTarget instead of Title
* Begin hook can no longer change known/broken status of link. Use the
TitleIsAlwaysKnown hook for that.
* $options are no longer passed, they can be read (but shouldn't be
modified!) from the LinkRenderer object.
Bug: T469
Change-Id: I057cc86ae6404a080aa3c8e0e956ecbb10a897d5
2016-04-21 20:13:21 +00:00
|
|
|
if ( $html !== null ) {
|
|
|
|
|
$text = new HtmlArmor( $html );
|
|
|
|
|
} else {
|
2019-02-17 11:32:50 +00:00
|
|
|
$text = null;
|
2008-07-30 21:02:28 +00:00
|
|
|
}
|
2019-02-09 07:25:57 +00:00
|
|
|
|
Add LinkRenderer (rewrite of Linker::link())
This is a rewrite of Linker::link() to a non-static, LinkTarget-based
interface. Users of plain Linker::link() with no options can use the
LinkRenderer instance provided by MediaWikiServices. Others that
have specific options should create and configure their own instance,
which can be used to create as many links as necessary.
The main entrypoints for making links are:
* ->makeLink( $target, $text, $attribs, $query );
* ->makeKnownLink( $target, $text, $attribs, $query );
* ->makeBrokenLink( $target, $text, $attribs, $query );
The order of the parameters are the same as Linker::link(), except
$options are now part of the LinkRenderer instance, and
known/broken status requires calling the function explicitly.
Additionally, instead of passing in raw $html for the link text, the
$text parameter will automatically be escaped unless it is specially
marked as safe HTML using the MediaWiki\Linker\HtmlArmor class.
The LinkBegin and LinkEnd hooks are now deprecated, but still function
for backwards-compatability. Clients should migrate to the nearly-
equivalent LinkRendererBegin and LinkRendererEnd hooks.
The main differences between the hooks are:
* Passing HtmlPageLinkRenderer object instead of deprecated DummyLinker
* Using LinkTarget instead of Title
* Begin hook can no longer change known/broken status of link. Use the
TitleIsAlwaysKnown hook for that.
* $options are no longer passed, they can be read (but shouldn't be
modified!) from the LinkRenderer object.
Bug: T469
Change-Id: I057cc86ae6404a080aa3c8e0e956ecbb10a897d5
2016-04-21 20:13:21 +00:00
|
|
|
if ( in_array( 'known', $options, true ) ) {
|
|
|
|
|
return $linkRenderer->makeKnownLink( $target, $text, $customAttribs, $query );
|
2019-02-09 07:25:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( in_array( 'broken', $options, true ) ) {
|
Add LinkRenderer (rewrite of Linker::link())
This is a rewrite of Linker::link() to a non-static, LinkTarget-based
interface. Users of plain Linker::link() with no options can use the
LinkRenderer instance provided by MediaWikiServices. Others that
have specific options should create and configure their own instance,
which can be used to create as many links as necessary.
The main entrypoints for making links are:
* ->makeLink( $target, $text, $attribs, $query );
* ->makeKnownLink( $target, $text, $attribs, $query );
* ->makeBrokenLink( $target, $text, $attribs, $query );
The order of the parameters are the same as Linker::link(), except
$options are now part of the LinkRenderer instance, and
known/broken status requires calling the function explicitly.
Additionally, instead of passing in raw $html for the link text, the
$text parameter will automatically be escaped unless it is specially
marked as safe HTML using the MediaWiki\Linker\HtmlArmor class.
The LinkBegin and LinkEnd hooks are now deprecated, but still function
for backwards-compatability. Clients should migrate to the nearly-
equivalent LinkRendererBegin and LinkRendererEnd hooks.
The main differences between the hooks are:
* Passing HtmlPageLinkRenderer object instead of deprecated DummyLinker
* Using LinkTarget instead of Title
* Begin hook can no longer change known/broken status of link. Use the
TitleIsAlwaysKnown hook for that.
* $options are no longer passed, they can be read (but shouldn't be
modified!) from the LinkRenderer object.
Bug: T469
Change-Id: I057cc86ae6404a080aa3c8e0e956ecbb10a897d5
2016-04-21 20:13:21 +00:00
|
|
|
return $linkRenderer->makeBrokenLink( $target, $text, $customAttribs, $query );
|
2019-02-09 07:25:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( in_array( 'noclasses', $options, true ) ) {
|
2016-05-25 23:37:21 +00:00
|
|
|
return $linkRenderer->makePreloadedLink( $target, $text, '', $customAttribs, $query );
|
2008-08-05 17:05:59 +00:00
|
|
|
}
|
2019-02-09 07:25:57 +00:00
|
|
|
|
|
|
|
|
return $linkRenderer->makeLink( $target, $text, $customAttribs, $query );
|
2008-07-30 21:02:28 +00:00
|
|
|
}
|
|
|
|
|
|
2009-03-29 16:02:16 +00:00
|
|
|
/**
|
|
|
|
|
* Identical to link(), except $options defaults to 'known'.
|
2016-07-12 22:26:53 +00:00
|
|
|
*
|
2016-05-04 12:48:58 +00:00
|
|
|
* @since 1.16.3
|
2016-07-12 22:26:53 +00:00
|
|
|
* @deprecated since 1.28, use MediaWiki\Linker\LinkRenderer instead
|
2014-08-24 09:19:32 +00:00
|
|
|
* @see Linker::link
|
2019-04-15 12:47:32 +00:00
|
|
|
* @param LinkTarget $target
|
2019-12-12 18:57:11 +00:00
|
|
|
* @param string|null $html
|
2017-08-11 18:04:11 +00:00
|
|
|
* @param array $customAttribs
|
|
|
|
|
* @param array $query
|
|
|
|
|
* @param string|array $options
|
2012-02-09 21:35:05 +00:00
|
|
|
* @return string
|
2009-03-29 16:02:16 +00:00
|
|
|
*/
|
2011-04-03 12:04:04 +00:00
|
|
|
public static function linkKnown(
|
2016-02-17 09:09:32 +00:00
|
|
|
$target, $html = null, $customAttribs = [],
|
2016-05-19 21:42:52 +00:00
|
|
|
$query = [], $options = [ 'known' ]
|
2013-12-01 20:39:00 +00:00
|
|
|
) {
|
2011-09-03 13:46:56 +00:00
|
|
|
return self::link( $target, $html, $customAttribs, $query, $options );
|
2009-03-29 16:02:16 +00:00
|
|
|
}
|
|
|
|
|
|
2008-04-14 07:45:50 +00:00
|
|
|
/**
|
2017-03-16 22:19:36 +00:00
|
|
|
* Make appropriate markup for a link to the current article. This is since
|
|
|
|
|
* MediaWiki 1.29.0 rendered as an <a> tag without an href and with a class
|
|
|
|
|
* showing the link text. The calling sequence is the same as for the other
|
|
|
|
|
* make*LinkObj static functions, but $query is not used.
|
2011-03-02 10:57:55 +00:00
|
|
|
*
|
2016-05-04 12:48:58 +00:00
|
|
|
* @since 1.16.3
|
2019-04-15 12:47:32 +00:00
|
|
|
* @param LinkTarget $nt
|
2021-06-17 14:32:05 +00:00
|
|
|
* @param string $html
|
|
|
|
|
* @param string $query
|
|
|
|
|
* @param string $trail
|
|
|
|
|
* @param string $prefix
|
2023-01-23 17:48:10 +00:00
|
|
|
* @param string $hash hash fragment since 1.40. Should be properly escaped using
|
|
|
|
|
* Sanitizer::escapeIdForLink before being passed to this function.
|
2012-07-10 14:58:52 +00:00
|
|
|
*
|
2011-05-06 22:53:59 +00:00
|
|
|
* @return string
|
2006-02-01 04:34:47 +00:00
|
|
|
*/
|
2023-01-06 17:37:22 +00:00
|
|
|
public static function makeSelfLinkObj( $nt, $html = '', $query = '', $trail = '', $prefix = '', $hash = '' ) {
|
2019-04-15 12:47:32 +00:00
|
|
|
$nt = Title::newFromLinkTarget( $nt );
|
2023-01-06 17:37:22 +00:00
|
|
|
$attrs = [
|
|
|
|
|
'class' => 'mw-selflink',
|
|
|
|
|
];
|
|
|
|
|
if ( $hash ) {
|
|
|
|
|
$attrs['href'] = '#' . $hash;
|
|
|
|
|
$attrs['class'] = 'mw-selflink-fragment';
|
|
|
|
|
} else {
|
|
|
|
|
// For backwards compatibility with gadgets we add selflink as well.
|
|
|
|
|
$attrs['class'] = 'mw-selflink selflink';
|
|
|
|
|
}
|
|
|
|
|
$ret = Html::rawElement( 'a', $attrs, $prefix . $html ) . $trail;
|
Hooks::run() call site migration
Migrate all callers of Hooks::run() to use the new
HookContainer/HookRunner system.
General principles:
* Use DI if it is already used. We're not changing the way state is
managed in this patch.
* HookContainer is always injected, not HookRunner. HookContainer
is a service, it's a more generic interface, it is the only
thing that provides isRegistered() which is needed in some cases,
and a HookRunner can be efficiently constructed from it
(confirmed by benchmark). Because HookContainer is needed
for object construction, it is also needed by all factories.
* "Ask your friendly local base class". Big hierarchies like
SpecialPage and ApiBase have getHookContainer() and getHookRunner()
methods in the base class, and classes that extend that base class
are not expected to know or care where the base class gets its
HookContainer from.
* ProtectedHookAccessorTrait provides protected getHookContainer() and
getHookRunner() methods, getting them from the global service
container. The point of this is to ease migration to DI by ensuring
that call sites ask their local friendly base class rather than
getting a HookRunner from the service container directly.
* Private $this->hookRunner. In some smaller classes where accessor
methods did not seem warranted, there is a private HookRunner property
which is accessed directly. Very rarely (two cases), there is a
protected property, for consistency with code that conventionally
assumes protected=private, but in cases where the class might actually
be overridden, a protected accessor is preferred over a protected
property.
* The last resort: Hooks::runner(). Mostly for static, file-scope and
global code. In a few cases it was used for objects with broken
construction schemes, out of horror or laziness.
Constructors with new required arguments:
* AuthManager
* BadFileLookup
* BlockManager
* ClassicInterwikiLookup
* ContentHandlerFactory
* ContentSecurityPolicy
* DefaultOptionsManager
* DerivedPageDataUpdater
* FullSearchResultWidget
* HtmlCacheUpdater
* LanguageFactory
* LanguageNameUtils
* LinkRenderer
* LinkRendererFactory
* LocalisationCache
* MagicWordFactory
* MessageCache
* NamespaceInfo
* PageEditStash
* PageHandlerFactory
* PageUpdater
* ParserFactory
* PermissionManager
* RevisionStore
* RevisionStoreFactory
* SearchEngineConfig
* SearchEngineFactory
* SearchFormWidget
* SearchNearMatcher
* SessionBackend
* SpecialPageFactory
* UserNameUtils
* UserOptionsManager
* WatchedItemQueryService
* WatchedItemStore
Constructors with new optional arguments:
* DefaultPreferencesFactory
* Language
* LinkHolderArray
* MovePage
* Parser
* ParserCache
* PasswordReset
* Router
setHookContainer() now required after construction:
* AuthenticationProvider
* ResourceLoaderModule
* SearchEngine
Change-Id: Id442b0dbe43aba84bd5cf801d86dedc768b082c7
2020-03-19 02:42:09 +00:00
|
|
|
if ( !Hooks::runner()->onSelfLinkBegin( $nt, $html, $trail, $prefix, $ret ) ) {
|
2014-04-06 02:06:43 +00:00
|
|
|
return $ret;
|
|
|
|
|
}
|
|
|
|
|
|
2011-09-03 13:46:56 +00:00
|
|
|
if ( $html == '' ) {
|
|
|
|
|
$html = htmlspecialchars( $nt->getPrefixedText() );
|
2004-12-18 06:29:23 +00:00
|
|
|
}
|
2022-04-01 15:58:32 +00:00
|
|
|
[ $inside, $trail ] = self::splitTrail( $trail );
|
2023-01-06 17:37:22 +00:00
|
|
|
return Html::rawElement( 'a', $attrs, $prefix . $html . $inside ) . $trail;
|
2004-12-18 06:29:23 +00:00
|
|
|
}
|
|
|
|
|
|
2012-04-10 09:28:48 +00:00
|
|
|
/**
|
|
|
|
|
* Get a message saying that an invalid title was encountered.
|
|
|
|
|
* This should be called after a method like Title::makeTitleSafe() returned
|
|
|
|
|
* a value indicating that the title object is invalid.
|
|
|
|
|
*
|
2014-04-22 11:07:02 +00:00
|
|
|
* @param IContextSource $context Context to use to get the messages
|
2013-03-11 17:15:01 +00:00
|
|
|
* @param int $namespace Namespace number
|
|
|
|
|
* @param string $title Text of the title, without the namespace part
|
2012-10-07 23:35:26 +00:00
|
|
|
* @return string
|
2012-04-10 09:28:48 +00:00
|
|
|
*/
|
|
|
|
|
public static function getInvalidTitleDescription( IContextSource $context, $namespace, $title ) {
|
|
|
|
|
// First we check whether the namespace exists or not.
|
2018-08-05 17:58:51 +00:00
|
|
|
if ( MediaWikiServices::getInstance()->getNamespaceInfo()->exists( $namespace ) ) {
|
2012-04-10 09:28:48 +00:00
|
|
|
if ( $namespace == NS_MAIN ) {
|
|
|
|
|
$name = $context->msg( 'blanknamespace' )->text();
|
|
|
|
|
} else {
|
2018-07-29 12:24:54 +00:00
|
|
|
$name = MediaWikiServices::getInstance()->getContentLanguage()->
|
|
|
|
|
getFormattedNsText( $namespace );
|
2012-04-10 09:28:48 +00:00
|
|
|
}
|
|
|
|
|
return $context->msg( 'invalidtitle-knownnamespace', $namespace, $name, $title )->text();
|
|
|
|
|
}
|
2019-02-09 07:25:57 +00:00
|
|
|
|
|
|
|
|
return $context->msg( 'invalidtitle-unknownnamespace', $namespace, $title )->text();
|
2012-04-10 09:28:48 +00:00
|
|
|
}
|
|
|
|
|
|
2010-01-07 09:32:09 +00:00
|
|
|
/**
|
2009-10-26 14:25:48 +00:00
|
|
|
* Returns the filename part of an url.
|
|
|
|
|
* Used as alternative text for external images.
|
2011-05-06 22:53:59 +00:00
|
|
|
*
|
2014-04-22 11:07:02 +00:00
|
|
|
* @param string $url
|
2011-05-06 22:53:59 +00:00
|
|
|
*
|
|
|
|
|
* @return string
|
2009-10-26 14:25:48 +00:00
|
|
|
*/
|
2011-09-12 09:25:00 +00:00
|
|
|
private static function fnamePart( $url ) {
|
2004-12-18 06:29:23 +00:00
|
|
|
$basename = strrchr( $url, '/' );
|
2018-06-30 09:43:00 +00:00
|
|
|
if ( $basename === false ) {
|
2004-12-18 06:29:23 +00:00
|
|
|
$basename = $url;
|
|
|
|
|
} else {
|
|
|
|
|
$basename = substr( $basename, 1 );
|
|
|
|
|
}
|
2008-07-02 01:19:00 +00:00
|
|
|
return $basename;
|
2004-12-18 06:29:23 +00:00
|
|
|
}
|
|
|
|
|
|
2010-01-07 09:32:09 +00:00
|
|
|
/**
|
2009-10-26 14:25:48 +00:00
|
|
|
* Return the code for images which were added via external links,
|
|
|
|
|
* via Parser::maybeMakeExternalImage().
|
2011-05-06 22:53:59 +00:00
|
|
|
*
|
2016-05-04 12:48:58 +00:00
|
|
|
* @since 1.16.3
|
2014-04-22 11:07:02 +00:00
|
|
|
* @param string $url
|
|
|
|
|
* @param string $alt
|
2011-05-06 22:53:59 +00:00
|
|
|
*
|
|
|
|
|
* @return string
|
2009-10-26 14:25:48 +00:00
|
|
|
*/
|
2011-09-12 09:25:00 +00:00
|
|
|
public static function makeExternalImage( $url, $alt = '' ) {
|
2010-01-06 19:59:42 +00:00
|
|
|
if ( $alt == '' ) {
|
2011-04-03 11:44:11 +00:00
|
|
|
$alt = self::fnamePart( $url );
|
2004-12-18 06:29:23 +00:00
|
|
|
}
|
2008-06-24 14:32:49 +00:00
|
|
|
$img = '';
|
Hooks::run() call site migration
Migrate all callers of Hooks::run() to use the new
HookContainer/HookRunner system.
General principles:
* Use DI if it is already used. We're not changing the way state is
managed in this patch.
* HookContainer is always injected, not HookRunner. HookContainer
is a service, it's a more generic interface, it is the only
thing that provides isRegistered() which is needed in some cases,
and a HookRunner can be efficiently constructed from it
(confirmed by benchmark). Because HookContainer is needed
for object construction, it is also needed by all factories.
* "Ask your friendly local base class". Big hierarchies like
SpecialPage and ApiBase have getHookContainer() and getHookRunner()
methods in the base class, and classes that extend that base class
are not expected to know or care where the base class gets its
HookContainer from.
* ProtectedHookAccessorTrait provides protected getHookContainer() and
getHookRunner() methods, getting them from the global service
container. The point of this is to ease migration to DI by ensuring
that call sites ask their local friendly base class rather than
getting a HookRunner from the service container directly.
* Private $this->hookRunner. In some smaller classes where accessor
methods did not seem warranted, there is a private HookRunner property
which is accessed directly. Very rarely (two cases), there is a
protected property, for consistency with code that conventionally
assumes protected=private, but in cases where the class might actually
be overridden, a protected accessor is preferred over a protected
property.
* The last resort: Hooks::runner(). Mostly for static, file-scope and
global code. In a few cases it was used for objects with broken
construction schemes, out of horror or laziness.
Constructors with new required arguments:
* AuthManager
* BadFileLookup
* BlockManager
* ClassicInterwikiLookup
* ContentHandlerFactory
* ContentSecurityPolicy
* DefaultOptionsManager
* DerivedPageDataUpdater
* FullSearchResultWidget
* HtmlCacheUpdater
* LanguageFactory
* LanguageNameUtils
* LinkRenderer
* LinkRendererFactory
* LocalisationCache
* MagicWordFactory
* MessageCache
* NamespaceInfo
* PageEditStash
* PageHandlerFactory
* PageUpdater
* ParserFactory
* PermissionManager
* RevisionStore
* RevisionStoreFactory
* SearchEngineConfig
* SearchEngineFactory
* SearchFormWidget
* SearchNearMatcher
* SessionBackend
* SpecialPageFactory
* UserNameUtils
* UserOptionsManager
* WatchedItemQueryService
* WatchedItemStore
Constructors with new optional arguments:
* DefaultPreferencesFactory
* Language
* LinkHolderArray
* MovePage
* Parser
* ParserCache
* PasswordReset
* Router
setHookContainer() now required after construction:
* AuthenticationProvider
* ResourceLoaderModule
* SearchEngine
Change-Id: Id442b0dbe43aba84bd5cf801d86dedc768b082c7
2020-03-19 02:42:09 +00:00
|
|
|
$success = Hooks::runner()->onLinkerMakeExternalImage( $url, $alt, $img );
|
2010-08-26 19:40:29 +00:00
|
|
|
if ( !$success ) {
|
2014-05-15 15:38:28 +00:00
|
|
|
wfDebug( "Hook LinkerMakeExternalImage changed the output of external image "
|
2020-06-01 05:00:39 +00:00
|
|
|
. "with url {$url} and alt text {$alt} to {$img}" );
|
2008-06-24 14:32:49 +00:00
|
|
|
return $img;
|
|
|
|
|
}
|
2009-08-21 20:39:35 +00:00
|
|
|
return Html::element( 'img',
|
2016-02-17 09:09:32 +00:00
|
|
|
[
|
2008-07-02 01:19:00 +00:00
|
|
|
'src' => $url,
|
2019-02-09 07:25:57 +00:00
|
|
|
'alt' => $alt
|
|
|
|
|
]
|
|
|
|
|
);
|
2004-12-18 06:29:23 +00:00
|
|
|
}
|
|
|
|
|
|
2008-04-14 07:45:50 +00:00
|
|
|
/**
|
2009-06-08 22:33:37 +00:00
|
|
|
* Given parameters derived from [[Image:Foo|options...]], generate the
|
|
|
|
|
* HTML that that syntax inserts in the page.
|
2008-02-26 17:20:39 +00:00
|
|
|
*
|
2014-04-22 11:07:02 +00:00
|
|
|
* @param Parser $parser
|
2019-04-15 12:47:32 +00:00
|
|
|
* @param LinkTarget $title LinkTarget object of the file (not the currently viewed page)
|
2021-10-21 18:09:51 +00:00
|
|
|
* @param File|false $file File object, or false if it doesn't exist
|
2014-04-22 11:07:02 +00:00
|
|
|
* @param array $frameParams Associative array of parameters external to the media handler.
|
2008-04-14 07:45:50 +00:00
|
|
|
* Boolean parameters are indicated by presence or absence, the value is arbitrary and
|
2008-01-03 19:28:50 +00:00
|
|
|
* will often be false.
|
|
|
|
|
* thumbnail If present, downscale and frame
|
|
|
|
|
* manualthumb Image name to use as a thumbnail, instead of automatic scaling
|
|
|
|
|
* framed Shows image in original size in a frame
|
|
|
|
|
* frameless Downscale but don't frame
|
|
|
|
|
* upright If present, tweak default sizes for portrait orientation
|
|
|
|
|
* upright_factor Fudge factor for "upright" tweak (default 0.75)
|
|
|
|
|
* border If present, show a border around the image
|
|
|
|
|
* align Horizontal alignment (left, right, center, none)
|
2008-04-14 07:45:50 +00:00
|
|
|
* valign Vertical alignment (baseline, sub, super, top, text-top, middle,
|
2008-01-03 19:28:50 +00:00
|
|
|
* bottom, text-bottom)
|
|
|
|
|
* alt Alternate text for image (i.e. alt attribute). Plain text.
|
2022-05-09 20:35:04 +00:00
|
|
|
* title Used for tooltips if caption isn't visible.
|
2012-08-20 10:12:47 +00:00
|
|
|
* class HTML for image classes. Plain text.
|
2008-01-03 19:28:50 +00:00
|
|
|
* caption HTML for image caption.
|
2008-10-07 00:31:26 +00:00
|
|
|
* link-url URL to link to
|
2019-04-15 12:47:32 +00:00
|
|
|
* link-title LinkTarget object to link to
|
2013-03-13 07:42:41 +00:00
|
|
|
* link-target Value for the target attribute, only with link-url
|
2008-10-06 05:55:27 +00:00
|
|
|
* no-link Boolean, suppress description link
|
Basic integrated audio/video support, with Ogg implementation.
* JavaScript video player based loosely on Greg Maxwell's player
* Image page text snippet customisation
* Abstraction of transform parameters in the parser. Introduced Linker::makeImageLink2().
* Made canRender(), mustRender() depend on file, not just on handler. Moved width=0, height=0 checking to ImageHandler::canRender(), since audio streams have width=height=0 but should be rendered.
Also:
* Automatic upgrade for oldimage rows on image page view, allows media handler selection based on oi_*_mime
* oi_*_mime unconditionally referenced, REQUIRES SCHEMA UPGRADE
* Don't destroy file info for missing files on upgrade
* Simple, centralised extension message file handling
* Made MessageCache::loadAllMessages non-static, optimised for repeated-call case due to abuse in User.php
* Support for lightweight parser output hooks, with callback whitelist for security
* Moved Linker::formatSize() to Language, to join the new formatTimePeriod() and formatBitrate()
* Introduced MagicWordArray, regex capture trick requires that magic word IDs DO NOT CONTAIN HYPHENS.
2007-08-15 10:50:09 +00:00
|
|
|
*
|
2014-04-08 15:29:17 +00:00
|
|
|
* @param array $handlerParams Associative array of media handler parameters, to be passed
|
2008-04-14 07:45:50 +00:00
|
|
|
* to transform(). Typical keys are "width" and "page".
|
2022-03-14 22:51:23 +00:00
|
|
|
* targetlang (optional) Target language code, see Parser::getTargetLanguage()
|
2021-10-21 18:09:51 +00:00
|
|
|
* @param string|false $time Timestamp of the file, set as false for current
|
2014-04-08 15:29:17 +00:00
|
|
|
* @param string $query Query params for desc url
|
|
|
|
|
* @param int|null $widthOption Used by the parser to remember the user preference thumbnailsize
|
2012-08-01 03:34:32 +00:00
|
|
|
* @since 1.20
|
2014-04-08 15:29:17 +00:00
|
|
|
* @return string HTML for an image, with links, wrappers, etc.
|
Basic integrated audio/video support, with Ogg implementation.
* JavaScript video player based loosely on Greg Maxwell's player
* Image page text snippet customisation
* Abstraction of transform parameters in the parser. Introduced Linker::makeImageLink2().
* Made canRender(), mustRender() depend on file, not just on handler. Moved width=0, height=0 checking to ImageHandler::canRender(), since audio streams have width=height=0 but should be rendered.
Also:
* Automatic upgrade for oldimage rows on image page view, allows media handler selection based on oi_*_mime
* oi_*_mime unconditionally referenced, REQUIRES SCHEMA UPGRADE
* Don't destroy file info for missing files on upgrade
* Simple, centralised extension message file handling
* Made MessageCache::loadAllMessages non-static, optimised for repeated-call case due to abuse in User.php
* Support for lightweight parser output hooks, with callback whitelist for security
* Moved Linker::formatSize() to Language, to join the new formatTimePeriod() and formatBitrate()
* Introduced MagicWordArray, regex capture trick requires that magic word IDs DO NOT CONTAIN HYPHENS.
2007-08-15 10:50:09 +00:00
|
|
|
*/
|
2019-04-15 12:47:32 +00:00
|
|
|
public static function makeImageLink( Parser $parser, LinkTarget $title,
|
2016-02-17 09:09:32 +00:00
|
|
|
$file, $frameParams = [], $handlerParams = [], $time = false,
|
2022-08-23 08:38:49 +00:00
|
|
|
$query = '', $widthOption = null
|
2013-12-01 20:39:00 +00:00
|
|
|
) {
|
2019-04-15 12:47:32 +00:00
|
|
|
$title = Title::newFromLinkTarget( $title );
|
2008-02-26 17:20:39 +00:00
|
|
|
$res = null;
|
2011-04-03 12:04:04 +00:00
|
|
|
$dummy = new DummyLinker;
|
Hooks::run() call site migration
Migrate all callers of Hooks::run() to use the new
HookContainer/HookRunner system.
General principles:
* Use DI if it is already used. We're not changing the way state is
managed in this patch.
* HookContainer is always injected, not HookRunner. HookContainer
is a service, it's a more generic interface, it is the only
thing that provides isRegistered() which is needed in some cases,
and a HookRunner can be efficiently constructed from it
(confirmed by benchmark). Because HookContainer is needed
for object construction, it is also needed by all factories.
* "Ask your friendly local base class". Big hierarchies like
SpecialPage and ApiBase have getHookContainer() and getHookRunner()
methods in the base class, and classes that extend that base class
are not expected to know or care where the base class gets its
HookContainer from.
* ProtectedHookAccessorTrait provides protected getHookContainer() and
getHookRunner() methods, getting them from the global service
container. The point of this is to ease migration to DI by ensuring
that call sites ask their local friendly base class rather than
getting a HookRunner from the service container directly.
* Private $this->hookRunner. In some smaller classes where accessor
methods did not seem warranted, there is a private HookRunner property
which is accessed directly. Very rarely (two cases), there is a
protected property, for consistency with code that conventionally
assumes protected=private, but in cases where the class might actually
be overridden, a protected accessor is preferred over a protected
property.
* The last resort: Hooks::runner(). Mostly for static, file-scope and
global code. In a few cases it was used for objects with broken
construction schemes, out of horror or laziness.
Constructors with new required arguments:
* AuthManager
* BadFileLookup
* BlockManager
* ClassicInterwikiLookup
* ContentHandlerFactory
* ContentSecurityPolicy
* DefaultOptionsManager
* DerivedPageDataUpdater
* FullSearchResultWidget
* HtmlCacheUpdater
* LanguageFactory
* LanguageNameUtils
* LinkRenderer
* LinkRendererFactory
* LocalisationCache
* MagicWordFactory
* MessageCache
* NamespaceInfo
* PageEditStash
* PageHandlerFactory
* PageUpdater
* ParserFactory
* PermissionManager
* RevisionStore
* RevisionStoreFactory
* SearchEngineConfig
* SearchEngineFactory
* SearchFormWidget
* SearchNearMatcher
* SessionBackend
* SpecialPageFactory
* UserNameUtils
* UserOptionsManager
* WatchedItemQueryService
* WatchedItemStore
Constructors with new optional arguments:
* DefaultPreferencesFactory
* Language
* LinkHolderArray
* MovePage
* Parser
* ParserCache
* PasswordReset
* Router
setHookContainer() now required after construction:
* AuthenticationProvider
* ResourceLoaderModule
* SearchEngine
Change-Id: Id442b0dbe43aba84bd5cf801d86dedc768b082c7
2020-03-19 02:42:09 +00:00
|
|
|
if ( !Hooks::runner()->onImageBeforeProduceHTML( $dummy, $title,
|
2021-10-25 19:15:52 +00:00
|
|
|
// @phan-suppress-next-line PhanTypeMismatchArgument Type mismatch on pass-by-ref args
|
Hooks::run() call site migration
Migrate all callers of Hooks::run() to use the new
HookContainer/HookRunner system.
General principles:
* Use DI if it is already used. We're not changing the way state is
managed in this patch.
* HookContainer is always injected, not HookRunner. HookContainer
is a service, it's a more generic interface, it is the only
thing that provides isRegistered() which is needed in some cases,
and a HookRunner can be efficiently constructed from it
(confirmed by benchmark). Because HookContainer is needed
for object construction, it is also needed by all factories.
* "Ask your friendly local base class". Big hierarchies like
SpecialPage and ApiBase have getHookContainer() and getHookRunner()
methods in the base class, and classes that extend that base class
are not expected to know or care where the base class gets its
HookContainer from.
* ProtectedHookAccessorTrait provides protected getHookContainer() and
getHookRunner() methods, getting them from the global service
container. The point of this is to ease migration to DI by ensuring
that call sites ask their local friendly base class rather than
getting a HookRunner from the service container directly.
* Private $this->hookRunner. In some smaller classes where accessor
methods did not seem warranted, there is a private HookRunner property
which is accessed directly. Very rarely (two cases), there is a
protected property, for consistency with code that conventionally
assumes protected=private, but in cases where the class might actually
be overridden, a protected accessor is preferred over a protected
property.
* The last resort: Hooks::runner(). Mostly for static, file-scope and
global code. In a few cases it was used for objects with broken
construction schemes, out of horror or laziness.
Constructors with new required arguments:
* AuthManager
* BadFileLookup
* BlockManager
* ClassicInterwikiLookup
* ContentHandlerFactory
* ContentSecurityPolicy
* DefaultOptionsManager
* DerivedPageDataUpdater
* FullSearchResultWidget
* HtmlCacheUpdater
* LanguageFactory
* LanguageNameUtils
* LinkRenderer
* LinkRendererFactory
* LocalisationCache
* MagicWordFactory
* MessageCache
* NamespaceInfo
* PageEditStash
* PageHandlerFactory
* PageUpdater
* ParserFactory
* PermissionManager
* RevisionStore
* RevisionStoreFactory
* SearchEngineConfig
* SearchEngineFactory
* SearchFormWidget
* SearchNearMatcher
* SessionBackend
* SpecialPageFactory
* UserNameUtils
* UserOptionsManager
* WatchedItemQueryService
* WatchedItemStore
Constructors with new optional arguments:
* DefaultPreferencesFactory
* Language
* LinkHolderArray
* MovePage
* Parser
* ParserCache
* PasswordReset
* Router
setHookContainer() now required after construction:
* AuthenticationProvider
* ResourceLoaderModule
* SearchEngine
Change-Id: Id442b0dbe43aba84bd5cf801d86dedc768b082c7
2020-03-19 02:42:09 +00:00
|
|
|
$file, $frameParams, $handlerParams, $time, $res,
|
2021-10-25 19:15:52 +00:00
|
|
|
// @phan-suppress-next-line PhanTypeMismatchArgument Type mismatch on pass-by-ref args
|
Hooks::run() call site migration
Migrate all callers of Hooks::run() to use the new
HookContainer/HookRunner system.
General principles:
* Use DI if it is already used. We're not changing the way state is
managed in this patch.
* HookContainer is always injected, not HookRunner. HookContainer
is a service, it's a more generic interface, it is the only
thing that provides isRegistered() which is needed in some cases,
and a HookRunner can be efficiently constructed from it
(confirmed by benchmark). Because HookContainer is needed
for object construction, it is also needed by all factories.
* "Ask your friendly local base class". Big hierarchies like
SpecialPage and ApiBase have getHookContainer() and getHookRunner()
methods in the base class, and classes that extend that base class
are not expected to know or care where the base class gets its
HookContainer from.
* ProtectedHookAccessorTrait provides protected getHookContainer() and
getHookRunner() methods, getting them from the global service
container. The point of this is to ease migration to DI by ensuring
that call sites ask their local friendly base class rather than
getting a HookRunner from the service container directly.
* Private $this->hookRunner. In some smaller classes where accessor
methods did not seem warranted, there is a private HookRunner property
which is accessed directly. Very rarely (two cases), there is a
protected property, for consistency with code that conventionally
assumes protected=private, but in cases where the class might actually
be overridden, a protected accessor is preferred over a protected
property.
* The last resort: Hooks::runner(). Mostly for static, file-scope and
global code. In a few cases it was used for objects with broken
construction schemes, out of horror or laziness.
Constructors with new required arguments:
* AuthManager
* BadFileLookup
* BlockManager
* ClassicInterwikiLookup
* ContentHandlerFactory
* ContentSecurityPolicy
* DefaultOptionsManager
* DerivedPageDataUpdater
* FullSearchResultWidget
* HtmlCacheUpdater
* LanguageFactory
* LanguageNameUtils
* LinkRenderer
* LinkRendererFactory
* LocalisationCache
* MagicWordFactory
* MessageCache
* NamespaceInfo
* PageEditStash
* PageHandlerFactory
* PageUpdater
* ParserFactory
* PermissionManager
* RevisionStore
* RevisionStoreFactory
* SearchEngineConfig
* SearchEngineFactory
* SearchFormWidget
* SearchNearMatcher
* SessionBackend
* SpecialPageFactory
* UserNameUtils
* UserOptionsManager
* WatchedItemQueryService
* WatchedItemStore
Constructors with new optional arguments:
* DefaultPreferencesFactory
* Language
* LinkHolderArray
* MovePage
* Parser
* ParserCache
* PasswordReset
* Router
setHookContainer() now required after construction:
* AuthenticationProvider
* ResourceLoaderModule
* SearchEngine
Change-Id: Id442b0dbe43aba84bd5cf801d86dedc768b082c7
2020-03-19 02:42:09 +00:00
|
|
|
$parser, $query, $widthOption )
|
|
|
|
|
) {
|
2008-02-26 17:20:39 +00:00
|
|
|
return $res;
|
|
|
|
|
}
|
|
|
|
|
|
Basic integrated audio/video support, with Ogg implementation.
* JavaScript video player based loosely on Greg Maxwell's player
* Image page text snippet customisation
* Abstraction of transform parameters in the parser. Introduced Linker::makeImageLink2().
* Made canRender(), mustRender() depend on file, not just on handler. Moved width=0, height=0 checking to ImageHandler::canRender(), since audio streams have width=height=0 but should be rendered.
Also:
* Automatic upgrade for oldimage rows on image page view, allows media handler selection based on oi_*_mime
* oi_*_mime unconditionally referenced, REQUIRES SCHEMA UPGRADE
* Don't destroy file info for missing files on upgrade
* Simple, centralised extension message file handling
* Made MessageCache::loadAllMessages non-static, optimised for repeated-call case due to abuse in User.php
* Support for lightweight parser output hooks, with callback whitelist for security
* Moved Linker::formatSize() to Language, to join the new formatTimePeriod() and formatBitrate()
* Introduced MagicWordArray, regex capture trick requires that magic word IDs DO NOT CONTAIN HYPHENS.
2007-08-15 10:50:09 +00:00
|
|
|
if ( $file && !$file->allowInlineDisplay() ) {
|
2022-08-23 08:38:49 +00:00
|
|
|
wfDebug( __METHOD__ . ': ' . $title->getPrefixedDBkey() . ' does not allow inline display' );
|
2011-04-03 11:44:11 +00:00
|
|
|
return self::link( $title );
|
Basic integrated audio/video support, with Ogg implementation.
* JavaScript video player based loosely on Greg Maxwell's player
* Image page text snippet customisation
* Abstraction of transform parameters in the parser. Introduced Linker::makeImageLink2().
* Made canRender(), mustRender() depend on file, not just on handler. Moved width=0, height=0 checking to ImageHandler::canRender(), since audio streams have width=height=0 but should be rendered.
Also:
* Automatic upgrade for oldimage rows on image page view, allows media handler selection based on oi_*_mime
* oi_*_mime unconditionally referenced, REQUIRES SCHEMA UPGRADE
* Don't destroy file info for missing files on upgrade
* Simple, centralised extension message file handling
* Made MessageCache::loadAllMessages non-static, optimised for repeated-call case due to abuse in User.php
* Support for lightweight parser output hooks, with callback whitelist for security
* Moved Linker::formatSize() to Language, to join the new formatTimePeriod() and formatBitrate()
* Introduced MagicWordArray, regex capture trick requires that magic word IDs DO NOT CONTAIN HYPHENS.
2007-08-15 10:50:09 +00:00
|
|
|
}
|
2006-01-07 13:31:29 +00:00
|
|
|
|
Basic integrated audio/video support, with Ogg implementation.
* JavaScript video player based loosely on Greg Maxwell's player
* Image page text snippet customisation
* Abstraction of transform parameters in the parser. Introduced Linker::makeImageLink2().
* Made canRender(), mustRender() depend on file, not just on handler. Moved width=0, height=0 checking to ImageHandler::canRender(), since audio streams have width=height=0 but should be rendered.
Also:
* Automatic upgrade for oldimage rows on image page view, allows media handler selection based on oi_*_mime
* oi_*_mime unconditionally referenced, REQUIRES SCHEMA UPGRADE
* Don't destroy file info for missing files on upgrade
* Simple, centralised extension message file handling
* Made MessageCache::loadAllMessages non-static, optimised for repeated-call case due to abuse in User.php
* Support for lightweight parser output hooks, with callback whitelist for security
* Moved Linker::formatSize() to Language, to join the new formatTimePeriod() and formatBitrate()
* Introduced MagicWordArray, regex capture trick requires that magic word IDs DO NOT CONTAIN HYPHENS.
2007-08-15 10:50:09 +00:00
|
|
|
// Clean up parameters
|
2017-10-06 22:17:58 +00:00
|
|
|
$page = $handlerParams['page'] ?? false;
|
2016-08-25 07:21:26 +00:00
|
|
|
if ( !isset( $frameParams['align'] ) ) {
|
|
|
|
|
$frameParams['align'] = '';
|
2011-03-02 10:57:55 +00:00
|
|
|
}
|
2016-08-25 07:21:26 +00:00
|
|
|
if ( !isset( $frameParams['title'] ) ) {
|
|
|
|
|
$frameParams['title'] = '';
|
2011-03-02 10:57:55 +00:00
|
|
|
}
|
2016-08-25 07:21:26 +00:00
|
|
|
if ( !isset( $frameParams['class'] ) ) {
|
|
|
|
|
$frameParams['class'] = '';
|
2012-08-20 10:12:47 +00:00
|
|
|
}
|
2006-01-03 00:51:57 +00:00
|
|
|
|
2018-02-13 22:51:22 +00:00
|
|
|
$services = MediaWikiServices::getInstance();
|
2022-01-18 23:53:32 +00:00
|
|
|
$config = $services->getMainConfig();
|
2022-04-01 15:58:32 +00:00
|
|
|
$enableLegacyMediaDOM = $config->get( MainConfigNames::ParserEnableLegacyMediaDOM );
|
2018-02-13 22:51:22 +00:00
|
|
|
|
|
|
|
|
$classes = [];
|
2022-04-13 01:21:10 +00:00
|
|
|
if (
|
|
|
|
|
!isset( $handlerParams['width'] ) &&
|
|
|
|
|
!isset( $frameParams['manualthumb'] ) &&
|
|
|
|
|
!isset( $frameParams['framed'] )
|
|
|
|
|
) {
|
2018-02-13 22:51:22 +00:00
|
|
|
$classes[] = 'mw-default-size';
|
|
|
|
|
}
|
|
|
|
|
|
Basic integrated audio/video support, with Ogg implementation.
* JavaScript video player based loosely on Greg Maxwell's player
* Image page text snippet customisation
* Abstraction of transform parameters in the parser. Introduced Linker::makeImageLink2().
* Made canRender(), mustRender() depend on file, not just on handler. Moved width=0, height=0 checking to ImageHandler::canRender(), since audio streams have width=height=0 but should be rendered.
Also:
* Automatic upgrade for oldimage rows on image page view, allows media handler selection based on oi_*_mime
* oi_*_mime unconditionally referenced, REQUIRES SCHEMA UPGRADE
* Don't destroy file info for missing files on upgrade
* Simple, centralised extension message file handling
* Made MessageCache::loadAllMessages non-static, optimised for repeated-call case due to abuse in User.php
* Support for lightweight parser output hooks, with callback whitelist for security
* Moved Linker::formatSize() to Language, to join the new formatTimePeriod() and formatBitrate()
* Introduced MagicWordArray, regex capture trick requires that magic word IDs DO NOT CONTAIN HYPHENS.
2007-08-15 10:50:09 +00:00
|
|
|
$prefix = $postfix = '';
|
2006-01-07 13:31:29 +00:00
|
|
|
|
2018-02-13 22:51:22 +00:00
|
|
|
if ( $enableLegacyMediaDOM ) {
|
|
|
|
|
if ( $frameParams['align'] == 'center' ) {
|
|
|
|
|
$prefix = '<div class="center">';
|
|
|
|
|
$postfix = '</div>';
|
|
|
|
|
$frameParams['align'] = 'none';
|
|
|
|
|
}
|
2004-12-18 06:29:23 +00:00
|
|
|
}
|
2018-02-13 22:51:22 +00:00
|
|
|
|
2016-08-25 07:21:26 +00:00
|
|
|
if ( $file && !isset( $handlerParams['width'] ) ) {
|
|
|
|
|
if ( isset( $handlerParams['height'] ) && $file->isVectorized() ) {
|
2011-05-14 23:48:59 +00:00
|
|
|
// If its a vector image, and user only specifies height
|
|
|
|
|
// we don't want it to be limited by its "normal" width.
|
2022-04-01 15:58:32 +00:00
|
|
|
$svgMaxSize = $config->get( MainConfigNames::SVGMaxSize );
|
2022-01-06 18:44:56 +00:00
|
|
|
$handlerParams['width'] = $svgMaxSize;
|
2011-06-17 16:03:52 +00:00
|
|
|
} else {
|
2016-08-25 07:21:26 +00:00
|
|
|
$handlerParams['width'] = $file->getWidth( $page );
|
2011-05-14 23:48:59 +00:00
|
|
|
}
|
Basic integrated audio/video support, with Ogg implementation.
* JavaScript video player based loosely on Greg Maxwell's player
* Image page text snippet customisation
* Abstraction of transform parameters in the parser. Introduced Linker::makeImageLink2().
* Made canRender(), mustRender() depend on file, not just on handler. Moved width=0, height=0 checking to ImageHandler::canRender(), since audio streams have width=height=0 but should be rendered.
Also:
* Automatic upgrade for oldimage rows on image page view, allows media handler selection based on oi_*_mime
* oi_*_mime unconditionally referenced, REQUIRES SCHEMA UPGRADE
* Don't destroy file info for missing files on upgrade
* Simple, centralised extension message file handling
* Made MessageCache::loadAllMessages non-static, optimised for repeated-call case due to abuse in User.php
* Support for lightweight parser output hooks, with callback whitelist for security
* Moved Linker::formatSize() to Language, to join the new formatTimePeriod() and formatBitrate()
* Introduced MagicWordArray, regex capture trick requires that magic word IDs DO NOT CONTAIN HYPHENS.
2007-08-15 10:50:09 +00:00
|
|
|
|
2016-08-25 07:21:26 +00:00
|
|
|
if ( isset( $frameParams['thumbnail'] )
|
|
|
|
|
|| isset( $frameParams['manualthumb'] )
|
|
|
|
|
|| isset( $frameParams['framed'] )
|
|
|
|
|
|| isset( $frameParams['frameless'] )
|
|
|
|
|
|| !$handlerParams['width']
|
2014-05-15 15:38:28 +00:00
|
|
|
) {
|
2022-04-01 15:58:32 +00:00
|
|
|
$thumbLimits = $config->get( MainConfigNames::ThumbLimits );
|
|
|
|
|
$thumbUpright = $config->get( MainConfigNames::ThumbUpright );
|
2022-01-06 18:44:56 +00:00
|
|
|
if ( $widthOption === null || !isset( $thumbLimits[$widthOption] ) ) {
|
2022-01-18 23:53:32 +00:00
|
|
|
$userOptionsLookup = $services->getUserOptionsLookup();
|
2021-03-16 15:16:18 +00:00
|
|
|
$widthOption = $userOptionsLookup->getDefaultOption( 'thumbsize' );
|
2007-04-20 19:24:53 +00:00
|
|
|
}
|
2007-04-20 12:31:36 +00:00
|
|
|
|
2007-05-21 18:56:03 +00:00
|
|
|
// Reduce width for upright images when parameter 'upright' is used
|
2016-08-25 07:21:26 +00:00
|
|
|
if ( isset( $frameParams['upright'] ) && $frameParams['upright'] == 0 ) {
|
2022-01-06 18:44:56 +00:00
|
|
|
$frameParams['upright'] = $thumbUpright;
|
2007-05-21 18:56:03 +00:00
|
|
|
}
|
2014-05-15 15:38:28 +00:00
|
|
|
|
|
|
|
|
// For caching health: If width scaled down due to upright
|
|
|
|
|
// parameter, round to full __0 pixel to avoid the creation of a
|
|
|
|
|
// lot of odd thumbs.
|
2016-08-25 07:21:26 +00:00
|
|
|
$prefWidth = isset( $frameParams['upright'] ) ?
|
2022-01-06 18:44:56 +00:00
|
|
|
round( $thumbLimits[$widthOption] * $frameParams['upright'], -1 ) :
|
|
|
|
|
$thumbLimits[$widthOption];
|
2010-10-31 21:53:29 +00:00
|
|
|
|
2014-06-02 01:18:16 +00:00
|
|
|
// Use width which is smaller: real image width or user preference width
|
2010-10-31 21:53:29 +00:00
|
|
|
// Unless image is scalable vector.
|
2016-08-25 07:21:26 +00:00
|
|
|
if ( !isset( $handlerParams['height'] ) && ( $handlerParams['width'] <= 0 ||
|
|
|
|
|
$prefWidth < $handlerParams['width'] || $file->isVectorized() ) ) {
|
|
|
|
|
$handlerParams['width'] = $prefWidth;
|
Basic integrated audio/video support, with Ogg implementation.
* JavaScript video player based loosely on Greg Maxwell's player
* Image page text snippet customisation
* Abstraction of transform parameters in the parser. Introduced Linker::makeImageLink2().
* Made canRender(), mustRender() depend on file, not just on handler. Moved width=0, height=0 checking to ImageHandler::canRender(), since audio streams have width=height=0 but should be rendered.
Also:
* Automatic upgrade for oldimage rows on image page view, allows media handler selection based on oi_*_mime
* oi_*_mime unconditionally referenced, REQUIRES SCHEMA UPGRADE
* Don't destroy file info for missing files on upgrade
* Simple, centralised extension message file handling
* Made MessageCache::loadAllMessages non-static, optimised for repeated-call case due to abuse in User.php
* Support for lightweight parser output hooks, with callback whitelist for security
* Moved Linker::formatSize() to Language, to join the new formatTimePeriod() and formatBitrate()
* Introduced MagicWordArray, regex capture trick requires that magic word IDs DO NOT CONTAIN HYPHENS.
2007-08-15 10:50:09 +00:00
|
|
|
}
|
2007-04-20 19:24:53 +00:00
|
|
|
}
|
2007-04-20 12:31:36 +00:00
|
|
|
}
|
|
|
|
|
|
2022-05-09 20:35:04 +00:00
|
|
|
// Parser::makeImage has a similarly named variable
|
|
|
|
|
$hasVisibleCaption = isset( $frameParams['thumbnail'] ) ||
|
|
|
|
|
isset( $frameParams['manualthumb'] ) ||
|
|
|
|
|
isset( $frameParams['framed'] );
|
|
|
|
|
|
|
|
|
|
if ( $hasVisibleCaption ) {
|
2018-02-13 22:51:22 +00:00
|
|
|
if ( $enableLegacyMediaDOM ) {
|
|
|
|
|
// This is no longer needed in our new media output, since the
|
2021-09-30 01:02:36 +00:00
|
|
|
// default styling in content.media-common.less takes care of it;
|
2018-02-13 22:51:22 +00:00
|
|
|
// see T269704.
|
|
|
|
|
|
|
|
|
|
# Create a thumbnail. Alignment depends on the writing direction of
|
|
|
|
|
# the page content language (right-aligned for LTR languages,
|
|
|
|
|
# left-aligned for RTL languages)
|
|
|
|
|
# If a thumbnail width has not been provided, it is set
|
|
|
|
|
# to the default user option as specified in Language*.php
|
|
|
|
|
if ( $frameParams['align'] == '' ) {
|
|
|
|
|
$frameParams['align'] = $parser->getTargetLanguage()->alignEnd();
|
|
|
|
|
}
|
2004-12-18 06:29:23 +00:00
|
|
|
}
|
2022-02-18 00:10:12 +00:00
|
|
|
return $prefix . self::makeThumbLink2(
|
|
|
|
|
$title, $file, $frameParams, $handlerParams, $time, $query,
|
|
|
|
|
$classes, $parser
|
|
|
|
|
) . $postfix;
|
2005-08-16 13:27:00 +00:00
|
|
|
}
|
2004-12-18 06:29:23 +00:00
|
|
|
|
Emit mw:File typeof for media
This matches Parsoid commit I676a88d80e2739ba69d4e7646de56fb87ae3e8e8
A few reasons for this change:
* It's less painful to do this now than after the legacy parser is made
to match Parsoid's output and more stuff depends on it.
* It simplifies the queries that are necessary to collect the media on a
page, which makes it both easier to work with and requires shipping
less bytes.
* The nested img|audio|video tags retain the distinction, so there's
no loss of information.
* mw:File is a closer match to the [[File:Hiho.jpg]] syntax that
generates these elements.
* For broken media, it's unclear which type should be applied and
mw:File obviates the question.
* For manualthumb, it's unclear which type to choose if, say, the file is
a video but we've rendered an image. For now, we've gone with what's
actually rendered in the page, but again mw:File avoids the issue.
And yes, for this special case a poster could be used, that's T121617
* There's precedent in MediaWiki, where Image: redirects to the File:
namespace. We should have followed that lead earlier instead of
introducing mw:Audio and mw:Video.
Bug: T273505
Depends-On: I9f36ef8bc3023195dd31e52f04e8f0f0f792d311
Depends-On: I0b11463b802b898429a451378f15987700752e0e
Change-Id: I719a367622b3887cfea3032582a767c2c38d0f87
2022-05-19 20:43:00 +00:00
|
|
|
$rdfaType = 'mw:File';
|
2018-02-13 22:51:22 +00:00
|
|
|
|
2016-08-25 07:21:26 +00:00
|
|
|
if ( $file && isset( $frameParams['frameless'] ) ) {
|
2018-02-13 22:51:22 +00:00
|
|
|
$rdfaType .= '/Frameless';
|
2007-11-20 12:11:05 +00:00
|
|
|
$srcWidth = $file->getWidth( $page );
|
2014-05-15 15:38:28 +00:00
|
|
|
# For "frameless" option: do not present an image bigger than the
|
|
|
|
|
# source (for bitmap-style images). This is the same behavior as the
|
|
|
|
|
# "thumb" option does it already.
|
2016-08-25 07:21:26 +00:00
|
|
|
if ( $srcWidth && !$file->mustRender() && $handlerParams['width'] > $srcWidth ) {
|
|
|
|
|
$handlerParams['width'] = $srcWidth;
|
2007-11-20 12:11:05 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-25 07:21:26 +00:00
|
|
|
if ( $file && isset( $handlerParams['width'] ) ) {
|
2007-04-20 12:31:36 +00:00
|
|
|
# Create a resized image, without the additional thumbnail features
|
2016-08-25 07:21:26 +00:00
|
|
|
$thumb = $file->transform( $handlerParams );
|
2005-07-04 19:20:53 +00:00
|
|
|
} else {
|
2007-04-20 12:31:36 +00:00
|
|
|
$thumb = false;
|
2004-12-18 06:29:23 +00:00
|
|
|
}
|
|
|
|
|
|
2007-04-20 12:31:36 +00:00
|
|
|
if ( !$thumb ) {
|
2018-02-13 22:51:22 +00:00
|
|
|
$rdfaType = 'mw:Error ' . $rdfaType;
|
|
|
|
|
$label = '';
|
|
|
|
|
if ( $enableLegacyMediaDOM ) {
|
|
|
|
|
$label = $frameParams['title'];
|
2021-01-26 23:15:53 +00:00
|
|
|
} else {
|
|
|
|
|
$label = $frameParams['alt'] ?? '';
|
2018-02-13 22:51:22 +00:00
|
|
|
}
|
|
|
|
|
$s = self::makeBrokenImageLinkObj(
|
2021-08-17 19:52:34 +00:00
|
|
|
$title, $label, '', '', '', (bool)$time, $handlerParams
|
2018-02-13 22:51:22 +00:00
|
|
|
);
|
2004-12-18 06:29:23 +00:00
|
|
|
} else {
|
2016-08-25 07:21:26 +00:00
|
|
|
self::processResponsiveImages( $file, $thumb, $handlerParams );
|
2023-02-08 20:08:42 +00:00
|
|
|
$params = [];
|
|
|
|
|
// An empty alt indicates an image is not a key part of the content
|
|
|
|
|
// and that non-visual browsers may omit it from rendering. Only
|
|
|
|
|
// set the parameter if it's explicitly requested.
|
|
|
|
|
if ( isset( $frameParams['alt'] ) ) {
|
|
|
|
|
$params['alt'] = $frameParams['alt'];
|
|
|
|
|
}
|
|
|
|
|
$params['title'] = $frameParams['title'];
|
2018-02-13 22:51:22 +00:00
|
|
|
if ( $enableLegacyMediaDOM ) {
|
|
|
|
|
$params += [
|
|
|
|
|
'valign' => $frameParams['valign'] ?? false,
|
|
|
|
|
'img-class' => $frameParams['class'],
|
|
|
|
|
];
|
|
|
|
|
if ( isset( $frameParams['border'] ) ) {
|
|
|
|
|
$params['img-class'] .= ( $params['img-class'] !== '' ? ' ' : '' ) . 'thumbborder';
|
|
|
|
|
}
|
2012-08-20 10:12:47 +00:00
|
|
|
}
|
2016-08-25 07:21:26 +00:00
|
|
|
$params = self::getImageLinkMTOParams( $frameParams, $query, $parser ) + $params;
|
2008-10-06 05:55:27 +00:00
|
|
|
$s = $thumb->toHtml( $params );
|
2004-12-18 06:29:23 +00:00
|
|
|
}
|
2018-02-13 22:51:22 +00:00
|
|
|
|
|
|
|
|
if ( $enableLegacyMediaDOM ) {
|
|
|
|
|
if ( $frameParams['align'] != '' ) {
|
|
|
|
|
$s = Html::rawElement(
|
|
|
|
|
'div',
|
|
|
|
|
[ 'class' => 'float' . $frameParams['align'] ],
|
|
|
|
|
$s
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
return str_replace( "\n", ' ', $prefix . $s . $postfix );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$wrapper = 'span';
|
|
|
|
|
$caption = '';
|
|
|
|
|
|
2016-08-25 07:21:26 +00:00
|
|
|
if ( $frameParams['align'] != '' ) {
|
2018-02-13 22:51:22 +00:00
|
|
|
$wrapper = 'figure';
|
|
|
|
|
// Possible values: mw-halign-left mw-halign-center mw-halign-right mw-halign-none
|
|
|
|
|
$classes[] = "mw-halign-{$frameParams['align']}";
|
|
|
|
|
$caption = Html::rawElement(
|
|
|
|
|
'figcaption', [], $frameParams['caption'] ?? ''
|
2018-06-13 15:28:20 +00:00
|
|
|
);
|
2018-02-13 22:51:22 +00:00
|
|
|
} elseif ( isset( $frameParams['valign'] ) ) {
|
|
|
|
|
// Possible values: mw-valign-middle mw-valign-baseline mw-valign-sub
|
|
|
|
|
// mw-valign-super mw-valign-top mw-valign-text-top mw-valign-bottom
|
|
|
|
|
// mw-valign-text-bottom
|
|
|
|
|
$classes[] = "mw-valign-{$frameParams['valign']}";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( isset( $frameParams['border'] ) ) {
|
|
|
|
|
$classes[] = 'mw-image-border';
|
2004-12-18 06:29:23 +00:00
|
|
|
}
|
2018-02-13 22:51:22 +00:00
|
|
|
|
|
|
|
|
if ( isset( $frameParams['class'] ) ) {
|
|
|
|
|
$classes[] = $frameParams['class'];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$attribs = [
|
|
|
|
|
'class' => $classes,
|
|
|
|
|
'typeof' => $rdfaType,
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
$s = Html::rawElement( $wrapper, $attribs, $s . $caption );
|
|
|
|
|
|
|
|
|
|
return str_replace( "\n", ' ', $s );
|
2004-12-18 06:29:23 +00:00
|
|
|
}
|
|
|
|
|
|
2010-07-20 10:28:00 +00:00
|
|
|
/**
|
2010-08-26 19:40:29 +00:00
|
|
|
* Get the link parameters for MediaTransformOutput::toHtml() from given
|
2010-07-20 10:28:00 +00:00
|
|
|
* frame parameters supplied by the Parser.
|
2013-03-11 17:15:01 +00:00
|
|
|
* @param array $frameParams The frame parameters
|
|
|
|
|
* @param string $query An optional query string to add to description page links
|
2014-08-14 18:22:52 +00:00
|
|
|
* @param Parser|null $parser
|
2012-02-09 19:29:36 +00:00
|
|
|
* @return array
|
2010-07-20 10:28:00 +00:00
|
|
|
*/
|
2022-05-10 19:40:16 +00:00
|
|
|
public static function getImageLinkMTOParams( $frameParams, $query = '', $parser = null ) {
|
2016-02-17 09:09:32 +00:00
|
|
|
$mtoParams = [];
|
2010-07-20 10:28:00 +00:00
|
|
|
if ( isset( $frameParams['link-url'] ) && $frameParams['link-url'] !== '' ) {
|
|
|
|
|
$mtoParams['custom-url-link'] = $frameParams['link-url'];
|
2010-11-09 12:25:57 +00:00
|
|
|
if ( isset( $frameParams['link-target'] ) ) {
|
|
|
|
|
$mtoParams['custom-target-link'] = $frameParams['link-target'];
|
|
|
|
|
}
|
2012-08-06 17:04:07 +00:00
|
|
|
if ( $parser ) {
|
|
|
|
|
$extLinkAttrs = $parser->getExternalLinkAttribs( $frameParams['link-url'] );
|
|
|
|
|
foreach ( $extLinkAttrs as $name => $val ) {
|
|
|
|
|
// Currently could include 'rel' and 'target'
|
2013-02-03 20:05:24 +00:00
|
|
|
$mtoParams['parser-extlink-' . $name] = $val;
|
2012-08-06 17:04:07 +00:00
|
|
|
}
|
|
|
|
|
}
|
2010-07-20 10:28:00 +00:00
|
|
|
} elseif ( isset( $frameParams['link-title'] ) && $frameParams['link-title'] !== '' ) {
|
2020-05-22 18:33:06 +00:00
|
|
|
$linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
|
2016-08-25 06:49:00 +00:00
|
|
|
$mtoParams['custom-title-link'] = Title::newFromLinkTarget(
|
2020-05-22 18:33:06 +00:00
|
|
|
$linkRenderer->normalizeTarget( $frameParams['link-title'] )
|
2016-08-25 06:49:00 +00:00
|
|
|
);
|
2022-02-18 22:50:52 +00:00
|
|
|
if ( isset( $frameParams['link-title-query'] ) ) {
|
|
|
|
|
$mtoParams['custom-title-link-query'] = $frameParams['link-title-query'];
|
|
|
|
|
}
|
2010-07-20 10:28:00 +00:00
|
|
|
} elseif ( !empty( $frameParams['no-link'] ) ) {
|
|
|
|
|
// No link
|
|
|
|
|
} else {
|
|
|
|
|
$mtoParams['desc-link'] = true;
|
|
|
|
|
$mtoParams['desc-query'] = $query;
|
|
|
|
|
}
|
|
|
|
|
return $mtoParams;
|
|
|
|
|
}
|
|
|
|
|
|
2004-12-18 06:29:23 +00:00
|
|
|
/**
|
|
|
|
|
* Make HTML for a thumbnail including image, border and caption
|
2019-04-15 12:47:32 +00:00
|
|
|
* @param LinkTarget $title
|
2021-10-21 18:09:51 +00:00
|
|
|
* @param File|false $file File object or false if it doesn't exist
|
2014-04-22 11:07:02 +00:00
|
|
|
* @param string $label
|
|
|
|
|
* @param string $alt
|
2018-02-13 22:51:22 +00:00
|
|
|
* @param string|null $align
|
2014-04-22 11:07:02 +00:00
|
|
|
* @param array $params
|
|
|
|
|
* @param bool $framed
|
|
|
|
|
* @param string $manualthumb
|
|
|
|
|
* @return string
|
2004-12-18 06:29:23 +00:00
|
|
|
*/
|
2018-02-13 22:51:22 +00:00
|
|
|
public static function makeThumbLinkObj(
|
|
|
|
|
LinkTarget $title, $file, $label = '', $alt = '', $align = null,
|
2022-08-23 08:38:49 +00:00
|
|
|
$params = [], $framed = false, $manualthumb = ''
|
2013-12-01 20:39:00 +00:00
|
|
|
) {
|
2016-02-17 09:09:32 +00:00
|
|
|
$frameParams = [
|
Basic integrated audio/video support, with Ogg implementation.
* JavaScript video player based loosely on Greg Maxwell's player
* Image page text snippet customisation
* Abstraction of transform parameters in the parser. Introduced Linker::makeImageLink2().
* Made canRender(), mustRender() depend on file, not just on handler. Moved width=0, height=0 checking to ImageHandler::canRender(), since audio streams have width=height=0 but should be rendered.
Also:
* Automatic upgrade for oldimage rows on image page view, allows media handler selection based on oi_*_mime
* oi_*_mime unconditionally referenced, REQUIRES SCHEMA UPGRADE
* Don't destroy file info for missing files on upgrade
* Simple, centralised extension message file handling
* Made MessageCache::loadAllMessages non-static, optimised for repeated-call case due to abuse in User.php
* Support for lightweight parser output hooks, with callback whitelist for security
* Moved Linker::formatSize() to Language, to join the new formatTimePeriod() and formatBitrate()
* Introduced MagicWordArray, regex capture trick requires that magic word IDs DO NOT CONTAIN HYPHENS.
2007-08-15 10:50:09 +00:00
|
|
|
'alt' => $alt,
|
|
|
|
|
'caption' => $label,
|
|
|
|
|
'align' => $align
|
2016-02-17 09:09:32 +00:00
|
|
|
];
|
2022-04-13 01:21:10 +00:00
|
|
|
$classes = [];
|
2011-03-02 10:57:55 +00:00
|
|
|
if ( $manualthumb ) {
|
|
|
|
|
$frameParams['manualthumb'] = $manualthumb;
|
2022-04-13 01:21:10 +00:00
|
|
|
} elseif ( $framed ) {
|
|
|
|
|
$frameParams['framed'] = true;
|
|
|
|
|
} elseif ( !isset( $params['width'] ) ) {
|
|
|
|
|
$classes[] = 'mw-default-size';
|
2011-03-02 10:57:55 +00:00
|
|
|
}
|
2018-02-13 22:51:22 +00:00
|
|
|
return self::makeThumbLink2(
|
|
|
|
|
$title, $file, $frameParams, $params, false, '', $classes
|
|
|
|
|
);
|
Basic integrated audio/video support, with Ogg implementation.
* JavaScript video player based loosely on Greg Maxwell's player
* Image page text snippet customisation
* Abstraction of transform parameters in the parser. Introduced Linker::makeImageLink2().
* Made canRender(), mustRender() depend on file, not just on handler. Moved width=0, height=0 checking to ImageHandler::canRender(), since audio streams have width=height=0 but should be rendered.
Also:
* Automatic upgrade for oldimage rows on image page view, allows media handler selection based on oi_*_mime
* oi_*_mime unconditionally referenced, REQUIRES SCHEMA UPGRADE
* Don't destroy file info for missing files on upgrade
* Simple, centralised extension message file handling
* Made MessageCache::loadAllMessages non-static, optimised for repeated-call case due to abuse in User.php
* Support for lightweight parser output hooks, with callback whitelist for security
* Moved Linker::formatSize() to Language, to join the new formatTimePeriod() and formatBitrate()
* Introduced MagicWordArray, regex capture trick requires that magic word IDs DO NOT CONTAIN HYPHENS.
2007-08-15 10:50:09 +00:00
|
|
|
}
|
|
|
|
|
|
2011-03-02 10:57:55 +00:00
|
|
|
/**
|
2019-04-15 12:47:32 +00:00
|
|
|
* @param LinkTarget $title
|
2022-03-03 20:33:11 +00:00
|
|
|
* @param File|false $file
|
2011-03-02 10:57:55 +00:00
|
|
|
* @param array $frameParams
|
|
|
|
|
* @param array $handlerParams
|
|
|
|
|
* @param bool $time
|
|
|
|
|
* @param string $query
|
2018-02-13 22:51:22 +00:00
|
|
|
* @param string[] $classes @since 1.36
|
2022-02-18 00:10:12 +00:00
|
|
|
* @param Parser|null $parser @since 1.38
|
2014-04-22 11:07:02 +00:00
|
|
|
* @return string
|
2011-03-02 10:57:55 +00:00
|
|
|
*/
|
2018-02-13 22:51:22 +00:00
|
|
|
public static function makeThumbLink2(
|
|
|
|
|
LinkTarget $title, $file, $frameParams = [], $handlerParams = [],
|
2022-08-23 08:38:49 +00:00
|
|
|
$time = false, $query = '', array $classes = [], ?Parser $parser = null
|
2013-12-01 20:39:00 +00:00
|
|
|
) {
|
Basic integrated audio/video support, with Ogg implementation.
* JavaScript video player based loosely on Greg Maxwell's player
* Image page text snippet customisation
* Abstraction of transform parameters in the parser. Introduced Linker::makeImageLink2().
* Made canRender(), mustRender() depend on file, not just on handler. Moved width=0, height=0 checking to ImageHandler::canRender(), since audio streams have width=height=0 but should be rendered.
Also:
* Automatic upgrade for oldimage rows on image page view, allows media handler selection based on oi_*_mime
* oi_*_mime unconditionally referenced, REQUIRES SCHEMA UPGRADE
* Don't destroy file info for missing files on upgrade
* Simple, centralised extension message file handling
* Made MessageCache::loadAllMessages non-static, optimised for repeated-call case due to abuse in User.php
* Support for lightweight parser output hooks, with callback whitelist for security
* Moved Linker::formatSize() to Language, to join the new formatTimePeriod() and formatBitrate()
* Introduced MagicWordArray, regex capture trick requires that magic word IDs DO NOT CONTAIN HYPHENS.
2007-08-15 10:50:09 +00:00
|
|
|
$exists = $file && $file->exists();
|
2004-12-18 06:29:23 +00:00
|
|
|
|
2018-02-13 22:51:22 +00:00
|
|
|
$services = MediaWikiServices::getInstance();
|
2022-04-01 15:58:32 +00:00
|
|
|
$enableLegacyMediaDOM = $services->getMainConfig()->get( MainConfigNames::ParserEnableLegacyMediaDOM );
|
2018-02-13 22:51:22 +00:00
|
|
|
|
2017-10-06 22:17:58 +00:00
|
|
|
$page = $handlerParams['page'] ?? false;
|
2016-08-25 07:21:26 +00:00
|
|
|
if ( !isset( $frameParams['align'] ) ) {
|
2018-02-13 22:51:22 +00:00
|
|
|
$frameParams['align'] = '';
|
|
|
|
|
if ( $enableLegacyMediaDOM ) {
|
|
|
|
|
$frameParams['align'] = 'right';
|
|
|
|
|
}
|
2013-04-20 22:49:30 +00:00
|
|
|
}
|
2016-08-25 07:21:26 +00:00
|
|
|
if ( !isset( $frameParams['caption'] ) ) {
|
|
|
|
|
$frameParams['caption'] = '';
|
2013-04-20 22:49:30 +00:00
|
|
|
}
|
Basic integrated audio/video support, with Ogg implementation.
* JavaScript video player based loosely on Greg Maxwell's player
* Image page text snippet customisation
* Abstraction of transform parameters in the parser. Introduced Linker::makeImageLink2().
* Made canRender(), mustRender() depend on file, not just on handler. Moved width=0, height=0 checking to ImageHandler::canRender(), since audio streams have width=height=0 but should be rendered.
Also:
* Automatic upgrade for oldimage rows on image page view, allows media handler selection based on oi_*_mime
* oi_*_mime unconditionally referenced, REQUIRES SCHEMA UPGRADE
* Don't destroy file info for missing files on upgrade
* Simple, centralised extension message file handling
* Made MessageCache::loadAllMessages non-static, optimised for repeated-call case due to abuse in User.php
* Support for lightweight parser output hooks, with callback whitelist for security
* Moved Linker::formatSize() to Language, to join the new formatTimePeriod() and formatBitrate()
* Introduced MagicWordArray, regex capture trick requires that magic word IDs DO NOT CONTAIN HYPHENS.
2007-08-15 10:50:09 +00:00
|
|
|
|
2016-08-25 07:21:26 +00:00
|
|
|
if ( empty( $handlerParams['width'] ) ) {
|
2008-04-14 07:45:50 +00:00
|
|
|
// Reduce width for upright images when parameter 'upright' is used
|
2016-08-25 07:21:26 +00:00
|
|
|
$handlerParams['width'] = isset( $frameParams['upright'] ) ? 130 : 180;
|
2004-12-18 06:29:23 +00:00
|
|
|
}
|
2018-02-13 22:51:22 +00:00
|
|
|
|
2007-04-20 12:31:36 +00:00
|
|
|
$thumb = false;
|
Initial stab at responsive images for screen densities.
* adds $wgResponsiveImages setting, defaulting to true, to enable the feature
* adds 'srcset' attribute with 1.5x and 2x URLs to image links and image thumbs
* adds jquery.hidpi plugin to check pixel density and implement partial 'srcset' polyfill
** $.devicePixelRatio() returns window.devicePixelRatio, with compat fallback for IE 10
** $().hidpi() performs a 'srcset' polyfill for browsers with no native 'srcset' support
* adds mediawiki.hidpi RL script to trigger hidpi loads after main images load
Note that this is a work in progress. There will be places where this doesn't yet work which output their imgs differently. If moving from a low to high-DPI screen on a MacBook Pro Retina display, you won't see images load until you reload.
Confirmed basic images and thumbs in wikitext appear to work in Safari 6, Chrome 21, Firefox 18 nightly on MacBook Pro Retina display, and IE 10 in Windows 8 at 150% zoom, 200% zoom, and 140% and 180%-ratio Metro tablet sizes.
Internally this is still a bit of a hack; Linker::makeImageLink and Linker::makeThumbLink explicitly ask for 1.5x and 2x scaled versions and insert their URLs, if different, into the original thumbnail object which (in default handler) outputs the srcset. This means that a number of places that handle images differently won't see the higher-resolution versions, such as <gallery> and the large thumbnail on the File: description page.
At some point we may wish to redo some of how the MediaHandler stuff works so that requesting a single thumbnail automatically produces the extra sizes in all circumstances. We might also consider outputting a 'srcset' or multiple src sizes in 'imageinfo' API requests, which would make ApiForeignRepo/InstantCommons more efficient. (Currently it has to make three requests for each image to get the three sizes.)
Change-Id: Id80ebd07a1a9f401a2c2bfeb21aae987e5aa863b
2012-09-18 07:18:50 +00:00
|
|
|
$noscale = false;
|
2013-02-27 00:12:12 +00:00
|
|
|
$manualthumb = false;
|
Emit mw:File typeof for media
This matches Parsoid commit I676a88d80e2739ba69d4e7646de56fb87ae3e8e8
A few reasons for this change:
* It's less painful to do this now than after the legacy parser is made
to match Parsoid's output and more stuff depends on it.
* It simplifies the queries that are necessary to collect the media on a
page, which makes it both easier to work with and requires shipping
less bytes.
* The nested img|audio|video tags retain the distinction, so there's
no loss of information.
* mw:File is a closer match to the [[File:Hiho.jpg]] syntax that
generates these elements.
* For broken media, it's unclear which type should be applied and
mw:File obviates the question.
* For manualthumb, it's unclear which type to choose if, say, the file is
a video but we've rendered an image. For now, we've gone with what's
actually rendered in the page, but again mw:File avoids the issue.
And yes, for this special case a poster could be used, that's T121617
* There's precedent in MediaWiki, where Image: redirects to the File:
namespace. We should have followed that lead earlier instead of
introducing mw:Audio and mw:Video.
Bug: T273505
Depends-On: I9f36ef8bc3023195dd31e52f04e8f0f0f792d311
Depends-On: I0b11463b802b898429a451378f15987700752e0e
Change-Id: I719a367622b3887cfea3032582a767c2c38d0f87
2022-05-19 20:43:00 +00:00
|
|
|
$rdfaType = 'mw:File/Thumb';
|
2007-05-30 21:02:32 +00:00
|
|
|
|
|
|
|
|
if ( !$exists ) {
|
2016-08-25 07:21:26 +00:00
|
|
|
$outerWidth = $handlerParams['width'] + 2;
|
2004-12-18 06:29:23 +00:00
|
|
|
} else {
|
2016-08-25 07:21:26 +00:00
|
|
|
if ( isset( $frameParams['manualthumb'] ) ) {
|
2007-05-30 21:02:32 +00:00
|
|
|
# Use manually specified thumbnail
|
2016-08-25 07:21:26 +00:00
|
|
|
$manual_title = Title::makeTitleSafe( NS_FILE, $frameParams['manualthumb'] );
|
2010-08-26 19:40:29 +00:00
|
|
|
if ( $manual_title ) {
|
2018-02-13 22:51:22 +00:00
|
|
|
$manual_img = $services->getRepoGroup()
|
2019-05-14 17:00:34 +00:00
|
|
|
->findFile( $manual_title );
|
2007-05-30 21:02:32 +00:00
|
|
|
if ( $manual_img ) {
|
2016-08-25 07:21:26 +00:00
|
|
|
$thumb = $manual_img->getUnscaledThumb( $handlerParams );
|
2013-02-27 00:12:12 +00:00
|
|
|
$manualthumb = true;
|
2007-05-30 21:02:32 +00:00
|
|
|
} else {
|
|
|
|
|
$exists = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-08-25 07:21:26 +00:00
|
|
|
} elseif ( isset( $frameParams['framed'] ) ) {
|
2007-05-30 21:02:32 +00:00
|
|
|
// Use image dimensions, don't scale
|
2016-08-25 07:21:26 +00:00
|
|
|
$thumb = $file->getUnscaledThumb( $handlerParams );
|
Initial stab at responsive images for screen densities.
* adds $wgResponsiveImages setting, defaulting to true, to enable the feature
* adds 'srcset' attribute with 1.5x and 2x URLs to image links and image thumbs
* adds jquery.hidpi plugin to check pixel density and implement partial 'srcset' polyfill
** $.devicePixelRatio() returns window.devicePixelRatio, with compat fallback for IE 10
** $().hidpi() performs a 'srcset' polyfill for browsers with no native 'srcset' support
* adds mediawiki.hidpi RL script to trigger hidpi loads after main images load
Note that this is a work in progress. There will be places where this doesn't yet work which output their imgs differently. If moving from a low to high-DPI screen on a MacBook Pro Retina display, you won't see images load until you reload.
Confirmed basic images and thumbs in wikitext appear to work in Safari 6, Chrome 21, Firefox 18 nightly on MacBook Pro Retina display, and IE 10 in Windows 8 at 150% zoom, 200% zoom, and 140% and 180%-ratio Metro tablet sizes.
Internally this is still a bit of a hack; Linker::makeImageLink and Linker::makeThumbLink explicitly ask for 1.5x and 2x scaled versions and insert their URLs, if different, into the original thumbnail object which (in default handler) outputs the srcset. This means that a number of places that handle images differently won't see the higher-resolution versions, such as <gallery> and the large thumbnail on the File: description page.
At some point we may wish to redo some of how the MediaHandler stuff works so that requesting a single thumbnail automatically produces the extra sizes in all circumstances. We might also consider outputting a 'srcset' or multiple src sizes in 'imageinfo' API requests, which would make ApiForeignRepo/InstantCommons more efficient. (Currently it has to make three requests for each image to get the three sizes.)
Change-Id: Id80ebd07a1a9f401a2c2bfeb21aae987e5aa863b
2012-09-18 07:18:50 +00:00
|
|
|
$noscale = true;
|
Emit mw:File typeof for media
This matches Parsoid commit I676a88d80e2739ba69d4e7646de56fb87ae3e8e8
A few reasons for this change:
* It's less painful to do this now than after the legacy parser is made
to match Parsoid's output and more stuff depends on it.
* It simplifies the queries that are necessary to collect the media on a
page, which makes it both easier to work with and requires shipping
less bytes.
* The nested img|audio|video tags retain the distinction, so there's
no loss of information.
* mw:File is a closer match to the [[File:Hiho.jpg]] syntax that
generates these elements.
* For broken media, it's unclear which type should be applied and
mw:File obviates the question.
* For manualthumb, it's unclear which type to choose if, say, the file is
a video but we've rendered an image. For now, we've gone with what's
actually rendered in the page, but again mw:File avoids the issue.
And yes, for this special case a poster could be used, that's T121617
* There's precedent in MediaWiki, where Image: redirects to the File:
namespace. We should have followed that lead earlier instead of
introducing mw:Audio and mw:Video.
Bug: T273505
Depends-On: I9f36ef8bc3023195dd31e52f04e8f0f0f792d311
Depends-On: I0b11463b802b898429a451378f15987700752e0e
Change-Id: I719a367622b3887cfea3032582a767c2c38d0f87
2022-05-19 20:43:00 +00:00
|
|
|
$rdfaType = 'mw:File/Frame';
|
2007-05-30 21:02:32 +00:00
|
|
|
} else {
|
2007-09-18 14:30:40 +00:00
|
|
|
# Do not present an image bigger than the source, for bitmap-style images
|
2013-03-04 08:44:38 +00:00
|
|
|
# This is a hack to maintain compatibility with arbitrary pre-1.10 behavior
|
2007-09-18 14:30:40 +00:00
|
|
|
$srcWidth = $file->getWidth( $page );
|
2016-08-25 07:21:26 +00:00
|
|
|
if ( $srcWidth && !$file->mustRender() && $handlerParams['width'] > $srcWidth ) {
|
|
|
|
|
$handlerParams['width'] = $srcWidth;
|
2007-09-18 14:30:40 +00:00
|
|
|
}
|
2016-08-25 07:21:26 +00:00
|
|
|
$thumb = $file->transform( $handlerParams );
|
2007-05-01 21:30:46 +00:00
|
|
|
}
|
2004-12-18 06:29:23 +00:00
|
|
|
|
2007-05-30 21:02:32 +00:00
|
|
|
if ( $thumb ) {
|
|
|
|
|
$outerWidth = $thumb->getWidth() + 2;
|
|
|
|
|
} else {
|
2016-08-25 07:21:26 +00:00
|
|
|
$outerWidth = $handlerParams['width'] + 2;
|
2007-05-30 21:02:32 +00:00
|
|
|
}
|
2004-12-18 06:29:23 +00:00
|
|
|
}
|
|
|
|
|
|
2019-04-15 12:47:32 +00:00
|
|
|
$url = Title::newFromLinkTarget( $title )->getLocalURL( $query );
|
2022-02-18 20:17:18 +00:00
|
|
|
$linkTitleQuery = [];
|
2018-02-13 22:51:22 +00:00
|
|
|
|
2021-12-15 01:03:58 +00:00
|
|
|
if ( $page ) {
|
2022-02-18 20:17:18 +00:00
|
|
|
$linkTitleQuery['page'] = $page;
|
2018-02-13 22:51:22 +00:00
|
|
|
# ThumbnailImage::toHtml() already adds page= onto the end of DjVu URLs
|
|
|
|
|
# So we don't need to pass it here in $query. However, the URL for the
|
|
|
|
|
# zoom icon still needs it, so we make a unique query for it. See T16771
|
2021-12-15 01:03:58 +00:00
|
|
|
# FIXME: What about "lang" and other querystring parameters
|
2022-02-18 20:17:18 +00:00
|
|
|
$url = wfAppendQuery( $url, $linkTitleQuery );
|
2009-03-13 15:02:28 +00:00
|
|
|
}
|
2018-02-13 22:51:22 +00:00
|
|
|
|
2013-12-01 19:58:51 +00:00
|
|
|
if ( $manualthumb
|
2016-08-25 07:21:26 +00:00
|
|
|
&& !isset( $frameParams['link-title'] )
|
|
|
|
|
&& !isset( $frameParams['link-url'] )
|
|
|
|
|
&& !isset( $frameParams['no-link'] ) ) {
|
2022-02-18 20:17:18 +00:00
|
|
|
$frameParams['link-title'] = $title;
|
|
|
|
|
$frameParams['link-title-query'] = $linkTitleQuery;
|
2013-02-27 00:12:12 +00:00
|
|
|
}
|
2004-12-18 06:29:23 +00:00
|
|
|
|
2018-02-13 22:51:22 +00:00
|
|
|
if ( $frameParams['align'] != '' ) {
|
|
|
|
|
// Possible values: mw-halign-left mw-halign-center mw-halign-right mw-halign-none
|
|
|
|
|
$classes[] = "mw-halign-{$frameParams['align']}";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( isset( $frameParams['class'] ) ) {
|
|
|
|
|
$classes[] = $frameParams['class'];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$s = '';
|
|
|
|
|
|
|
|
|
|
if ( $enableLegacyMediaDOM ) {
|
|
|
|
|
$s .= "<div class=\"thumb t{$frameParams['align']}\">"
|
|
|
|
|
. "<div class=\"thumbinner\" style=\"width:{$outerWidth}px;\">";
|
|
|
|
|
}
|
2014-05-15 15:38:28 +00:00
|
|
|
|
2010-08-26 19:40:29 +00:00
|
|
|
if ( !$exists ) {
|
2018-02-13 22:51:22 +00:00
|
|
|
$label = '';
|
2021-01-26 23:15:53 +00:00
|
|
|
if ( !$enableLegacyMediaDOM ) {
|
|
|
|
|
$label = $frameParams['alt'] ?? '';
|
|
|
|
|
}
|
2018-02-13 22:51:22 +00:00
|
|
|
$s .= self::makeBrokenImageLinkObj(
|
2021-08-17 19:52:34 +00:00
|
|
|
$title, $label, '', '', '', (bool)$time, $handlerParams
|
2018-02-13 22:51:22 +00:00
|
|
|
);
|
2010-07-20 10:28:00 +00:00
|
|
|
$zoomIcon = '';
|
2007-05-30 21:02:32 +00:00
|
|
|
} elseif ( !$thumb ) {
|
2021-12-17 02:23:20 +00:00
|
|
|
if ( $enableLegacyMediaDOM ) {
|
|
|
|
|
$s .= wfMessage( 'thumbnail_error', '' )->escaped();
|
|
|
|
|
} else {
|
2021-01-26 23:15:53 +00:00
|
|
|
$label = $frameParams['alt'] ?? '';
|
2021-12-17 02:23:20 +00:00
|
|
|
$s .= self::makeBrokenImageLinkObj(
|
2021-01-26 23:15:53 +00:00
|
|
|
$title, $label, '', '', '', (bool)$time, $handlerParams
|
2021-12-17 02:23:20 +00:00
|
|
|
);
|
|
|
|
|
}
|
2010-07-20 10:28:00 +00:00
|
|
|
$zoomIcon = '';
|
2004-12-18 06:29:23 +00:00
|
|
|
} else {
|
2013-02-27 00:12:12 +00:00
|
|
|
if ( !$noscale && !$manualthumb ) {
|
2016-08-25 07:21:26 +00:00
|
|
|
self::processResponsiveImages( $file, $thumb, $handlerParams );
|
Initial stab at responsive images for screen densities.
* adds $wgResponsiveImages setting, defaulting to true, to enable the feature
* adds 'srcset' attribute with 1.5x and 2x URLs to image links and image thumbs
* adds jquery.hidpi plugin to check pixel density and implement partial 'srcset' polyfill
** $.devicePixelRatio() returns window.devicePixelRatio, with compat fallback for IE 10
** $().hidpi() performs a 'srcset' polyfill for browsers with no native 'srcset' support
* adds mediawiki.hidpi RL script to trigger hidpi loads after main images load
Note that this is a work in progress. There will be places where this doesn't yet work which output their imgs differently. If moving from a low to high-DPI screen on a MacBook Pro Retina display, you won't see images load until you reload.
Confirmed basic images and thumbs in wikitext appear to work in Safari 6, Chrome 21, Firefox 18 nightly on MacBook Pro Retina display, and IE 10 in Windows 8 at 150% zoom, 200% zoom, and 140% and 180%-ratio Metro tablet sizes.
Internally this is still a bit of a hack; Linker::makeImageLink and Linker::makeThumbLink explicitly ask for 1.5x and 2x scaled versions and insert their URLs, if different, into the original thumbnail object which (in default handler) outputs the srcset. This means that a number of places that handle images differently won't see the higher-resolution versions, such as <gallery> and the large thumbnail on the File: description page.
At some point we may wish to redo some of how the MediaHandler stuff works so that requesting a single thumbnail automatically produces the extra sizes in all circumstances. We might also consider outputting a 'srcset' or multiple src sizes in 'imageinfo' API requests, which would make ApiForeignRepo/InstantCommons more efficient. (Currently it has to make three requests for each image to get the three sizes.)
Change-Id: Id80ebd07a1a9f401a2c2bfeb21aae987e5aa863b
2012-09-18 07:18:50 +00:00
|
|
|
}
|
2023-02-08 20:08:42 +00:00
|
|
|
$params = [];
|
|
|
|
|
// An empty alt indicates an image is not a key part of the content
|
|
|
|
|
// and that non-visual browsers may omit it from rendering. Only
|
|
|
|
|
// set the parameter if it's explicitly requested.
|
|
|
|
|
if ( isset( $frameParams['alt'] ) ) {
|
|
|
|
|
$params['alt'] = $frameParams['alt'];
|
|
|
|
|
}
|
2018-02-13 22:51:22 +00:00
|
|
|
if ( $enableLegacyMediaDOM ) {
|
|
|
|
|
$params += [
|
|
|
|
|
'img-class' => ( isset( $frameParams['class'] ) && $frameParams['class'] !== ''
|
|
|
|
|
? $frameParams['class'] . ' '
|
|
|
|
|
: '' ) . 'thumbimage'
|
|
|
|
|
];
|
|
|
|
|
}
|
2022-02-18 00:10:12 +00:00
|
|
|
$params = self::getImageLinkMTOParams( $frameParams, $query, $parser ) + $params;
|
2010-07-20 10:28:00 +00:00
|
|
|
$s .= $thumb->toHtml( $params );
|
2016-08-25 07:21:26 +00:00
|
|
|
if ( isset( $frameParams['framed'] ) ) {
|
2022-08-23 08:38:49 +00:00
|
|
|
$zoomIcon = '';
|
2004-12-18 06:29:23 +00:00
|
|
|
} else {
|
2016-02-17 09:09:32 +00:00
|
|
|
$zoomIcon = Html::rawElement( 'div', [ 'class' => 'magnify' ],
|
|
|
|
|
Html::rawElement( 'a', [
|
2011-04-30 12:04:00 +00:00
|
|
|
'href' => $url,
|
|
|
|
|
'class' => 'internal',
|
2022-08-23 08:38:49 +00:00
|
|
|
'title' => wfMessage( 'thumbnail-more' )->text(),
|
|
|
|
|
] )
|
|
|
|
|
);
|
2004-12-18 06:29:23 +00:00
|
|
|
}
|
|
|
|
|
}
|
2018-02-13 22:51:22 +00:00
|
|
|
|
|
|
|
|
if ( $enableLegacyMediaDOM ) {
|
2022-08-23 08:38:49 +00:00
|
|
|
$s .= ' <div class="thumbcaption">' . $zoomIcon . $frameParams['caption'] . '</div></div></div>';
|
2018-02-13 22:51:22 +00:00
|
|
|
return str_replace( "\n", ' ', $s );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$s .= Html::rawElement(
|
|
|
|
|
'figcaption', [], $frameParams['caption'] ?? ''
|
|
|
|
|
);
|
|
|
|
|
|
2021-12-17 02:23:20 +00:00
|
|
|
if ( !$exists || !$thumb ) {
|
2018-02-13 22:51:22 +00:00
|
|
|
$rdfaType = 'mw:Error ' . $rdfaType;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$attribs = [
|
|
|
|
|
'class' => $classes,
|
|
|
|
|
'typeof' => $rdfaType,
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
$s = Html::rawElement( 'figure', $attribs, $s );
|
|
|
|
|
|
2010-08-26 19:40:29 +00:00
|
|
|
return str_replace( "\n", ' ', $s );
|
2004-12-18 06:29:23 +00:00
|
|
|
}
|
2006-01-07 13:31:29 +00:00
|
|
|
|
Initial stab at responsive images for screen densities.
* adds $wgResponsiveImages setting, defaulting to true, to enable the feature
* adds 'srcset' attribute with 1.5x and 2x URLs to image links and image thumbs
* adds jquery.hidpi plugin to check pixel density and implement partial 'srcset' polyfill
** $.devicePixelRatio() returns window.devicePixelRatio, with compat fallback for IE 10
** $().hidpi() performs a 'srcset' polyfill for browsers with no native 'srcset' support
* adds mediawiki.hidpi RL script to trigger hidpi loads after main images load
Note that this is a work in progress. There will be places where this doesn't yet work which output their imgs differently. If moving from a low to high-DPI screen on a MacBook Pro Retina display, you won't see images load until you reload.
Confirmed basic images and thumbs in wikitext appear to work in Safari 6, Chrome 21, Firefox 18 nightly on MacBook Pro Retina display, and IE 10 in Windows 8 at 150% zoom, 200% zoom, and 140% and 180%-ratio Metro tablet sizes.
Internally this is still a bit of a hack; Linker::makeImageLink and Linker::makeThumbLink explicitly ask for 1.5x and 2x scaled versions and insert their URLs, if different, into the original thumbnail object which (in default handler) outputs the srcset. This means that a number of places that handle images differently won't see the higher-resolution versions, such as <gallery> and the large thumbnail on the File: description page.
At some point we may wish to redo some of how the MediaHandler stuff works so that requesting a single thumbnail automatically produces the extra sizes in all circumstances. We might also consider outputting a 'srcset' or multiple src sizes in 'imageinfo' API requests, which would make ApiForeignRepo/InstantCommons more efficient. (Currently it has to make three requests for each image to get the three sizes.)
Change-Id: Id80ebd07a1a9f401a2c2bfeb21aae987e5aa863b
2012-09-18 07:18:50 +00:00
|
|
|
/**
|
|
|
|
|
* Process responsive images: add 1.5x and 2x subimages to the thumbnail, where
|
|
|
|
|
* applicable.
|
|
|
|
|
*
|
|
|
|
|
* @param File $file
|
2021-10-21 18:09:51 +00:00
|
|
|
* @param MediaTransformOutput|null $thumb
|
2014-04-22 11:07:02 +00:00
|
|
|
* @param array $hp Image parameters
|
Initial stab at responsive images for screen densities.
* adds $wgResponsiveImages setting, defaulting to true, to enable the feature
* adds 'srcset' attribute with 1.5x and 2x URLs to image links and image thumbs
* adds jquery.hidpi plugin to check pixel density and implement partial 'srcset' polyfill
** $.devicePixelRatio() returns window.devicePixelRatio, with compat fallback for IE 10
** $().hidpi() performs a 'srcset' polyfill for browsers with no native 'srcset' support
* adds mediawiki.hidpi RL script to trigger hidpi loads after main images load
Note that this is a work in progress. There will be places where this doesn't yet work which output their imgs differently. If moving from a low to high-DPI screen on a MacBook Pro Retina display, you won't see images load until you reload.
Confirmed basic images and thumbs in wikitext appear to work in Safari 6, Chrome 21, Firefox 18 nightly on MacBook Pro Retina display, and IE 10 in Windows 8 at 150% zoom, 200% zoom, and 140% and 180%-ratio Metro tablet sizes.
Internally this is still a bit of a hack; Linker::makeImageLink and Linker::makeThumbLink explicitly ask for 1.5x and 2x scaled versions and insert their URLs, if different, into the original thumbnail object which (in default handler) outputs the srcset. This means that a number of places that handle images differently won't see the higher-resolution versions, such as <gallery> and the large thumbnail on the File: description page.
At some point we may wish to redo some of how the MediaHandler stuff works so that requesting a single thumbnail automatically produces the extra sizes in all circumstances. We might also consider outputting a 'srcset' or multiple src sizes in 'imageinfo' API requests, which would make ApiForeignRepo/InstantCommons more efficient. (Currently it has to make three requests for each image to get the three sizes.)
Change-Id: Id80ebd07a1a9f401a2c2bfeb21aae987e5aa863b
2012-09-18 07:18:50 +00:00
|
|
|
*/
|
2013-11-20 02:48:58 +00:00
|
|
|
public static function processResponsiveImages( $file, $thumb, $hp ) {
|
2022-04-01 15:58:32 +00:00
|
|
|
$responsiveImages = MediaWikiServices::getInstance()->getMainConfig()->get( MainConfigNames::ResponsiveImages );
|
2022-01-06 18:44:56 +00:00
|
|
|
if ( $responsiveImages && $thumb && !$thumb->isError() ) {
|
Initial stab at responsive images for screen densities.
* adds $wgResponsiveImages setting, defaulting to true, to enable the feature
* adds 'srcset' attribute with 1.5x and 2x URLs to image links and image thumbs
* adds jquery.hidpi plugin to check pixel density and implement partial 'srcset' polyfill
** $.devicePixelRatio() returns window.devicePixelRatio, with compat fallback for IE 10
** $().hidpi() performs a 'srcset' polyfill for browsers with no native 'srcset' support
* adds mediawiki.hidpi RL script to trigger hidpi loads after main images load
Note that this is a work in progress. There will be places where this doesn't yet work which output their imgs differently. If moving from a low to high-DPI screen on a MacBook Pro Retina display, you won't see images load until you reload.
Confirmed basic images and thumbs in wikitext appear to work in Safari 6, Chrome 21, Firefox 18 nightly on MacBook Pro Retina display, and IE 10 in Windows 8 at 150% zoom, 200% zoom, and 140% and 180%-ratio Metro tablet sizes.
Internally this is still a bit of a hack; Linker::makeImageLink and Linker::makeThumbLink explicitly ask for 1.5x and 2x scaled versions and insert their URLs, if different, into the original thumbnail object which (in default handler) outputs the srcset. This means that a number of places that handle images differently won't see the higher-resolution versions, such as <gallery> and the large thumbnail on the File: description page.
At some point we may wish to redo some of how the MediaHandler stuff works so that requesting a single thumbnail automatically produces the extra sizes in all circumstances. We might also consider outputting a 'srcset' or multiple src sizes in 'imageinfo' API requests, which would make ApiForeignRepo/InstantCommons more efficient. (Currently it has to make three requests for each image to get the three sizes.)
Change-Id: Id80ebd07a1a9f401a2c2bfeb21aae987e5aa863b
2012-09-18 07:18:50 +00:00
|
|
|
$hp15 = $hp;
|
|
|
|
|
$hp15['width'] = round( $hp['width'] * 1.5 );
|
|
|
|
|
$hp20 = $hp;
|
|
|
|
|
$hp20['width'] = $hp['width'] * 2;
|
|
|
|
|
if ( isset( $hp['height'] ) ) {
|
|
|
|
|
$hp15['height'] = round( $hp['height'] * 1.5 );
|
|
|
|
|
$hp20['height'] = $hp['height'] * 2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$thumb15 = $file->transform( $hp15 );
|
|
|
|
|
$thumb20 = $file->transform( $hp20 );
|
2014-06-26 00:48:16 +00:00
|
|
|
if ( $thumb15 && !$thumb15->isError() && $thumb15->getUrl() !== $thumb->getUrl() ) {
|
2013-12-05 12:55:04 +00:00
|
|
|
$thumb->responsiveUrls['1.5'] = $thumb15->getUrl();
|
Initial stab at responsive images for screen densities.
* adds $wgResponsiveImages setting, defaulting to true, to enable the feature
* adds 'srcset' attribute with 1.5x and 2x URLs to image links and image thumbs
* adds jquery.hidpi plugin to check pixel density and implement partial 'srcset' polyfill
** $.devicePixelRatio() returns window.devicePixelRatio, with compat fallback for IE 10
** $().hidpi() performs a 'srcset' polyfill for browsers with no native 'srcset' support
* adds mediawiki.hidpi RL script to trigger hidpi loads after main images load
Note that this is a work in progress. There will be places where this doesn't yet work which output their imgs differently. If moving from a low to high-DPI screen on a MacBook Pro Retina display, you won't see images load until you reload.
Confirmed basic images and thumbs in wikitext appear to work in Safari 6, Chrome 21, Firefox 18 nightly on MacBook Pro Retina display, and IE 10 in Windows 8 at 150% zoom, 200% zoom, and 140% and 180%-ratio Metro tablet sizes.
Internally this is still a bit of a hack; Linker::makeImageLink and Linker::makeThumbLink explicitly ask for 1.5x and 2x scaled versions and insert their URLs, if different, into the original thumbnail object which (in default handler) outputs the srcset. This means that a number of places that handle images differently won't see the higher-resolution versions, such as <gallery> and the large thumbnail on the File: description page.
At some point we may wish to redo some of how the MediaHandler stuff works so that requesting a single thumbnail automatically produces the extra sizes in all circumstances. We might also consider outputting a 'srcset' or multiple src sizes in 'imageinfo' API requests, which would make ApiForeignRepo/InstantCommons more efficient. (Currently it has to make three requests for each image to get the three sizes.)
Change-Id: Id80ebd07a1a9f401a2c2bfeb21aae987e5aa863b
2012-09-18 07:18:50 +00:00
|
|
|
}
|
2014-06-26 00:48:16 +00:00
|
|
|
if ( $thumb20 && !$thumb20->isError() && $thumb20->getUrl() !== $thumb->getUrl() ) {
|
2013-12-05 12:55:04 +00:00
|
|
|
$thumb->responsiveUrls['2'] = $thumb20->getUrl();
|
Initial stab at responsive images for screen densities.
* adds $wgResponsiveImages setting, defaulting to true, to enable the feature
* adds 'srcset' attribute with 1.5x and 2x URLs to image links and image thumbs
* adds jquery.hidpi plugin to check pixel density and implement partial 'srcset' polyfill
** $.devicePixelRatio() returns window.devicePixelRatio, with compat fallback for IE 10
** $().hidpi() performs a 'srcset' polyfill for browsers with no native 'srcset' support
* adds mediawiki.hidpi RL script to trigger hidpi loads after main images load
Note that this is a work in progress. There will be places where this doesn't yet work which output their imgs differently. If moving from a low to high-DPI screen on a MacBook Pro Retina display, you won't see images load until you reload.
Confirmed basic images and thumbs in wikitext appear to work in Safari 6, Chrome 21, Firefox 18 nightly on MacBook Pro Retina display, and IE 10 in Windows 8 at 150% zoom, 200% zoom, and 140% and 180%-ratio Metro tablet sizes.
Internally this is still a bit of a hack; Linker::makeImageLink and Linker::makeThumbLink explicitly ask for 1.5x and 2x scaled versions and insert their URLs, if different, into the original thumbnail object which (in default handler) outputs the srcset. This means that a number of places that handle images differently won't see the higher-resolution versions, such as <gallery> and the large thumbnail on the File: description page.
At some point we may wish to redo some of how the MediaHandler stuff works so that requesting a single thumbnail automatically produces the extra sizes in all circumstances. We might also consider outputting a 'srcset' or multiple src sizes in 'imageinfo' API requests, which would make ApiForeignRepo/InstantCommons more efficient. (Currently it has to make three requests for each image to get the three sizes.)
Change-Id: Id80ebd07a1a9f401a2c2bfeb21aae987e5aa863b
2012-09-18 07:18:50 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2005-04-17 08:30:15 +00:00
|
|
|
/**
|
2007-06-09 16:19:38 +00:00
|
|
|
* Make a "broken" link to an image
|
|
|
|
|
*
|
2016-05-04 12:48:58 +00:00
|
|
|
* @since 1.16.3
|
2019-04-15 12:47:32 +00:00
|
|
|
* @param LinkTarget $title
|
2014-04-22 11:07:02 +00:00
|
|
|
* @param string $label Link label (plain text)
|
|
|
|
|
* @param string $query Query string
|
2014-04-08 15:29:17 +00:00
|
|
|
* @param string $unused1 Unused parameter kept for b/c
|
|
|
|
|
* @param string $unused2 Unused parameter kept for b/c
|
2014-04-22 11:07:02 +00:00
|
|
|
* @param bool $time A file of a certain timestamp was requested
|
2018-02-13 22:51:22 +00:00
|
|
|
* @param array $handlerParams @since 1.36
|
2014-04-08 15:29:17 +00:00
|
|
|
* @return string
|
2005-04-17 08:30:15 +00:00
|
|
|
*/
|
2018-02-13 22:51:22 +00:00
|
|
|
public static function makeBrokenImageLinkObj(
|
|
|
|
|
$title, $label = '', $query = '', $unused1 = '', $unused2 = '',
|
|
|
|
|
$time = false, array $handlerParams = []
|
2014-05-15 15:38:28 +00:00
|
|
|
) {
|
2019-04-15 12:47:32 +00:00
|
|
|
if ( !$title instanceof LinkTarget ) {
|
|
|
|
|
wfWarn( __METHOD__ . ': Requires $title to be a LinkTarget object.' );
|
2012-08-31 03:40:19 +00:00
|
|
|
return "<!-- ERROR -->" . htmlspecialchars( $label );
|
2011-05-15 12:34:00 +00:00
|
|
|
}
|
2014-07-08 08:00:11 +00:00
|
|
|
|
2019-04-15 12:47:32 +00:00
|
|
|
$title = Title::castFromLinkTarget( $title );
|
2022-01-18 23:53:32 +00:00
|
|
|
$services = MediaWikiServices::getInstance();
|
|
|
|
|
$mainConfig = $services->getMainConfig();
|
2022-04-01 15:58:32 +00:00
|
|
|
$enableUploads = $mainConfig->get( MainConfigNames::EnableUploads );
|
|
|
|
|
$uploadMissingFileUrl = $mainConfig->get( MainConfigNames::UploadMissingFileUrl );
|
|
|
|
|
$uploadNavigationUrl = $mainConfig->get( MainConfigNames::UploadNavigationUrl );
|
2012-08-31 03:40:19 +00:00
|
|
|
if ( $label == '' ) {
|
|
|
|
|
$label = $title->getPrefixedText();
|
|
|
|
|
}
|
2018-02-13 22:51:22 +00:00
|
|
|
|
|
|
|
|
$html = Html::element( 'span', [
|
2022-03-17 19:45:29 +00:00
|
|
|
'class' => 'mw-broken-media',
|
2018-02-13 22:51:22 +00:00
|
|
|
// These data attributes are used to dynamically size the span, see T273013
|
|
|
|
|
'data-width' => $handlerParams['width'] ?? null,
|
|
|
|
|
'data-height' => $handlerParams['height'] ?? null,
|
|
|
|
|
], $label );
|
|
|
|
|
|
2022-04-01 15:58:32 +00:00
|
|
|
if ( $mainConfig->get( MainConfigNames::ParserEnableLegacyMediaDOM ) ) {
|
2022-01-25 05:23:23 +00:00
|
|
|
$html = htmlspecialchars( $label, ENT_COMPAT );
|
2018-02-13 22:51:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$repoGroup = $services->getRepoGroup();
|
2019-06-25 14:30:31 +00:00
|
|
|
$currentExists = $time
|
2019-08-19 20:45:43 +00:00
|
|
|
&& $repoGroup->findFile( $title ) !== false;
|
2010-09-04 22:25:59 +00:00
|
|
|
|
2022-01-06 18:44:56 +00:00
|
|
|
if ( ( $uploadMissingFileUrl || $uploadNavigationUrl || $enableUploads )
|
2014-05-15 15:38:28 +00:00
|
|
|
&& !$currentExists
|
|
|
|
|
) {
|
2022-03-23 22:35:48 +00:00
|
|
|
if (
|
|
|
|
|
$title->inNamespace( NS_FILE ) &&
|
|
|
|
|
$repoGroup->getLocalRepo()->checkRedirect( $title )
|
|
|
|
|
) {
|
2017-11-12 19:56:44 +00:00
|
|
|
// We already know it's a redirect, so mark it accordingly
|
2016-05-20 01:58:34 +00:00
|
|
|
return self::link(
|
|
|
|
|
$title,
|
2020-12-18 16:26:55 +00:00
|
|
|
$html,
|
2016-05-20 01:58:34 +00:00
|
|
|
[ 'class' => 'mw-redirect' ],
|
|
|
|
|
wfCgiToArray( $query ),
|
|
|
|
|
[ 'known', 'noclasses' ]
|
|
|
|
|
);
|
2007-06-09 16:19:38 +00:00
|
|
|
}
|
2020-12-18 16:26:55 +00:00
|
|
|
return Html::rawElement( 'a', [
|
2017-11-12 19:56:44 +00:00
|
|
|
'href' => self::getUploadUrl( $title, $query ),
|
|
|
|
|
'class' => 'new',
|
|
|
|
|
'title' => $title->getPrefixedText()
|
2020-12-18 16:26:55 +00:00
|
|
|
], $html );
|
2005-04-17 08:30:15 +00:00
|
|
|
}
|
2017-11-12 19:56:44 +00:00
|
|
|
return self::link(
|
|
|
|
|
$title,
|
2020-12-18 16:26:55 +00:00
|
|
|
$html,
|
2017-11-12 19:56:44 +00:00
|
|
|
[],
|
|
|
|
|
wfCgiToArray( $query ),
|
|
|
|
|
[ 'known', 'noclasses' ]
|
|
|
|
|
);
|
2005-04-17 08:30:15 +00:00
|
|
|
}
|
2010-08-26 19:40:29 +00:00
|
|
|
|
2010-01-12 19:43:03 +00:00
|
|
|
/**
|
|
|
|
|
* Get the URL to upload a certain file
|
2010-08-26 19:40:29 +00:00
|
|
|
*
|
2016-05-04 12:48:58 +00:00
|
|
|
* @since 1.16.3
|
2019-04-15 12:47:32 +00:00
|
|
|
* @param LinkTarget $destFile LinkTarget object of the file to upload
|
2014-04-08 15:29:17 +00:00
|
|
|
* @param string $query Urlencoded query string to prepend
|
|
|
|
|
* @return string Urlencoded URL
|
2010-01-12 19:43:03 +00:00
|
|
|
*/
|
2011-04-03 12:04:04 +00:00
|
|
|
protected static function getUploadUrl( $destFile, $query = '' ) {
|
2022-01-18 23:53:32 +00:00
|
|
|
$mainConfig = MediaWikiServices::getInstance()->getMainConfig();
|
2022-04-01 15:58:32 +00:00
|
|
|
$uploadMissingFileUrl = $mainConfig->get( MainConfigNames::UploadMissingFileUrl );
|
|
|
|
|
$uploadNavigationUrl = $mainConfig->get( MainConfigNames::UploadNavigationUrl );
|
2019-04-15 12:47:32 +00:00
|
|
|
$q = 'wpDestFile=' . Title::castFromLinkTarget( $destFile )->getPartialURL();
|
2013-04-20 22:49:30 +00:00
|
|
|
if ( $query != '' ) {
|
2010-01-12 19:43:03 +00:00
|
|
|
$q .= '&' . $query;
|
2013-04-20 22:49:30 +00:00
|
|
|
}
|
2010-01-12 19:43:03 +00:00
|
|
|
|
2022-01-06 18:44:56 +00:00
|
|
|
if ( $uploadMissingFileUrl ) {
|
|
|
|
|
return wfAppendQuery( $uploadMissingFileUrl, $q );
|
2019-02-09 07:25:57 +00:00
|
|
|
}
|
|
|
|
|
|
2022-01-06 18:44:56 +00:00
|
|
|
if ( $uploadNavigationUrl ) {
|
|
|
|
|
return wfAppendQuery( $uploadNavigationUrl, $q );
|
2010-08-26 19:40:29 +00:00
|
|
|
}
|
2019-02-09 07:25:57 +00:00
|
|
|
|
|
|
|
|
$upload = SpecialPage::getTitleFor( 'Upload' );
|
|
|
|
|
|
|
|
|
|
return $upload->getLocalURL( $q );
|
2010-01-12 19:43:03 +00:00
|
|
|
}
|
2006-01-07 13:31:29 +00:00
|
|
|
|
2005-02-21 01:56:50 +00:00
|
|
|
/**
|
|
|
|
|
* Create a direct link to a given uploaded file.
|
2006-11-08 07:12:03 +00:00
|
|
|
*
|
2016-05-04 12:48:58 +00:00
|
|
|
* @since 1.16.3
|
2019-04-15 12:47:32 +00:00
|
|
|
* @param LinkTarget $title
|
2014-04-08 15:29:17 +00:00
|
|
|
* @param string $html Pre-sanitized HTML
|
2019-05-14 17:00:34 +00:00
|
|
|
* @param string|false $time MW timestamp of file creation time
|
2014-04-08 15:29:17 +00:00
|
|
|
* @return string HTML
|
2011-03-23 03:13:37 +00:00
|
|
|
*/
|
2011-09-03 13:46:56 +00:00
|
|
|
public static function makeMediaLinkObj( $title, $html = '', $time = false ) {
|
2019-05-14 17:00:34 +00:00
|
|
|
$img = MediaWikiServices::getInstance()->getRepoGroup()->findFile(
|
|
|
|
|
$title, [ 'time' => $time ]
|
|
|
|
|
);
|
2011-09-03 13:46:56 +00:00
|
|
|
return self::makeMediaLinkFile( $title, $img, $html );
|
2011-03-23 03:13:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Create a direct link to a given uploaded file.
|
|
|
|
|
* This will make a broken link if $file is false.
|
|
|
|
|
*
|
2016-05-04 12:48:58 +00:00
|
|
|
* @since 1.16.3
|
2019-04-15 12:47:32 +00:00
|
|
|
* @param LinkTarget $title
|
2021-10-21 18:09:51 +00:00
|
|
|
* @param File|false $file File object or false
|
2014-04-08 15:29:17 +00:00
|
|
|
* @param string $html Pre-sanitized HTML
|
|
|
|
|
* @return string HTML
|
2006-11-08 07:12:03 +00:00
|
|
|
*
|
2005-02-21 01:56:50 +00:00
|
|
|
* @todo Handle invalid or missing images better.
|
|
|
|
|
*/
|
2019-04-15 12:47:32 +00:00
|
|
|
public static function makeMediaLinkFile( LinkTarget $title, $file, $html = '' ) {
|
2011-03-23 03:13:37 +00:00
|
|
|
if ( $file && $file->exists() ) {
|
2016-03-19 00:08:06 +00:00
|
|
|
$url = $file->getUrl();
|
2011-03-23 03:13:37 +00:00
|
|
|
$class = 'internal';
|
2004-12-18 06:29:23 +00:00
|
|
|
} else {
|
2011-04-03 11:44:11 +00:00
|
|
|
$url = self::getUploadUrl( $title );
|
2011-03-23 03:13:37 +00:00
|
|
|
$class = 'new';
|
|
|
|
|
}
|
2014-03-25 10:56:41 +00:00
|
|
|
|
|
|
|
|
$alt = $title->getText();
|
2011-09-03 13:46:56 +00:00
|
|
|
if ( $html == '' ) {
|
|
|
|
|
$html = $alt;
|
2004-12-18 06:29:23 +00:00
|
|
|
}
|
2014-03-25 10:56:41 +00:00
|
|
|
|
|
|
|
|
$ret = '';
|
2016-02-17 09:09:32 +00:00
|
|
|
$attribs = [
|
2014-03-25 10:56:41 +00:00
|
|
|
'href' => $url,
|
|
|
|
|
'class' => $class,
|
|
|
|
|
'title' => $alt
|
2016-02-17 09:09:32 +00:00
|
|
|
];
|
2014-03-25 10:56:41 +00:00
|
|
|
|
Hooks::run() call site migration
Migrate all callers of Hooks::run() to use the new
HookContainer/HookRunner system.
General principles:
* Use DI if it is already used. We're not changing the way state is
managed in this patch.
* HookContainer is always injected, not HookRunner. HookContainer
is a service, it's a more generic interface, it is the only
thing that provides isRegistered() which is needed in some cases,
and a HookRunner can be efficiently constructed from it
(confirmed by benchmark). Because HookContainer is needed
for object construction, it is also needed by all factories.
* "Ask your friendly local base class". Big hierarchies like
SpecialPage and ApiBase have getHookContainer() and getHookRunner()
methods in the base class, and classes that extend that base class
are not expected to know or care where the base class gets its
HookContainer from.
* ProtectedHookAccessorTrait provides protected getHookContainer() and
getHookRunner() methods, getting them from the global service
container. The point of this is to ease migration to DI by ensuring
that call sites ask their local friendly base class rather than
getting a HookRunner from the service container directly.
* Private $this->hookRunner. In some smaller classes where accessor
methods did not seem warranted, there is a private HookRunner property
which is accessed directly. Very rarely (two cases), there is a
protected property, for consistency with code that conventionally
assumes protected=private, but in cases where the class might actually
be overridden, a protected accessor is preferred over a protected
property.
* The last resort: Hooks::runner(). Mostly for static, file-scope and
global code. In a few cases it was used for objects with broken
construction schemes, out of horror or laziness.
Constructors with new required arguments:
* AuthManager
* BadFileLookup
* BlockManager
* ClassicInterwikiLookup
* ContentHandlerFactory
* ContentSecurityPolicy
* DefaultOptionsManager
* DerivedPageDataUpdater
* FullSearchResultWidget
* HtmlCacheUpdater
* LanguageFactory
* LanguageNameUtils
* LinkRenderer
* LinkRendererFactory
* LocalisationCache
* MagicWordFactory
* MessageCache
* NamespaceInfo
* PageEditStash
* PageHandlerFactory
* PageUpdater
* ParserFactory
* PermissionManager
* RevisionStore
* RevisionStoreFactory
* SearchEngineConfig
* SearchEngineFactory
* SearchFormWidget
* SearchNearMatcher
* SessionBackend
* SpecialPageFactory
* UserNameUtils
* UserOptionsManager
* WatchedItemQueryService
* WatchedItemStore
Constructors with new optional arguments:
* DefaultPreferencesFactory
* Language
* LinkHolderArray
* MovePage
* Parser
* ParserCache
* PasswordReset
* Router
setHookContainer() now required after construction:
* AuthenticationProvider
* ResourceLoaderModule
* SearchEngine
Change-Id: Id442b0dbe43aba84bd5cf801d86dedc768b082c7
2020-03-19 02:42:09 +00:00
|
|
|
if ( !Hooks::runner()->onLinkerMakeMediaLinkFile(
|
|
|
|
|
Title::castFromLinkTarget( $title ), $file, $html, $attribs, $ret )
|
|
|
|
|
) {
|
2014-05-15 15:38:28 +00:00
|
|
|
wfDebug( "Hook LinkerMakeMediaLinkFile changed the output of link "
|
2020-06-01 05:00:39 +00:00
|
|
|
. "with url {$url} and text {$html} to {$ret}" );
|
2014-03-25 10:56:41 +00:00
|
|
|
return $ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Html::rawElement( 'a', $attribs, $html );
|
2004-12-18 06:29:23 +00:00
|
|
|
}
|
|
|
|
|
|
2010-01-07 09:32:09 +00:00
|
|
|
/**
|
2011-05-29 14:01:47 +00:00
|
|
|
* Make a link to a special page given its name and, optionally,
|
2010-01-07 09:32:09 +00:00
|
|
|
* a message key from the link text.
|
2011-10-11 13:00:17 +00:00
|
|
|
* Usage example: Linker::specialLink( 'Recentchanges' )
|
2011-05-29 14:01:47 +00:00
|
|
|
*
|
2016-05-04 12:48:58 +00:00
|
|
|
* @since 1.16.3
|
2014-04-22 11:07:02 +00:00
|
|
|
* @param string $name
|
|
|
|
|
* @param string $key
|
2011-07-23 20:58:18 +00:00
|
|
|
* @return string
|
2009-10-26 14:25:48 +00:00
|
|
|
*/
|
2011-09-12 09:25:00 +00:00
|
|
|
public static function specialLink( $name, $key = '' ) {
|
2022-12-09 17:53:12 +00:00
|
|
|
$queryPos = strpos( $name, '?' );
|
|
|
|
|
if ( $queryPos !== false ) {
|
|
|
|
|
$getParams = wfCgiToArray( substr( $name, $queryPos + 1 ) );
|
|
|
|
|
$name = substr( $name, 0, $queryPos );
|
|
|
|
|
} else {
|
|
|
|
|
$getParams = [];
|
2022-11-29 21:48:59 +00:00
|
|
|
}
|
|
|
|
|
|
2022-12-09 17:53:12 +00:00
|
|
|
$slashPos = strpos( $name, '/' );
|
|
|
|
|
if ( $slashPos !== false ) {
|
|
|
|
|
$subpage = substr( $name, $slashPos + 1 );
|
|
|
|
|
$name = substr( $name, 0, $slashPos );
|
|
|
|
|
} else {
|
|
|
|
|
$subpage = false;
|
2022-11-29 21:48:59 +00:00
|
|
|
}
|
|
|
|
|
|
2011-05-29 14:01:47 +00:00
|
|
|
if ( $key == '' ) {
|
|
|
|
|
$key = strtolower( $name );
|
|
|
|
|
}
|
2010-09-03 21:53:02 +00:00
|
|
|
|
2022-11-29 21:48:59 +00:00
|
|
|
return self::linkKnown(
|
|
|
|
|
SpecialPage::getTitleFor( $name, $subpage ),
|
|
|
|
|
wfMessage( $key )->escaped(),
|
|
|
|
|
[],
|
|
|
|
|
$getParams
|
|
|
|
|
);
|
2004-12-18 06:29:23 +00:00
|
|
|
}
|
|
|
|
|
|
2009-03-09 15:53:08 +00:00
|
|
|
/**
|
|
|
|
|
* Make an external link
|
2019-02-17 11:32:50 +00:00
|
|
|
*
|
2016-05-04 12:48:58 +00:00
|
|
|
* @since 1.16.3. $title added in 1.21
|
2013-03-11 17:15:01 +00:00
|
|
|
* @param string $url URL to link to
|
2019-02-17 11:32:50 +00:00
|
|
|
* @param-taint $url escapes_html
|
2014-04-08 15:29:17 +00:00
|
|
|
* @param string $text Text of link
|
2019-02-17 11:32:50 +00:00
|
|
|
* @param-taint $text escapes_html
|
2014-04-08 15:29:17 +00:00
|
|
|
* @param bool $escape Do we escape the link text?
|
2019-02-17 11:32:50 +00:00
|
|
|
* @param-taint $escape none
|
2014-04-08 15:29:17 +00:00
|
|
|
* @param string $linktype Type of external link. Gets added to the classes
|
2019-02-17 11:32:50 +00:00
|
|
|
* @param-taint $linktype escapes_html
|
2014-04-08 15:29:17 +00:00
|
|
|
* @param array $attribs Array of extra attributes to <a>
|
2019-02-17 11:32:50 +00:00
|
|
|
* @param-taint $attribs escapes_html
|
2019-04-15 12:47:32 +00:00
|
|
|
* @param LinkTarget|null $title LinkTarget object used for title specific link attributes
|
2019-02-17 11:32:50 +00:00
|
|
|
* @param-taint $title none
|
2012-02-09 21:35:05 +00:00
|
|
|
* @return string
|
2009-03-09 15:53:08 +00:00
|
|
|
*/
|
2014-05-15 15:38:28 +00:00
|
|
|
public static function makeExternalLink( $url, $text, $escape = true,
|
2016-02-17 09:09:32 +00:00
|
|
|
$linktype = '', $attribs = [], $title = null
|
2014-05-15 15:38:28 +00:00
|
|
|
) {
|
2012-12-05 20:09:14 +00:00
|
|
|
global $wgTitle;
|
2022-08-23 08:38:49 +00:00
|
|
|
$class = 'external';
|
2011-09-19 18:38:38 +00:00
|
|
|
if ( $linktype ) {
|
2011-02-05 23:06:36 +00:00
|
|
|
$class .= " $linktype";
|
2010-08-26 19:40:29 +00:00
|
|
|
}
|
2011-09-19 18:38:38 +00:00
|
|
|
if ( isset( $attribs['class'] ) && $attribs['class'] ) {
|
2011-02-05 23:06:36 +00:00
|
|
|
$class .= " {$attribs['class']}";
|
|
|
|
|
}
|
|
|
|
|
$attribs['class'] = $class;
|
2011-05-25 15:39:47 +00:00
|
|
|
|
2010-08-26 19:40:29 +00:00
|
|
|
if ( $escape ) {
|
2022-01-25 05:23:23 +00:00
|
|
|
$text = htmlspecialchars( $text, ENT_COMPAT );
|
2004-12-18 06:29:23 +00:00
|
|
|
}
|
2012-12-05 20:09:14 +00:00
|
|
|
|
|
|
|
|
if ( !$title ) {
|
|
|
|
|
$title = $wgTitle;
|
|
|
|
|
}
|
2016-04-25 18:08:46 +00:00
|
|
|
$newRel = Parser::getExternalLinkRel( $url, $title );
|
|
|
|
|
if ( !isset( $attribs['rel'] ) || $attribs['rel'] === '' ) {
|
|
|
|
|
$attribs['rel'] = $newRel;
|
2022-07-26 01:03:31 +00:00
|
|
|
} elseif ( $newRel !== null ) {
|
2016-04-25 18:08:46 +00:00
|
|
|
// Merge the rel attributes.
|
|
|
|
|
$newRels = explode( ' ', $newRel );
|
|
|
|
|
$oldRels = explode( ' ', $attribs['rel'] );
|
|
|
|
|
$combined = array_unique( array_merge( $newRels, $oldRels ) );
|
|
|
|
|
$attribs['rel'] = implode( ' ', $combined );
|
|
|
|
|
}
|
2008-06-08 21:45:05 +00:00
|
|
|
$link = '';
|
Hooks::run() call site migration
Migrate all callers of Hooks::run() to use the new
HookContainer/HookRunner system.
General principles:
* Use DI if it is already used. We're not changing the way state is
managed in this patch.
* HookContainer is always injected, not HookRunner. HookContainer
is a service, it's a more generic interface, it is the only
thing that provides isRegistered() which is needed in some cases,
and a HookRunner can be efficiently constructed from it
(confirmed by benchmark). Because HookContainer is needed
for object construction, it is also needed by all factories.
* "Ask your friendly local base class". Big hierarchies like
SpecialPage and ApiBase have getHookContainer() and getHookRunner()
methods in the base class, and classes that extend that base class
are not expected to know or care where the base class gets its
HookContainer from.
* ProtectedHookAccessorTrait provides protected getHookContainer() and
getHookRunner() methods, getting them from the global service
container. The point of this is to ease migration to DI by ensuring
that call sites ask their local friendly base class rather than
getting a HookRunner from the service container directly.
* Private $this->hookRunner. In some smaller classes where accessor
methods did not seem warranted, there is a private HookRunner property
which is accessed directly. Very rarely (two cases), there is a
protected property, for consistency with code that conventionally
assumes protected=private, but in cases where the class might actually
be overridden, a protected accessor is preferred over a protected
property.
* The last resort: Hooks::runner(). Mostly for static, file-scope and
global code. In a few cases it was used for objects with broken
construction schemes, out of horror or laziness.
Constructors with new required arguments:
* AuthManager
* BadFileLookup
* BlockManager
* ClassicInterwikiLookup
* ContentHandlerFactory
* ContentSecurityPolicy
* DefaultOptionsManager
* DerivedPageDataUpdater
* FullSearchResultWidget
* HtmlCacheUpdater
* LanguageFactory
* LanguageNameUtils
* LinkRenderer
* LinkRendererFactory
* LocalisationCache
* MagicWordFactory
* MessageCache
* NamespaceInfo
* PageEditStash
* PageHandlerFactory
* PageUpdater
* ParserFactory
* PermissionManager
* RevisionStore
* RevisionStoreFactory
* SearchEngineConfig
* SearchEngineFactory
* SearchFormWidget
* SearchNearMatcher
* SessionBackend
* SpecialPageFactory
* UserNameUtils
* UserOptionsManager
* WatchedItemQueryService
* WatchedItemStore
Constructors with new optional arguments:
* DefaultPreferencesFactory
* Language
* LinkHolderArray
* MovePage
* Parser
* ParserCache
* PasswordReset
* Router
setHookContainer() now required after construction:
* AuthenticationProvider
* ResourceLoaderModule
* SearchEngine
Change-Id: Id442b0dbe43aba84bd5cf801d86dedc768b082c7
2020-03-19 02:42:09 +00:00
|
|
|
$success = Hooks::runner()->onLinkerMakeExternalLink(
|
|
|
|
|
$url, $text, $link, $attribs, $linktype );
|
2010-08-26 19:40:29 +00:00
|
|
|
if ( !$success ) {
|
2014-05-15 15:38:28 +00:00
|
|
|
wfDebug( "Hook LinkerMakeExternalLink changed the output of link "
|
2020-06-01 05:00:39 +00:00
|
|
|
. "with url {$url} and text {$text} to {$link}" );
|
2008-06-08 21:45:05 +00:00
|
|
|
return $link;
|
|
|
|
|
}
|
2011-02-05 23:06:36 +00:00
|
|
|
$attribs['href'] = $url;
|
|
|
|
|
return Html::rawElement( 'a', $attribs, $text );
|
2004-12-18 06:29:23 +00:00
|
|
|
}
|
|
|
|
|
|
2006-03-16 19:04:25 +00:00
|
|
|
/**
|
|
|
|
|
* Make user link (or user contributions for unregistered users)
|
2021-04-19 16:55:41 +00:00
|
|
|
*
|
|
|
|
|
* This method produces HTML that requires CSS styles in mediawiki.interface.helpers.styles.
|
|
|
|
|
*
|
2014-04-08 15:29:17 +00:00
|
|
|
* @param int $userId User id in database.
|
|
|
|
|
* @param string $userName User name in database.
|
2019-09-15 14:40:42 +00:00
|
|
|
* @param string|false $altUserName Text to display instead of the user name (optional)
|
2022-11-09 16:24:30 +00:00
|
|
|
* @param string[] $attributes Extra HTML attributes. See Linker::link.
|
2014-04-08 15:29:17 +00:00
|
|
|
* @return string HTML fragment
|
2022-11-09 16:24:30 +00:00
|
|
|
* @since 1.16.3. $altUserName was added in 1.19. $attributes was added in 1.40.
|
2006-03-16 19:04:25 +00:00
|
|
|
*/
|
2022-11-09 16:24:30 +00:00
|
|
|
public static function userLink(
|
|
|
|
|
$userId,
|
|
|
|
|
$userName,
|
|
|
|
|
$altUserName = false,
|
|
|
|
|
$attributes = []
|
|
|
|
|
) {
|
2019-05-22 20:12:11 +00:00
|
|
|
if ( $userName === '' || $userName === false || $userName === null ) {
|
2019-05-25 02:14:42 +00:00
|
|
|
wfDebug( __METHOD__ . ' received an empty username. Are there database errors ' .
|
2019-05-06 08:58:09 +00:00
|
|
|
'that need to be fixed?' );
|
|
|
|
|
return wfMessage( 'empty-username' )->parse();
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-24 00:24:34 +00:00
|
|
|
$classes = 'mw-userlink';
|
2010-08-26 19:40:29 +00:00
|
|
|
if ( $userId == 0 ) {
|
2018-01-11 12:20:35 +00:00
|
|
|
$page = ExternalUserNames::getUserLinkTitle( $userName );
|
|
|
|
|
|
|
|
|
|
if ( ExternalUserNames::isExternal( $userName ) ) {
|
2017-10-25 19:26:53 +00:00
|
|
|
$classes .= ' mw-extuserlink';
|
2018-01-11 12:20:35 +00:00
|
|
|
} elseif ( $altUserName === false ) {
|
2019-06-25 18:53:15 +00:00
|
|
|
$altUserName = IPUtils::prettifyIP( $userName );
|
2012-05-22 23:30:07 +00:00
|
|
|
}
|
2017-02-20 22:44:19 +00:00
|
|
|
$classes .= ' mw-anonuserlink'; // Separate link class for anons (T45179)
|
2006-03-16 19:04:25 +00:00
|
|
|
} else {
|
2023-03-06 06:17:26 +00:00
|
|
|
$services = MediaWikiServices::getInstance();
|
2022-11-09 16:24:30 +00:00
|
|
|
if (
|
2023-03-06 06:17:26 +00:00
|
|
|
$services->getTempUserConfig()->isReservedName( $userName )
|
2022-11-09 16:24:30 +00:00
|
|
|
) {
|
|
|
|
|
$classes .= ' mw-tempuserlink';
|
2023-03-06 06:17:26 +00:00
|
|
|
$page = SpecialPage::getTitleFor( 'Contributions', $userName );
|
|
|
|
|
} else {
|
|
|
|
|
$page = TitleValue::tryNew( NS_USER, strtr( $userName, ' ', '_' ) );
|
2022-11-09 16:24:30 +00:00
|
|
|
}
|
2006-03-16 19:04:25 +00:00
|
|
|
}
|
2012-01-05 09:33:46 +00:00
|
|
|
|
2016-08-31 20:58:05 +00:00
|
|
|
// Wrap the output with <bdi> tags for directionality isolation
|
2017-10-25 19:26:53 +00:00
|
|
|
$linkText =
|
|
|
|
|
'<bdi>' . htmlspecialchars( $altUserName !== false ? $altUserName : $userName ) . '</bdi>';
|
|
|
|
|
|
2022-11-09 16:24:30 +00:00
|
|
|
if ( isset( $attributes['class'] ) ) {
|
|
|
|
|
$attributes['class'] .= ' ' . $classes;
|
|
|
|
|
} else {
|
|
|
|
|
$attributes['class'] = $classes;
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-25 19:26:53 +00:00
|
|
|
return $page
|
2022-11-09 16:24:30 +00:00
|
|
|
? self::link( $page, $linkText, $attributes )
|
|
|
|
|
: Html::rawElement( 'span', $attributes, $linkText );
|
2006-03-16 19:04:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2007-08-06 07:09:59 +00:00
|
|
|
* Generate standard user tool links (talk, contributions, block link, etc.)
|
|
|
|
|
*
|
2016-05-04 12:48:58 +00:00
|
|
|
* @since 1.16.3
|
2014-04-08 15:29:17 +00:00
|
|
|
* @param int $userId User identifier
|
|
|
|
|
* @param string $userText User name or IP address
|
|
|
|
|
* @param bool $redContribsWhenNoEdits Should the contributions link be
|
|
|
|
|
* red if the user has no edits?
|
2014-05-15 15:38:28 +00:00
|
|
|
* @param int $flags Customisation flags (e.g. Linker::TOOL_LINKS_NOBLOCK
|
|
|
|
|
* and Linker::TOOL_LINKS_EMAIL).
|
2018-06-26 21:14:43 +00:00
|
|
|
* @param int|null $edits User edit count (optional, for performance)
|
2018-11-21 23:15:19 +00:00
|
|
|
* @param bool $useParentheses (optional) Wrap comments in parentheses where needed
|
2014-04-08 15:29:17 +00:00
|
|
|
* @return string HTML fragment
|
2006-03-16 19:04:25 +00:00
|
|
|
*/
|
2011-04-03 12:04:04 +00:00
|
|
|
public static function userToolLinks(
|
2018-11-21 23:15:19 +00:00
|
|
|
$userId, $userText, $redContribsWhenNoEdits = false, $flags = 0, $edits = null,
|
|
|
|
|
$useParentheses = true
|
2011-03-21 16:15:56 +00:00
|
|
|
) {
|
2019-05-06 08:58:09 +00:00
|
|
|
if ( $userText === '' ) {
|
2019-05-25 02:14:42 +00:00
|
|
|
wfDebug( __METHOD__ . ' received an empty username. Are there database errors ' .
|
2019-05-06 08:58:09 +00:00
|
|
|
'that need to be fixed?' );
|
|
|
|
|
return ' ' . wfMessage( 'empty-username' )->parse();
|
|
|
|
|
}
|
2022-01-06 18:44:56 +00:00
|
|
|
global $wgLang;
|
2022-04-01 15:58:32 +00:00
|
|
|
$disableAnonTalk = MediaWikiServices::getInstance()->getMainConfig()->get( MainConfigNames::DisableAnonTalk );
|
2022-01-06 18:44:56 +00:00
|
|
|
$talkable = !( $disableAnonTalk && $userId == 0 );
|
2011-09-11 08:14:46 +00:00
|
|
|
$blockable = !( $flags & self::TOOL_LINKS_NOBLOCK );
|
|
|
|
|
$addEmailLink = $flags & self::TOOL_LINKS_EMAIL && $userId;
|
2006-03-16 19:04:25 +00:00
|
|
|
|
2018-01-11 12:20:35 +00:00
|
|
|
if ( $userId == 0 && ExternalUserNames::isExternal( $userText ) ) {
|
2017-10-25 19:26:53 +00:00
|
|
|
// No tools for an external user
|
|
|
|
|
return '';
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-17 09:09:32 +00:00
|
|
|
$items = [];
|
2010-08-26 19:40:29 +00:00
|
|
|
if ( $talkable ) {
|
2011-04-03 11:44:11 +00:00
|
|
|
$items[] = self::userTalkLink( $userId, $userText );
|
2006-03-16 19:04:25 +00:00
|
|
|
}
|
2010-08-26 19:40:29 +00:00
|
|
|
if ( $userId ) {
|
2007-01-23 19:56:01 +00:00
|
|
|
// check if the user has an edit
|
2016-02-17 09:09:32 +00:00
|
|
|
$attribs = [];
|
2017-02-17 12:59:16 +00:00
|
|
|
$attribs['class'] = 'mw-usertoollinks-contribs';
|
2010-08-26 19:40:29 +00:00
|
|
|
if ( $redContribsWhenNoEdits ) {
|
2012-10-03 15:22:40 +00:00
|
|
|
if ( intval( $edits ) === 0 && $edits !== 0 ) {
|
|
|
|
|
$user = User::newFromId( $userId );
|
|
|
|
|
$edits = $user->getEditCount();
|
|
|
|
|
}
|
|
|
|
|
if ( $edits === 0 ) {
|
2022-06-16 15:39:35 +00:00
|
|
|
// Note: "new" class is inappropriate here, as "new" class
|
|
|
|
|
// should only be used for pages that do not exist.
|
|
|
|
|
$attribs['class'] .= ' mw-usertoollinks-contribs-no-edits';
|
2008-07-30 21:02:28 +00:00
|
|
|
}
|
2007-01-23 19:56:01 +00:00
|
|
|
}
|
2006-12-02 08:52:54 +00:00
|
|
|
$contribsPage = SpecialPage::getTitleFor( 'Contributions', $userText );
|
2007-01-23 19:56:01 +00:00
|
|
|
|
2012-08-19 20:44:29 +00:00
|
|
|
$items[] = self::link( $contribsPage, wfMessage( 'contribslink' )->escaped(), $attribs );
|
2006-03-16 21:17:32 +00:00
|
|
|
}
|
2021-03-04 20:28:38 +00:00
|
|
|
$userCanBlock = RequestContext::getMain()->getAuthority()->isAllowed( 'block' );
|
2019-08-16 18:13:56 +00:00
|
|
|
if ( $blockable && $userCanBlock ) {
|
2011-04-03 11:44:11 +00:00
|
|
|
$items[] = self::blockLink( $userId, $userText );
|
2006-03-16 19:04:25 +00:00
|
|
|
}
|
|
|
|
|
|
2021-03-04 20:28:38 +00:00
|
|
|
$user = RequestContext::getMain()->getUser();
|
2020-02-19 23:38:21 +00:00
|
|
|
if ( $addEmailLink && $user->canSendEmail() ) {
|
2011-09-07 12:12:24 +00:00
|
|
|
$items[] = self::emailLink( $userId, $userText );
|
|
|
|
|
}
|
|
|
|
|
|
Hooks::run() call site migration
Migrate all callers of Hooks::run() to use the new
HookContainer/HookRunner system.
General principles:
* Use DI if it is already used. We're not changing the way state is
managed in this patch.
* HookContainer is always injected, not HookRunner. HookContainer
is a service, it's a more generic interface, it is the only
thing that provides isRegistered() which is needed in some cases,
and a HookRunner can be efficiently constructed from it
(confirmed by benchmark). Because HookContainer is needed
for object construction, it is also needed by all factories.
* "Ask your friendly local base class". Big hierarchies like
SpecialPage and ApiBase have getHookContainer() and getHookRunner()
methods in the base class, and classes that extend that base class
are not expected to know or care where the base class gets its
HookContainer from.
* ProtectedHookAccessorTrait provides protected getHookContainer() and
getHookRunner() methods, getting them from the global service
container. The point of this is to ease migration to DI by ensuring
that call sites ask their local friendly base class rather than
getting a HookRunner from the service container directly.
* Private $this->hookRunner. In some smaller classes where accessor
methods did not seem warranted, there is a private HookRunner property
which is accessed directly. Very rarely (two cases), there is a
protected property, for consistency with code that conventionally
assumes protected=private, but in cases where the class might actually
be overridden, a protected accessor is preferred over a protected
property.
* The last resort: Hooks::runner(). Mostly for static, file-scope and
global code. In a few cases it was used for objects with broken
construction schemes, out of horror or laziness.
Constructors with new required arguments:
* AuthManager
* BadFileLookup
* BlockManager
* ClassicInterwikiLookup
* ContentHandlerFactory
* ContentSecurityPolicy
* DefaultOptionsManager
* DerivedPageDataUpdater
* FullSearchResultWidget
* HtmlCacheUpdater
* LanguageFactory
* LanguageNameUtils
* LinkRenderer
* LinkRendererFactory
* LocalisationCache
* MagicWordFactory
* MessageCache
* NamespaceInfo
* PageEditStash
* PageHandlerFactory
* PageUpdater
* ParserFactory
* PermissionManager
* RevisionStore
* RevisionStoreFactory
* SearchEngineConfig
* SearchEngineFactory
* SearchFormWidget
* SearchNearMatcher
* SessionBackend
* SpecialPageFactory
* UserNameUtils
* UserOptionsManager
* WatchedItemQueryService
* WatchedItemStore
Constructors with new optional arguments:
* DefaultPreferencesFactory
* Language
* LinkHolderArray
* MovePage
* Parser
* ParserCache
* PasswordReset
* Router
setHookContainer() now required after construction:
* AuthenticationProvider
* ResourceLoaderModule
* SearchEngine
Change-Id: Id442b0dbe43aba84bd5cf801d86dedc768b082c7
2020-03-19 02:42:09 +00:00
|
|
|
Hooks::runner()->onUserToolLinksEdit( $userId, $userText, $items );
|
2011-11-20 15:16:54 +00:00
|
|
|
|
2019-01-03 10:13:19 +00:00
|
|
|
if ( !$items ) {
|
2006-03-16 19:04:25 +00:00
|
|
|
return '';
|
|
|
|
|
}
|
2019-01-03 10:13:19 +00:00
|
|
|
|
|
|
|
|
if ( $useParentheses ) {
|
|
|
|
|
return wfMessage( 'word-separator' )->escaped()
|
|
|
|
|
. '<span class="mw-usertoollinks">'
|
|
|
|
|
. wfMessage( 'parentheses' )->rawParams( $wgLang->pipeList( $items ) )->escaped()
|
|
|
|
|
. '</span>';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$tools = [];
|
|
|
|
|
foreach ( $items as $tool ) {
|
|
|
|
|
$tools[] = Html::rawElement( 'span', [], $tool );
|
|
|
|
|
}
|
|
|
|
|
return ' <span class="mw-usertoollinks mw-changeslist-links">' .
|
|
|
|
|
implode( ' ', $tools ) . '</span>';
|
2006-03-16 19:04:25 +00:00
|
|
|
}
|
|
|
|
|
|
2007-01-23 19:56:01 +00:00
|
|
|
/**
|
|
|
|
|
* Alias for userToolLinks( $userId, $userText, true );
|
2016-05-04 12:48:58 +00:00
|
|
|
* @since 1.16.3
|
2014-04-08 15:29:17 +00:00
|
|
|
* @param int $userId User identifier
|
|
|
|
|
* @param string $userText User name or IP address
|
2018-06-26 21:14:43 +00:00
|
|
|
* @param int|null $edits User edit count (optional, for performance)
|
2019-04-19 21:49:40 +00:00
|
|
|
* @param bool $useParentheses (optional) Wrap comments in parentheses where needed
|
2014-04-08 15:29:17 +00:00
|
|
|
* @return string
|
2007-01-23 19:56:01 +00:00
|
|
|
*/
|
2019-04-19 21:49:40 +00:00
|
|
|
public static function userToolLinksRedContribs(
|
|
|
|
|
$userId, $userText, $edits = null, $useParentheses = true
|
|
|
|
|
) {
|
|
|
|
|
return self::userToolLinks( $userId, $userText, true, 0, $edits, $useParentheses );
|
2007-01-23 19:56:01 +00:00
|
|
|
}
|
|
|
|
|
|
2006-03-16 19:04:25 +00:00
|
|
|
/**
|
2016-05-04 12:48:58 +00:00
|
|
|
* @since 1.16.3
|
2014-04-08 15:29:17 +00:00
|
|
|
* @param int $userId User id in database.
|
|
|
|
|
* @param string $userText User name in database.
|
|
|
|
|
* @return string HTML fragment with user talk link
|
2006-03-16 19:04:25 +00:00
|
|
|
*/
|
2011-09-07 17:00:46 +00:00
|
|
|
public static function userTalkLink( $userId, $userText ) {
|
2019-05-06 08:58:09 +00:00
|
|
|
if ( $userText === '' ) {
|
2019-05-25 02:14:42 +00:00
|
|
|
wfDebug( __METHOD__ . ' received an empty username. Are there database errors ' .
|
2019-05-06 08:58:09 +00:00
|
|
|
'that need to be fixed?' );
|
|
|
|
|
return wfMessage( 'empty-username' )->parse();
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-02 12:33:48 +00:00
|
|
|
$userTalkPage = TitleValue::tryNew( NS_USER_TALK, strtr( $userText, ' ', '_' ) );
|
2019-08-30 17:56:27 +00:00
|
|
|
$moreLinkAttribs = [ 'class' => 'mw-usertoollinks-talk' ];
|
2019-10-02 12:33:48 +00:00
|
|
|
$linkText = wfMessage( 'talkpagelinktext' )->escaped();
|
2019-02-09 07:25:57 +00:00
|
|
|
|
2019-10-02 12:33:48 +00:00
|
|
|
return $userTalkPage
|
|
|
|
|
? self::link( $userTalkPage, $linkText, $moreLinkAttribs )
|
|
|
|
|
: Html::rawElement( 'span', $moreLinkAttribs, $linkText );
|
2006-03-16 19:04:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2016-05-04 12:48:58 +00:00
|
|
|
* @since 1.16.3
|
2017-12-28 15:06:10 +00:00
|
|
|
* @param int $userId
|
2014-04-08 15:29:17 +00:00
|
|
|
* @param string $userText User name in database.
|
|
|
|
|
* @return string HTML fragment with block link
|
2006-03-16 19:04:25 +00:00
|
|
|
*/
|
2011-09-07 17:00:46 +00:00
|
|
|
public static function blockLink( $userId, $userText ) {
|
2019-05-06 08:58:09 +00:00
|
|
|
if ( $userText === '' ) {
|
2019-05-25 02:14:42 +00:00
|
|
|
wfDebug( __METHOD__ . ' received an empty username. Are there database errors ' .
|
2019-05-06 08:58:09 +00:00
|
|
|
'that need to be fixed?' );
|
|
|
|
|
return wfMessage( 'empty-username' )->parse();
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-12 23:22:34 +00:00
|
|
|
$blockPage = SpecialPage::getTitleFor( 'Block', $userText );
|
2019-08-30 17:56:27 +00:00
|
|
|
$moreLinkAttribs = [ 'class' => 'mw-usertoollinks-block' ];
|
2019-02-09 07:25:57 +00:00
|
|
|
|
|
|
|
|
return self::link( $blockPage,
|
|
|
|
|
wfMessage( 'blocklink' )->escaped(),
|
|
|
|
|
$moreLinkAttribs
|
|
|
|
|
);
|
2006-03-16 19:04:25 +00:00
|
|
|
}
|
2008-04-14 07:45:50 +00:00
|
|
|
|
2011-09-07 12:12:24 +00:00
|
|
|
/**
|
2017-12-28 15:06:10 +00:00
|
|
|
* @param int $userId
|
2014-04-08 15:29:17 +00:00
|
|
|
* @param string $userText User name in database.
|
|
|
|
|
* @return string HTML fragment with e-mail user link
|
2011-09-07 12:12:24 +00:00
|
|
|
*/
|
2011-09-07 17:00:46 +00:00
|
|
|
public static function emailLink( $userId, $userText ) {
|
2019-05-06 08:58:09 +00:00
|
|
|
if ( $userText === '' ) {
|
|
|
|
|
wfLogWarning( __METHOD__ . ' received an empty username. Are there database errors ' .
|
|
|
|
|
'that need to be fixed?' );
|
|
|
|
|
return wfMessage( 'empty-username' )->parse();
|
|
|
|
|
}
|
|
|
|
|
|
2011-09-07 13:35:01 +00:00
|
|
|
$emailPage = SpecialPage::getTitleFor( 'Emailuser', $userText );
|
2019-08-30 17:56:27 +00:00
|
|
|
$moreLinkAttribs = [ 'class' => 'mw-usertoollinks-mail' ];
|
2019-02-09 07:25:57 +00:00
|
|
|
return self::link( $emailPage,
|
|
|
|
|
wfMessage( 'emaillink' )->escaped(),
|
|
|
|
|
$moreLinkAttribs
|
|
|
|
|
);
|
2011-09-07 12:12:24 +00:00
|
|
|
}
|
|
|
|
|
|
2006-03-16 19:04:25 +00:00
|
|
|
/**
|
|
|
|
|
* Generate a user link if the current user is allowed to view it
|
2021-04-19 16:55:41 +00:00
|
|
|
*
|
|
|
|
|
* This method produces HTML that requires CSS styles in mediawiki.interface.helpers.styles.
|
|
|
|
|
*
|
2016-05-04 12:48:58 +00:00
|
|
|
* @since 1.16.3
|
2021-04-21 15:43:13 +00:00
|
|
|
* @param RevisionRecord $revRecord (Switched from the old Revision class to RevisionRecord
|
|
|
|
|
* since 1.35)
|
2014-04-08 15:29:17 +00:00
|
|
|
* @param bool $isPublic Show only if all users can see it
|
2014-04-22 11:07:02 +00:00
|
|
|
* @return string HTML fragment
|
2006-03-16 19:04:25 +00:00
|
|
|
*/
|
2021-04-21 15:43:13 +00:00
|
|
|
public static function revUserLink( RevisionRecord $revRecord, $isPublic = false ) {
|
2021-02-24 15:46:13 +00:00
|
|
|
// TODO inject authority
|
|
|
|
|
$authority = RequestContext::getMain()->getAuthority();
|
2020-03-11 01:41:49 +00:00
|
|
|
|
2020-04-06 22:25:41 +00:00
|
|
|
if ( $revRecord->isDeleted( RevisionRecord::DELETED_USER ) && $isPublic ) {
|
2012-08-19 20:44:29 +00:00
|
|
|
$link = wfMessage( 'rev-deleted-user' )->escaped();
|
2021-02-24 15:46:13 +00:00
|
|
|
} elseif ( $revRecord->userCan( RevisionRecord::DELETED_USER, $authority ) ) {
|
|
|
|
|
$revUser = $revRecord->getUser( RevisionRecord::FOR_THIS_USER, $authority );
|
2020-03-23 05:39:32 +00:00
|
|
|
$link = self::userLink(
|
2020-04-06 22:25:41 +00:00
|
|
|
$revUser ? $revUser->getId() : 0,
|
|
|
|
|
$revUser ? $revUser->getName() : ''
|
2020-03-23 05:39:32 +00:00
|
|
|
);
|
2006-03-16 19:04:25 +00:00
|
|
|
} else {
|
2012-08-19 20:44:29 +00:00
|
|
|
$link = wfMessage( 'rev-deleted-user' )->escaped();
|
2006-03-16 19:04:25 +00:00
|
|
|
}
|
2020-04-06 22:25:41 +00:00
|
|
|
if ( $revRecord->isDeleted( RevisionRecord::DELETED_USER ) ) {
|
2021-04-13 06:27:39 +00:00
|
|
|
$class = self::getRevisionDeletedClass( $revRecord );
|
|
|
|
|
return '<span class="' . $class . '">' . $link . '</span>';
|
2006-03-16 19:04:25 +00:00
|
|
|
}
|
|
|
|
|
return $link;
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-13 06:27:39 +00:00
|
|
|
/**
|
|
|
|
|
* Returns css class of a deleted revision
|
|
|
|
|
* @param RevisionRecord $revisionRecord
|
|
|
|
|
* @return string 'history-deleted', 'mw-history-suppressed' added if suppressed too
|
|
|
|
|
* @since 1.37
|
|
|
|
|
*/
|
2021-07-22 03:11:47 +00:00
|
|
|
public static function getRevisionDeletedClass( RevisionRecord $revisionRecord ): string {
|
2021-04-13 06:27:39 +00:00
|
|
|
$class = 'history-deleted';
|
|
|
|
|
if ( $revisionRecord->isDeleted( RevisionRecord::DELETED_RESTRICTED ) ) {
|
|
|
|
|
$class .= ' mw-history-suppressed';
|
|
|
|
|
}
|
|
|
|
|
return $class;
|
|
|
|
|
}
|
|
|
|
|
|
2006-03-16 19:04:25 +00:00
|
|
|
/**
|
|
|
|
|
* Generate a user tool link cluster if the current user is allowed to view it
|
2021-04-19 16:55:41 +00:00
|
|
|
*
|
|
|
|
|
* This method produces HTML that requires CSS styles in mediawiki.interface.helpers.styles.
|
|
|
|
|
*
|
2016-05-04 12:48:58 +00:00
|
|
|
* @since 1.16.3
|
2021-04-21 15:43:13 +00:00
|
|
|
* @param RevisionRecord $revRecord (Switched from the old Revision class to RevisionRecord
|
|
|
|
|
* since 1.35)
|
2014-04-08 15:29:17 +00:00
|
|
|
* @param bool $isPublic Show only if all users can see it
|
2019-02-13 22:18:31 +00:00
|
|
|
* @param bool $useParentheses (optional) Wrap comments in parentheses where needed
|
2006-03-16 19:04:25 +00:00
|
|
|
* @return string HTML
|
|
|
|
|
*/
|
2021-04-21 15:43:13 +00:00
|
|
|
public static function revUserTools(
|
|
|
|
|
RevisionRecord $revRecord,
|
|
|
|
|
$isPublic = false,
|
|
|
|
|
$useParentheses = true
|
|
|
|
|
) {
|
2021-02-24 15:46:13 +00:00
|
|
|
// TODO inject authority
|
|
|
|
|
$authority = RequestContext::getMain()->getAuthority();
|
2020-03-11 01:41:49 +00:00
|
|
|
|
2021-02-24 15:46:13 +00:00
|
|
|
if ( $revRecord->userCan( RevisionRecord::DELETED_USER, $authority ) &&
|
2020-04-06 22:25:41 +00:00
|
|
|
( !$revRecord->isDeleted( RevisionRecord::DELETED_USER ) || !$isPublic )
|
2019-06-10 22:26:24 +00:00
|
|
|
) {
|
2021-02-24 15:46:13 +00:00
|
|
|
$revUser = $revRecord->getUser( RevisionRecord::FOR_THIS_USER, $authority );
|
2020-04-06 22:25:41 +00:00
|
|
|
$userId = $revUser ? $revUser->getId() : 0;
|
|
|
|
|
$userText = $revUser ? $revUser->getName() : '';
|
|
|
|
|
|
|
|
|
|
if ( $userId || $userText !== '' ) {
|
2023-01-10 13:33:59 +00:00
|
|
|
$link = self::userLink(
|
|
|
|
|
$userId,
|
|
|
|
|
$userText,
|
|
|
|
|
false,
|
|
|
|
|
[ 'data-mw-revid' => $revRecord->getId() ]
|
|
|
|
|
) . self::userToolLinks(
|
|
|
|
|
$userId,
|
|
|
|
|
$userText,
|
|
|
|
|
false,
|
|
|
|
|
0,
|
|
|
|
|
null,
|
|
|
|
|
$useParentheses
|
|
|
|
|
);
|
2019-06-10 22:26:24 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( !isset( $link ) ) {
|
2012-08-19 20:44:29 +00:00
|
|
|
$link = wfMessage( 'rev-deleted-user' )->escaped();
|
2006-03-16 19:04:25 +00:00
|
|
|
}
|
2019-06-10 22:26:24 +00:00
|
|
|
|
2020-04-06 22:25:41 +00:00
|
|
|
if ( $revRecord->isDeleted( RevisionRecord::DELETED_USER ) ) {
|
2021-04-13 06:27:39 +00:00
|
|
|
$class = self::getRevisionDeletedClass( $revRecord );
|
|
|
|
|
return ' <span class="' . $class . ' mw-userlink">' . $link . '</span>';
|
2007-03-14 15:50:06 +00:00
|
|
|
}
|
|
|
|
|
return $link;
|
|
|
|
|
}
|
2008-04-14 07:45:50 +00:00
|
|
|
|
2021-10-04 03:32:27 +00:00
|
|
|
/**
|
|
|
|
|
* Helper function to expand local links. Mostly used in action=render
|
|
|
|
|
*
|
|
|
|
|
* @since 1.38
|
|
|
|
|
* @unstable
|
|
|
|
|
*
|
|
|
|
|
* @param string $html
|
|
|
|
|
*
|
|
|
|
|
* @return string HTML
|
|
|
|
|
*/
|
|
|
|
|
public static function expandLocalLinks( string $html ) {
|
2022-12-29 18:41:26 +00:00
|
|
|
return HtmlHelper::modifyElements(
|
|
|
|
|
$html,
|
|
|
|
|
static function ( SerializerNode $node ): bool {
|
|
|
|
|
return $node->name === 'a' && isset( $node->attrs['href'] );
|
|
|
|
|
},
|
|
|
|
|
static function ( SerializerNode $node ): SerializerNode {
|
|
|
|
|
$node->attrs['href'] =
|
|
|
|
|
wfExpandUrl( $node->attrs['href'], PROTO_RELATIVE );
|
|
|
|
|
return $node;
|
|
|
|
|
}
|
|
|
|
|
);
|
2021-10-04 03:32:27 +00:00
|
|
|
}
|
|
|
|
|
|
2004-12-18 06:29:23 +00:00
|
|
|
/**
|
|
|
|
|
* This function is called by all recent changes variants, by the page history,
|
|
|
|
|
* and by the user contributions list. It is responsible for formatting edit
|
2012-12-19 22:20:54 +00:00
|
|
|
* summaries. It escapes any HTML in the summary, but adds some CSS to format
|
2004-12-18 06:29:23 +00:00
|
|
|
* auto-generated comments (from section editing) and formats [[wikilinks]].
|
2006-11-08 07:12:03 +00:00
|
|
|
*
|
2021-04-19 16:55:41 +00:00
|
|
|
* This method produces HTML that can require CSS styles in mediawiki.interface.helpers.styles.
|
|
|
|
|
*
|
2016-05-04 12:48:58 +00:00
|
|
|
* @since 1.16.3. $wikiId added in 1.26
|
Introduce CommentFormatter
CommentParser:
* Move comment formatting backend from Linker to a CommentParser service.
Allow link existence and file existence to be batched.
* Rename $local to $samePage since I think that is clearer.
* Rename $title to $selfLinkTarget since it was unclear what the title
was used for.
* Rename the "autocomment" concept to "section link" in public
interfaces, although the old term remains in CSS classes.
* Keep unsafe HTML pass-through in separate "unsafe" methods, for easier
static analysis and code review.
CommentFormatter:
* Add CommentFormatter and RowCommentFormatter services as a usable
frontend for comment batches, and to replace the Linker static methods.
* Provide fluent and parametric interfaces.
Linker:
* Remove Linker::makeCommentLink() without deprecation -- nothing calls
it and it is obviously an internal helper.
* Soft-deprecate Linker methods formatComment(), formatLinksInComment(),
commentBlock() and revComment().
Caller migration:
* CommentFormatter single: Linker, RollbackAction, ApiComparePages,
ApiParse
* CommentFormatter parametric batch: ImageHistoryPseudoPager
* CommentFormatter fluent batch: ApiQueryFilearchive
* RowCommentFormatter sequential: History feed, BlocklistPager,
ProtectedPagesPager, ApiQueryProtectedTitles
* RowCommentFormatter with index: ChangesFeed, ChangesList,
ApiQueryDeletedrevs, ApiQueryLogEvents, ApiQueryRecentChanges
* RevisionCommentBatch: HistoryPager, ContribsPager
Bug: T285917
Change-Id: Ia3fd50a4a13138ba5003d884962da24746d562d0
2021-07-01 06:55:03 +00:00
|
|
|
* @deprecated since 1.38 use CommentFormatter
|
2006-11-08 07:12:03 +00:00
|
|
|
*
|
2014-04-08 15:29:17 +00:00
|
|
|
* @param string $comment
|
2019-04-15 12:47:32 +00:00
|
|
|
* @param LinkTarget|null $title LinkTarget object (to generate link to the section in
|
|
|
|
|
* autocomment) or null
|
2014-04-08 15:29:17 +00:00
|
|
|
* @param bool $local Whether section links should refer to local page
|
2015-09-26 16:31:24 +00:00
|
|
|
* @param string|null $wikiId Id (as used by WikiMap) of the wiki to generate links to.
|
|
|
|
|
* For use with external changes.
|
2015-09-07 17:02:24 +00:00
|
|
|
*
|
2018-10-12 22:18:03 +00:00
|
|
|
* @return string HTML
|
2004-12-18 06:29:23 +00:00
|
|
|
*/
|
2015-09-26 16:31:24 +00:00
|
|
|
public static function formatComment(
|
|
|
|
|
$comment, $title = null, $local = false, $wikiId = null
|
|
|
|
|
) {
|
Introduce CommentFormatter
CommentParser:
* Move comment formatting backend from Linker to a CommentParser service.
Allow link existence and file existence to be batched.
* Rename $local to $samePage since I think that is clearer.
* Rename $title to $selfLinkTarget since it was unclear what the title
was used for.
* Rename the "autocomment" concept to "section link" in public
interfaces, although the old term remains in CSS classes.
* Keep unsafe HTML pass-through in separate "unsafe" methods, for easier
static analysis and code review.
CommentFormatter:
* Add CommentFormatter and RowCommentFormatter services as a usable
frontend for comment batches, and to replace the Linker static methods.
* Provide fluent and parametric interfaces.
Linker:
* Remove Linker::makeCommentLink() without deprecation -- nothing calls
it and it is obviously an internal helper.
* Soft-deprecate Linker methods formatComment(), formatLinksInComment(),
commentBlock() and revComment().
Caller migration:
* CommentFormatter single: Linker, RollbackAction, ApiComparePages,
ApiParse
* CommentFormatter parametric batch: ImageHistoryPseudoPager
* CommentFormatter fluent batch: ApiQueryFilearchive
* RowCommentFormatter sequential: History feed, BlocklistPager,
ProtectedPagesPager, ApiQueryProtectedTitles
* RowCommentFormatter with index: ChangesFeed, ChangesList,
ApiQueryDeletedrevs, ApiQueryLogEvents, ApiQueryRecentChanges
* RevisionCommentBatch: HistoryPager, ContribsPager
Bug: T285917
Change-Id: Ia3fd50a4a13138ba5003d884962da24746d562d0
2021-07-01 06:55:03 +00:00
|
|
|
$formatter = MediaWikiServices::getInstance()->getCommentFormatter();
|
|
|
|
|
return $formatter->format( $comment, $title, $local, $wikiId );
|
2007-05-09 20:05:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2007-06-07 22:16:19 +00:00
|
|
|
* Formats wiki links and media links in text; all other wiki formatting
|
|
|
|
|
* is ignored
|
|
|
|
|
*
|
2016-05-04 12:48:58 +00:00
|
|
|
* @since 1.16.3. $wikiId added in 1.26
|
Introduce CommentFormatter
CommentParser:
* Move comment formatting backend from Linker to a CommentParser service.
Allow link existence and file existence to be batched.
* Rename $local to $samePage since I think that is clearer.
* Rename $title to $selfLinkTarget since it was unclear what the title
was used for.
* Rename the "autocomment" concept to "section link" in public
interfaces, although the old term remains in CSS classes.
* Keep unsafe HTML pass-through in separate "unsafe" methods, for easier
static analysis and code review.
CommentFormatter:
* Add CommentFormatter and RowCommentFormatter services as a usable
frontend for comment batches, and to replace the Linker static methods.
* Provide fluent and parametric interfaces.
Linker:
* Remove Linker::makeCommentLink() without deprecation -- nothing calls
it and it is obviously an internal helper.
* Soft-deprecate Linker methods formatComment(), formatLinksInComment(),
commentBlock() and revComment().
Caller migration:
* CommentFormatter single: Linker, RollbackAction, ApiComparePages,
ApiParse
* CommentFormatter parametric batch: ImageHistoryPseudoPager
* CommentFormatter fluent batch: ApiQueryFilearchive
* RowCommentFormatter sequential: History feed, BlocklistPager,
ProtectedPagesPager, ApiQueryProtectedTitles
* RowCommentFormatter with index: ChangesFeed, ChangesList,
ApiQueryDeletedrevs, ApiQueryLogEvents, ApiQueryRecentChanges
* RevisionCommentBatch: HistoryPager, ContribsPager
Bug: T285917
Change-Id: Ia3fd50a4a13138ba5003d884962da24746d562d0
2021-07-01 06:55:03 +00:00
|
|
|
* @deprecated since 1.38 use CommentFormatter
|
2016-05-04 12:48:58 +00:00
|
|
|
*
|
2015-12-04 00:12:41 +00:00
|
|
|
* @param string $comment Text to format links in. WARNING! Since the output of this
|
2018-05-19 20:46:54 +00:00
|
|
|
* function is html, $comment must be sanitized for use as html. You probably want
|
|
|
|
|
* to pass $comment through Sanitizer::escapeHtmlAllowEntities() before calling
|
|
|
|
|
* this function.
|
2019-04-15 12:47:32 +00:00
|
|
|
* @param LinkTarget|null $title An optional LinkTarget object used to links to sections
|
2014-04-08 15:29:17 +00:00
|
|
|
* @param bool $local Whether section links should refer to local page
|
2015-09-26 16:31:24 +00:00
|
|
|
* @param string|null $wikiId Id of the wiki to link to (if not the local wiki),
|
|
|
|
|
* as used by WikiMap.
|
2015-01-02 18:33:04 +00:00
|
|
|
*
|
2018-08-30 05:08:32 +00:00
|
|
|
* @return string HTML
|
2018-08-31 09:47:24 +00:00
|
|
|
* @return-taint onlysafefor_html
|
2007-05-09 20:05:08 +00:00
|
|
|
*/
|
2015-03-03 18:28:08 +00:00
|
|
|
public static function formatLinksInComment(
|
|
|
|
|
$comment, $title = null, $local = false, $wikiId = null
|
|
|
|
|
) {
|
Introduce CommentFormatter
CommentParser:
* Move comment formatting backend from Linker to a CommentParser service.
Allow link existence and file existence to be batched.
* Rename $local to $samePage since I think that is clearer.
* Rename $title to $selfLinkTarget since it was unclear what the title
was used for.
* Rename the "autocomment" concept to "section link" in public
interfaces, although the old term remains in CSS classes.
* Keep unsafe HTML pass-through in separate "unsafe" methods, for easier
static analysis and code review.
CommentFormatter:
* Add CommentFormatter and RowCommentFormatter services as a usable
frontend for comment batches, and to replace the Linker static methods.
* Provide fluent and parametric interfaces.
Linker:
* Remove Linker::makeCommentLink() without deprecation -- nothing calls
it and it is obviously an internal helper.
* Soft-deprecate Linker methods formatComment(), formatLinksInComment(),
commentBlock() and revComment().
Caller migration:
* CommentFormatter single: Linker, RollbackAction, ApiComparePages,
ApiParse
* CommentFormatter parametric batch: ImageHistoryPseudoPager
* CommentFormatter fluent batch: ApiQueryFilearchive
* RowCommentFormatter sequential: History feed, BlocklistPager,
ProtectedPagesPager, ApiQueryProtectedTitles
* RowCommentFormatter with index: ChangesFeed, ChangesList,
ApiQueryDeletedrevs, ApiQueryLogEvents, ApiQueryRecentChanges
* RevisionCommentBatch: HistoryPager, ContribsPager
Bug: T285917
Change-Id: Ia3fd50a4a13138ba5003d884962da24746d562d0
2021-07-01 06:55:03 +00:00
|
|
|
$formatter = MediaWikiServices::getInstance()->getCommentFormatter();
|
|
|
|
|
return $formatter->formatLinksUnsafe( $comment, $title, $local, $wikiId );
|
2015-09-07 17:02:24 +00:00
|
|
|
}
|
|
|
|
|
|
2011-03-02 10:57:55 +00:00
|
|
|
/**
|
2021-10-21 18:09:51 +00:00
|
|
|
* @param LinkTarget|null $contextTitle
|
2014-04-22 11:07:02 +00:00
|
|
|
* @param string $target
|
2017-08-11 00:23:16 +00:00
|
|
|
* @param string &$text
|
2011-03-02 10:57:55 +00:00
|
|
|
* @return string
|
|
|
|
|
*/
|
2011-09-12 09:25:00 +00:00
|
|
|
public static function normalizeSubpageLink( $contextTitle, $target, &$text ) {
|
2009-07-20 02:07:56 +00:00
|
|
|
# Valid link forms:
|
|
|
|
|
# Foobar -- normal
|
|
|
|
|
# :Foobar -- override special treatment of prefix (images, language links)
|
|
|
|
|
# /Foobar -- convert to CurrentPage/Foobar
|
2014-11-14 21:38:04 +00:00
|
|
|
# /Foobar/ -- convert to CurrentPage/Foobar, strip the initial and final / from text
|
2009-07-20 02:07:56 +00:00
|
|
|
# ../ -- convert to CurrentPage, from CurrentPage/CurrentSubPage
|
2014-11-14 21:38:04 +00:00
|
|
|
# ../Foobar -- convert to CurrentPage/Foobar,
|
|
|
|
|
# (from CurrentPage/CurrentSubPage)
|
|
|
|
|
# ../Foobar/ -- convert to CurrentPage/Foobar, use 'Foobar' as text
|
|
|
|
|
# (from CurrentPage/CurrentSubPage)
|
2009-07-20 02:07:56 +00:00
|
|
|
|
|
|
|
|
$ret = $target; # default return value is no change
|
|
|
|
|
|
|
|
|
|
# Some namespaces don't allow subpages,
|
|
|
|
|
# so only perform processing if subpages are allowed
|
2018-08-05 17:58:51 +00:00
|
|
|
if (
|
|
|
|
|
$contextTitle && MediaWikiServices::getInstance()->getNamespaceInfo()->
|
|
|
|
|
hasSubpages( $contextTitle->getNamespace() )
|
|
|
|
|
) {
|
2009-07-20 02:07:56 +00:00
|
|
|
$hash = strpos( $target, '#' );
|
2010-08-26 19:40:29 +00:00
|
|
|
if ( $hash !== false ) {
|
2009-07-20 02:07:56 +00:00
|
|
|
$suffix = substr( $target, $hash );
|
|
|
|
|
$target = substr( $target, 0, $hash );
|
|
|
|
|
} else {
|
|
|
|
|
$suffix = '';
|
|
|
|
|
}
|
2017-02-20 22:44:19 +00:00
|
|
|
# T9425
|
2009-07-20 02:07:56 +00:00
|
|
|
$target = trim( $target );
|
2019-04-15 12:47:32 +00:00
|
|
|
$contextPrefixedText = MediaWikiServices::getInstance()->getTitleFormatter()->
|
|
|
|
|
getPrefixedText( $contextTitle );
|
2009-07-20 02:07:56 +00:00
|
|
|
# Look at the first character
|
2011-09-22 14:57:08 +00:00
|
|
|
if ( $target != '' && $target[0] === '/' ) {
|
2009-07-20 02:07:56 +00:00
|
|
|
# / at end means we don't want the slash to be shown
|
2016-02-17 09:09:32 +00:00
|
|
|
$m = [];
|
2009-07-20 02:07:56 +00:00
|
|
|
$trailingSlashes = preg_match_all( '%(/+)$%', $target, $m );
|
2010-08-26 19:40:29 +00:00
|
|
|
if ( $trailingSlashes ) {
|
|
|
|
|
$noslash = $target = substr( $target, 1, -strlen( $m[0][0] ) );
|
2009-07-20 02:07:56 +00:00
|
|
|
} else {
|
|
|
|
|
$noslash = substr( $target, 1 );
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-15 12:47:32 +00:00
|
|
|
$ret = $contextPrefixedText . '/' . trim( $noslash ) . $suffix;
|
2010-08-26 19:40:29 +00:00
|
|
|
if ( $text === '' ) {
|
2009-07-20 02:07:56 +00:00
|
|
|
$text = $target . $suffix;
|
|
|
|
|
} # this might be changed for ugliness reasons
|
|
|
|
|
} else {
|
|
|
|
|
# check for .. subpage backlinks
|
|
|
|
|
$dotdotcount = 0;
|
|
|
|
|
$nodotdot = $target;
|
2022-08-23 08:38:49 +00:00
|
|
|
while ( str_starts_with( $nodotdot, '../' ) ) {
|
2009-07-20 02:07:56 +00:00
|
|
|
++$dotdotcount;
|
|
|
|
|
$nodotdot = substr( $nodotdot, 3 );
|
|
|
|
|
}
|
2010-08-26 19:40:29 +00:00
|
|
|
if ( $dotdotcount > 0 ) {
|
2019-04-15 12:47:32 +00:00
|
|
|
$exploded = explode( '/', $contextPrefixedText );
|
2010-08-26 19:40:29 +00:00
|
|
|
if ( count( $exploded ) > $dotdotcount ) { # not allowed to go below top level page
|
2009-07-20 02:07:56 +00:00
|
|
|
$ret = implode( '/', array_slice( $exploded, 0, -$dotdotcount ) );
|
|
|
|
|
# / at the end means don't show full path
|
2010-08-26 19:40:29 +00:00
|
|
|
if ( substr( $nodotdot, -1, 1 ) === '/' ) {
|
2014-11-14 21:38:04 +00:00
|
|
|
$nodotdot = rtrim( $nodotdot, '/' );
|
2010-08-26 19:40:29 +00:00
|
|
|
if ( $text === '' ) {
|
2009-07-20 02:07:56 +00:00
|
|
|
$text = $nodotdot . $suffix;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
$nodotdot = trim( $nodotdot );
|
2010-08-26 19:40:29 +00:00
|
|
|
if ( $nodotdot != '' ) {
|
2009-07-20 02:07:56 +00:00
|
|
|
$ret .= '/' . $nodotdot;
|
|
|
|
|
}
|
|
|
|
|
$ret .= $suffix;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $ret;
|
|
|
|
|
}
|
2006-01-07 13:31:29 +00:00
|
|
|
|
2005-02-21 12:23:52 +00:00
|
|
|
/**
|
|
|
|
|
* Wrap a comment in standard punctuation and formatting if
|
|
|
|
|
* it's non-empty, otherwise return empty string.
|
2006-11-08 07:12:03 +00:00
|
|
|
*
|
2021-08-15 13:57:22 +00:00
|
|
|
* This method produces HTML that requires CSS styles in mediawiki.interface.helpers.styles.
|
|
|
|
|
*
|
2016-05-04 12:48:58 +00:00
|
|
|
* @since 1.16.3. $wikiId added in 1.26
|
Introduce CommentFormatter
CommentParser:
* Move comment formatting backend from Linker to a CommentParser service.
Allow link existence and file existence to be batched.
* Rename $local to $samePage since I think that is clearer.
* Rename $title to $selfLinkTarget since it was unclear what the title
was used for.
* Rename the "autocomment" concept to "section link" in public
interfaces, although the old term remains in CSS classes.
* Keep unsafe HTML pass-through in separate "unsafe" methods, for easier
static analysis and code review.
CommentFormatter:
* Add CommentFormatter and RowCommentFormatter services as a usable
frontend for comment batches, and to replace the Linker static methods.
* Provide fluent and parametric interfaces.
Linker:
* Remove Linker::makeCommentLink() without deprecation -- nothing calls
it and it is obviously an internal helper.
* Soft-deprecate Linker methods formatComment(), formatLinksInComment(),
commentBlock() and revComment().
Caller migration:
* CommentFormatter single: Linker, RollbackAction, ApiComparePages,
ApiParse
* CommentFormatter parametric batch: ImageHistoryPseudoPager
* CommentFormatter fluent batch: ApiQueryFilearchive
* RowCommentFormatter sequential: History feed, BlocklistPager,
ProtectedPagesPager, ApiQueryProtectedTitles
* RowCommentFormatter with index: ChangesFeed, ChangesList,
ApiQueryDeletedrevs, ApiQueryLogEvents, ApiQueryRecentChanges
* RevisionCommentBatch: HistoryPager, ContribsPager
Bug: T285917
Change-Id: Ia3fd50a4a13138ba5003d884962da24746d562d0
2021-07-01 06:55:03 +00:00
|
|
|
* @deprecated since 1.38 use CommentFormatter
|
|
|
|
|
*
|
2014-04-08 15:29:17 +00:00
|
|
|
* @param string $comment
|
2019-04-15 12:47:32 +00:00
|
|
|
* @param LinkTarget|null $title LinkTarget object (to generate link to section in autocomment)
|
|
|
|
|
* or null
|
2014-04-08 15:29:17 +00:00
|
|
|
* @param bool $local Whether section links should refer to local page
|
2015-09-26 16:31:24 +00:00
|
|
|
* @param string|null $wikiId Id (as used by WikiMap) of the wiki to generate links to.
|
|
|
|
|
* For use with external changes.
|
2019-11-08 19:58:41 +00:00
|
|
|
* @param bool $useParentheses Whether the comment is wrapped in parentheses
|
2011-06-14 23:50:40 +00:00
|
|
|
*
|
2006-11-08 07:12:03 +00:00
|
|
|
* @return string
|
2005-02-21 12:23:52 +00:00
|
|
|
*/
|
2015-09-26 16:31:24 +00:00
|
|
|
public static function commentBlock(
|
2018-09-21 18:11:23 +00:00
|
|
|
$comment, $title = null, $local = false, $wikiId = null, $useParentheses = true
|
2015-09-26 16:31:24 +00:00
|
|
|
) {
|
Introduce CommentFormatter
CommentParser:
* Move comment formatting backend from Linker to a CommentParser service.
Allow link existence and file existence to be batched.
* Rename $local to $samePage since I think that is clearer.
* Rename $title to $selfLinkTarget since it was unclear what the title
was used for.
* Rename the "autocomment" concept to "section link" in public
interfaces, although the old term remains in CSS classes.
* Keep unsafe HTML pass-through in separate "unsafe" methods, for easier
static analysis and code review.
CommentFormatter:
* Add CommentFormatter and RowCommentFormatter services as a usable
frontend for comment batches, and to replace the Linker static methods.
* Provide fluent and parametric interfaces.
Linker:
* Remove Linker::makeCommentLink() without deprecation -- nothing calls
it and it is obviously an internal helper.
* Soft-deprecate Linker methods formatComment(), formatLinksInComment(),
commentBlock() and revComment().
Caller migration:
* CommentFormatter single: Linker, RollbackAction, ApiComparePages,
ApiParse
* CommentFormatter parametric batch: ImageHistoryPseudoPager
* CommentFormatter fluent batch: ApiQueryFilearchive
* RowCommentFormatter sequential: History feed, BlocklistPager,
ProtectedPagesPager, ApiQueryProtectedTitles
* RowCommentFormatter with index: ChangesFeed, ChangesList,
ApiQueryDeletedrevs, ApiQueryLogEvents, ApiQueryRecentChanges
* RevisionCommentBatch: HistoryPager, ContribsPager
Bug: T285917
Change-Id: Ia3fd50a4a13138ba5003d884962da24746d562d0
2021-07-01 06:55:03 +00:00
|
|
|
return MediaWikiServices::getInstance()->getCommentFormatter()
|
|
|
|
|
->formatBlock( $comment, $title, $local, $wikiId, $useParentheses );
|
2006-03-16 19:04:25 +00:00
|
|
|
}
|
2007-05-09 20:05:08 +00:00
|
|
|
|
2006-03-16 19:04:25 +00:00
|
|
|
/**
|
|
|
|
|
* Wrap and format the given revision's comment block, if the current
|
|
|
|
|
* user is allowed to view it.
|
2006-11-17 03:59:32 +00:00
|
|
|
*
|
2021-04-19 16:55:41 +00:00
|
|
|
* This method produces HTML that requires CSS styles in mediawiki.interface.helpers.styles.
|
|
|
|
|
*
|
2016-05-04 12:48:58 +00:00
|
|
|
* @since 1.16.3
|
Introduce CommentFormatter
CommentParser:
* Move comment formatting backend from Linker to a CommentParser service.
Allow link existence and file existence to be batched.
* Rename $local to $samePage since I think that is clearer.
* Rename $title to $selfLinkTarget since it was unclear what the title
was used for.
* Rename the "autocomment" concept to "section link" in public
interfaces, although the old term remains in CSS classes.
* Keep unsafe HTML pass-through in separate "unsafe" methods, for easier
static analysis and code review.
CommentFormatter:
* Add CommentFormatter and RowCommentFormatter services as a usable
frontend for comment batches, and to replace the Linker static methods.
* Provide fluent and parametric interfaces.
Linker:
* Remove Linker::makeCommentLink() without deprecation -- nothing calls
it and it is obviously an internal helper.
* Soft-deprecate Linker methods formatComment(), formatLinksInComment(),
commentBlock() and revComment().
Caller migration:
* CommentFormatter single: Linker, RollbackAction, ApiComparePages,
ApiParse
* CommentFormatter parametric batch: ImageHistoryPseudoPager
* CommentFormatter fluent batch: ApiQueryFilearchive
* RowCommentFormatter sequential: History feed, BlocklistPager,
ProtectedPagesPager, ApiQueryProtectedTitles
* RowCommentFormatter with index: ChangesFeed, ChangesList,
ApiQueryDeletedrevs, ApiQueryLogEvents, ApiQueryRecentChanges
* RevisionCommentBatch: HistoryPager, ContribsPager
Bug: T285917
Change-Id: Ia3fd50a4a13138ba5003d884962da24746d562d0
2021-07-01 06:55:03 +00:00
|
|
|
* @deprecated since 1.38 use CommentFormatter
|
2021-04-21 15:43:13 +00:00
|
|
|
* @param RevisionRecord $revRecord (Switched from the old Revision class to RevisionRecord
|
|
|
|
|
* since 1.35)
|
2014-04-08 15:29:17 +00:00
|
|
|
* @param bool $local Whether section links should refer to local page
|
|
|
|
|
* @param bool $isPublic Show only if all users can see it
|
2018-09-21 18:11:23 +00:00
|
|
|
* @param bool $useParentheses (optional) Wrap comments in parentheses where needed
|
2014-04-08 15:29:17 +00:00
|
|
|
* @return string HTML fragment
|
2006-03-16 19:04:25 +00:00
|
|
|
*/
|
2021-04-21 15:43:13 +00:00
|
|
|
public static function revComment(
|
|
|
|
|
RevisionRecord $revRecord,
|
|
|
|
|
$local = false,
|
|
|
|
|
$isPublic = false,
|
2018-09-21 18:11:23 +00:00
|
|
|
$useParentheses = true
|
|
|
|
|
) {
|
2021-02-24 15:46:13 +00:00
|
|
|
$authority = RequestContext::getMain()->getAuthority();
|
Introduce CommentFormatter
CommentParser:
* Move comment formatting backend from Linker to a CommentParser service.
Allow link existence and file existence to be batched.
* Rename $local to $samePage since I think that is clearer.
* Rename $title to $selfLinkTarget since it was unclear what the title
was used for.
* Rename the "autocomment" concept to "section link" in public
interfaces, although the old term remains in CSS classes.
* Keep unsafe HTML pass-through in separate "unsafe" methods, for easier
static analysis and code review.
CommentFormatter:
* Add CommentFormatter and RowCommentFormatter services as a usable
frontend for comment batches, and to replace the Linker static methods.
* Provide fluent and parametric interfaces.
Linker:
* Remove Linker::makeCommentLink() without deprecation -- nothing calls
it and it is obviously an internal helper.
* Soft-deprecate Linker methods formatComment(), formatLinksInComment(),
commentBlock() and revComment().
Caller migration:
* CommentFormatter single: Linker, RollbackAction, ApiComparePages,
ApiParse
* CommentFormatter parametric batch: ImageHistoryPseudoPager
* CommentFormatter fluent batch: ApiQueryFilearchive
* RowCommentFormatter sequential: History feed, BlocklistPager,
ProtectedPagesPager, ApiQueryProtectedTitles
* RowCommentFormatter with index: ChangesFeed, ChangesList,
ApiQueryDeletedrevs, ApiQueryLogEvents, ApiQueryRecentChanges
* RevisionCommentBatch: HistoryPager, ContribsPager
Bug: T285917
Change-Id: Ia3fd50a4a13138ba5003d884962da24746d562d0
2021-07-01 06:55:03 +00:00
|
|
|
$formatter = MediaWikiServices::getInstance()->getCommentFormatter();
|
|
|
|
|
return $formatter->formatRevision( $revRecord, $authority, $local, $isPublic, $useParentheses );
|
2005-02-21 12:23:52 +00:00
|
|
|
}
|
2005-01-27 19:51:47 +00:00
|
|
|
|
2011-05-29 14:01:47 +00:00
|
|
|
/**
|
2016-05-04 12:48:58 +00:00
|
|
|
* @since 1.16.3
|
2014-04-22 11:07:02 +00:00
|
|
|
* @param int $size
|
2011-05-29 14:01:47 +00:00
|
|
|
* @return string
|
|
|
|
|
*/
|
2011-04-03 12:04:04 +00:00
|
|
|
public static function formatRevisionSize( $size ) {
|
2008-07-14 19:10:47 +00:00
|
|
|
if ( $size == 0 ) {
|
2012-08-19 20:44:29 +00:00
|
|
|
$stxt = wfMessage( 'historyempty' )->escaped();
|
2008-07-14 19:10:47 +00:00
|
|
|
} else {
|
2012-08-19 20:44:29 +00:00
|
|
|
$stxt = wfMessage( 'nbytes' )->numParams( $size )->escaped();
|
2008-07-14 19:10:47 +00:00
|
|
|
}
|
2020-12-22 12:23:04 +00:00
|
|
|
return "<span class=\"history-size mw-diff-bytes\" data-mw-bytes=\"$size\">$stxt</span>";
|
2008-07-14 19:10:47 +00:00
|
|
|
}
|
|
|
|
|
|
2009-10-26 14:25:48 +00:00
|
|
|
/**
|
|
|
|
|
* Add another level to the Table of Contents
|
2011-05-29 14:01:47 +00:00
|
|
|
*
|
2016-05-04 12:48:58 +00:00
|
|
|
* @since 1.16.3
|
2011-05-29 14:01:47 +00:00
|
|
|
* @return string
|
2009-10-26 14:25:48 +00:00
|
|
|
*/
|
2011-09-12 09:25:00 +00:00
|
|
|
public static function tocIndent() {
|
2018-07-12 18:41:20 +00:00
|
|
|
return "\n<ul>\n";
|
2004-12-18 06:29:23 +00:00
|
|
|
}
|
|
|
|
|
|
2009-10-26 14:25:48 +00:00
|
|
|
/**
|
|
|
|
|
* Finish one or more sublevels on the Table of Contents
|
2011-05-29 14:01:47 +00:00
|
|
|
*
|
2016-05-04 12:48:58 +00:00
|
|
|
* @since 1.16.3
|
2014-04-22 11:07:02 +00:00
|
|
|
* @param int $level
|
2011-05-29 14:01:47 +00:00
|
|
|
* @return string
|
2009-10-26 14:25:48 +00:00
|
|
|
*/
|
2011-09-12 09:25:00 +00:00
|
|
|
public static function tocUnindent( $level ) {
|
2010-08-26 19:40:29 +00:00
|
|
|
return "</li>\n" . str_repeat( "</ul>\n</li>\n", $level > 0 ? $level : 0 );
|
2004-12-18 06:29:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* parameter level defines if we are on an indentation level
|
2011-05-29 14:01:47 +00:00
|
|
|
*
|
2016-05-04 12:48:58 +00:00
|
|
|
* @since 1.16.3
|
2022-09-22 21:38:15 +00:00
|
|
|
* @param string $linkAnchor
|
2014-04-22 11:07:02 +00:00
|
|
|
* @param string $tocline
|
|
|
|
|
* @param string $tocnumber
|
2021-10-21 18:09:51 +00:00
|
|
|
* @param int $level
|
2023-01-19 20:37:16 +00:00
|
|
|
* @param string|false $sectionIndex
|
2011-05-29 14:01:47 +00:00
|
|
|
* @return string
|
2004-12-18 06:29:23 +00:00
|
|
|
*/
|
2022-09-22 21:38:15 +00:00
|
|
|
public static function tocLine( $linkAnchor, $tocline, $tocnumber, $level, $sectionIndex = false ) {
|
2009-06-20 21:47:10 +00:00
|
|
|
$classes = "toclevel-$level";
|
2023-01-19 20:37:16 +00:00
|
|
|
|
|
|
|
|
// Parser.php used to suppress tocLine by setting $sectionindex to false.
|
|
|
|
|
// In those circumstances, we can now encounter '' or a "T-" prefixed index
|
|
|
|
|
// for when the section comes from templates.
|
|
|
|
|
if ( $sectionIndex !== false && $sectionIndex !== '' && !str_starts_with( $sectionIndex, "T-" ) ) {
|
2009-06-20 21:47:10 +00:00
|
|
|
$classes .= " tocsection-$sectionIndex";
|
2011-05-29 14:01:47 +00:00
|
|
|
}
|
2017-09-01 00:48:42 +00:00
|
|
|
|
2022-09-22 21:38:15 +00:00
|
|
|
// <li class="$classes"><a href="#$linkAnchor"><span class="tocnumber">
|
2017-09-01 00:48:42 +00:00
|
|
|
// $tocnumber</span> <span class="toctext">$tocline</span></a>
|
2018-07-12 18:41:20 +00:00
|
|
|
return Html::openElement( 'li', [ 'class' => $classes ] )
|
2017-09-01 00:48:42 +00:00
|
|
|
. Html::rawElement( 'a',
|
2022-09-22 21:38:15 +00:00
|
|
|
[ 'href' => "#$linkAnchor" ],
|
2017-09-01 00:48:42 +00:00
|
|
|
Html::element( 'span', [ 'class' => 'tocnumber' ], $tocnumber )
|
|
|
|
|
. ' '
|
|
|
|
|
. Html::rawElement( 'span', [ 'class' => 'toctext' ], $tocline )
|
|
|
|
|
);
|
2004-12-18 06:29:23 +00:00
|
|
|
}
|
|
|
|
|
|
2009-10-26 14:25:48 +00:00
|
|
|
/**
|
|
|
|
|
* End a Table Of Contents line.
|
2010-01-07 09:32:09 +00:00
|
|
|
* tocUnindent() will be used instead if we're ending a line below
|
2009-10-26 14:25:48 +00:00
|
|
|
* the new level.
|
2016-05-04 12:48:58 +00:00
|
|
|
* @since 1.16.3
|
2012-02-09 21:35:05 +00:00
|
|
|
* @return string
|
2009-10-26 14:25:48 +00:00
|
|
|
*/
|
2011-09-12 09:25:00 +00:00
|
|
|
public static function tocLineEnd() {
|
2005-01-15 23:21:52 +00:00
|
|
|
return "</li>\n";
|
2010-11-05 19:06:12 +00:00
|
|
|
}
|
2005-01-15 23:21:52 +00:00
|
|
|
|
2010-01-07 09:32:09 +00:00
|
|
|
/**
|
2016-06-29 11:02:12 +00:00
|
|
|
* Wraps the TOC in a div with ARIA navigation role and provides the hide/collapse JavaScript.
|
2010-05-20 20:36:12 +00:00
|
|
|
*
|
2016-05-04 12:48:58 +00:00
|
|
|
* @since 1.16.3
|
2014-04-08 15:29:17 +00:00
|
|
|
* @param string $toc Html of the Table Of Contents
|
2018-06-05 11:46:57 +00:00
|
|
|
* @param Language|null $lang Language for the toc title, defaults to user language
|
2014-04-08 15:29:17 +00:00
|
|
|
* @return string Full html of the TOC
|
2009-10-26 14:25:48 +00:00
|
|
|
*/
|
2018-06-05 11:46:57 +00:00
|
|
|
public static function tocList( $toc, Language $lang = null ) {
|
2022-10-21 02:26:49 +00:00
|
|
|
$lang ??= RequestContext::getMain()->getLanguage();
|
2018-07-16 08:44:54 +00:00
|
|
|
|
2012-08-22 19:29:18 +00:00
|
|
|
$title = wfMessage( 'toc' )->inLanguage( $lang )->escaped();
|
|
|
|
|
|
2016-06-29 11:02:12 +00:00
|
|
|
return '<div id="toc" class="toc" role="navigation" aria-labelledby="mw-toc-heading">'
|
2017-08-17 21:56:40 +00:00
|
|
|
. Html::element( 'input', [
|
|
|
|
|
'type' => 'checkbox',
|
2018-07-16 16:30:55 +00:00
|
|
|
'role' => 'button',
|
2017-08-17 21:56:40 +00:00
|
|
|
'id' => 'toctogglecheckbox',
|
|
|
|
|
'class' => 'toctogglecheckbox',
|
|
|
|
|
'style' => 'display:none',
|
|
|
|
|
] )
|
2015-05-29 17:07:55 +00:00
|
|
|
. Html::openElement( 'div', [
|
|
|
|
|
'class' => 'toctitle',
|
|
|
|
|
'lang' => $lang->getHtmlCode(),
|
|
|
|
|
'dir' => $lang->getDir(),
|
|
|
|
|
] )
|
2016-06-29 11:02:12 +00:00
|
|
|
. '<h2 id="mw-toc-heading">' . $title . '</h2>'
|
2017-08-17 21:56:40 +00:00
|
|
|
. '<span class="toctogglespan">'
|
|
|
|
|
. Html::label( '', 'toctogglecheckbox', [
|
|
|
|
|
'class' => 'toctogglelabel',
|
|
|
|
|
] )
|
|
|
|
|
. '</span>'
|
2022-08-23 08:38:49 +00:00
|
|
|
. '</div>'
|
2013-02-03 20:05:24 +00:00
|
|
|
. $toc
|
2012-12-21 19:26:29 +00:00
|
|
|
. "</ul>\n</div>\n";
|
2004-12-18 06:29:23 +00:00
|
|
|
}
|
2010-01-07 09:32:09 +00:00
|
|
|
|
2009-06-25 11:05:22 +00:00
|
|
|
/**
|
2022-12-01 00:31:10 +00:00
|
|
|
* @internal For use by ParserOutput and API modules
|
2015-08-03 05:33:43 +00:00
|
|
|
* Generate a table of contents from a section tree.
|
2010-05-20 20:36:12 +00:00
|
|
|
*
|
2023-01-26 22:05:10 +00:00
|
|
|
* @since 1.16.3. $lang added in 1.17. Parameters changed in 1.40.
|
|
|
|
|
* @param ?TOCData $tocData Return value of ParserOutput::getSections()
|
2018-06-05 11:46:57 +00:00
|
|
|
* @param Language|null $lang Language for the toc title, defaults to user language
|
2023-01-19 20:37:16 +00:00
|
|
|
* @param array $options FIXME: Document
|
2014-04-08 15:29:17 +00:00
|
|
|
* @return string HTML fragment
|
2009-06-25 11:05:22 +00:00
|
|
|
*/
|
2023-01-26 22:05:10 +00:00
|
|
|
public static function generateTOC( ?TOCData $tocData, Language $lang = null, array $options = [] ): string {
|
2009-06-25 11:05:22 +00:00
|
|
|
$toc = '';
|
|
|
|
|
$lastLevel = 0;
|
2023-01-19 20:37:16 +00:00
|
|
|
$maxTocLevel = $options['maxtoclevel'] ?? null;
|
2023-01-26 22:05:10 +00:00
|
|
|
foreach ( ( $tocData ? $tocData->getSections() : [] ) as $section ) {
|
|
|
|
|
$tocLevel = $section->tocLevel;
|
2023-01-19 20:37:16 +00:00
|
|
|
if ( $maxTocLevel !== null && $tocLevel < $maxTocLevel ) {
|
|
|
|
|
if ( $tocLevel > $lastLevel ) {
|
|
|
|
|
$toc .= self::tocIndent();
|
|
|
|
|
} elseif ( $tocLevel < $lastLevel ) {
|
|
|
|
|
if ( $lastLevel < $maxTocLevel ) {
|
|
|
|
|
$toc .= self::tocUnindent(
|
|
|
|
|
$lastLevel - $tocLevel );
|
|
|
|
|
} else {
|
|
|
|
|
$toc .= self::tocLineEnd();
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
$toc .= self::tocLineEnd();
|
|
|
|
|
}
|
2010-01-07 09:32:09 +00:00
|
|
|
|
2023-01-26 22:05:10 +00:00
|
|
|
$toc .= self::tocLine( $section->linkAnchor,
|
|
|
|
|
$section->line, $section->number,
|
|
|
|
|
$tocLevel, $section->index );
|
2023-01-19 20:37:16 +00:00
|
|
|
$lastLevel = $tocLevel;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if ( $lastLevel < $maxTocLevel && $lastLevel > 0 ) {
|
|
|
|
|
$toc .= self::tocUnindent( $lastLevel - 1 );
|
2009-06-25 11:05:22 +00:00
|
|
|
}
|
2015-05-06 17:37:41 +00:00
|
|
|
return self::tocList( $toc, $lang );
|
2004-12-18 06:29:23 +00:00
|
|
|
}
|
2005-04-27 07:48:14 +00:00
|
|
|
|
2007-01-08 02:11:45 +00:00
|
|
|
/**
|
|
|
|
|
* Create a headline for content
|
|
|
|
|
*
|
2016-05-04 12:48:58 +00:00
|
|
|
* @since 1.16.3
|
2014-04-08 15:29:17 +00:00
|
|
|
* @param int $level The level of the headline (1-6)
|
|
|
|
|
* @param string $attribs Any attributes for the headline, starting with
|
|
|
|
|
* a space and ending with '>'
|
|
|
|
|
* This *must* be at least '>' for no attribs
|
2014-04-22 11:07:02 +00:00
|
|
|
* @param string $anchor The anchor to give the headline (the bit after the #)
|
2017-06-30 00:13:12 +00:00
|
|
|
* @param string $html HTML for the text of the header
|
2013-03-11 17:15:01 +00:00
|
|
|
* @param string $link HTML to add for the section edit link
|
2021-10-21 18:09:51 +00:00
|
|
|
* @param string|false $fallbackAnchor A second, optional anchor to give for
|
2009-01-05 15:59:46 +00:00
|
|
|
* backward compatibility (false to omit)
|
2007-01-08 02:11:45 +00:00
|
|
|
*
|
2014-04-08 15:29:17 +00:00
|
|
|
* @return string HTML headline
|
2007-01-08 02:11:45 +00:00
|
|
|
*/
|
2014-05-15 15:38:28 +00:00
|
|
|
public static function makeHeadline( $level, $attribs, $anchor, $html,
|
2017-06-30 00:13:12 +00:00
|
|
|
$link, $fallbackAnchor = false
|
2014-05-15 15:38:28 +00:00
|
|
|
) {
|
2022-01-25 05:23:23 +00:00
|
|
|
$anchorEscaped = htmlspecialchars( $anchor, ENT_COMPAT );
|
2017-08-03 21:56:59 +00:00
|
|
|
$fallback = '';
|
2017-06-30 00:13:12 +00:00
|
|
|
if ( $fallbackAnchor !== false && $fallbackAnchor !== $anchor ) {
|
2022-01-25 05:23:23 +00:00
|
|
|
$fallbackAnchor = htmlspecialchars( $fallbackAnchor, ENT_COMPAT );
|
2017-08-03 21:56:59 +00:00
|
|
|
$fallback = "<span id=\"$fallbackAnchor\"></span>";
|
2009-01-05 15:59:46 +00:00
|
|
|
}
|
2019-02-09 07:25:57 +00:00
|
|
|
return "<h$level$attribs"
|
2017-08-03 21:56:59 +00:00
|
|
|
. "$fallback<span class=\"mw-headline\" id=\"$anchorEscaped\">$html</span>"
|
|
|
|
|
. $link
|
|
|
|
|
. "</h$level>";
|
2007-01-08 02:11:45 +00:00
|
|
|
}
|
|
|
|
|
|
2006-01-07 13:09:30 +00:00
|
|
|
/**
|
2005-04-27 07:48:14 +00:00
|
|
|
* Split a link trail, return the "inside" portion and the remainder of the trail
|
|
|
|
|
* as a two-element array
|
2014-04-22 11:07:02 +00:00
|
|
|
* @param string $trail
|
2020-03-24 08:59:02 +00:00
|
|
|
* @return string[]
|
2005-04-27 07:48:14 +00:00
|
|
|
*/
|
2019-11-30 22:32:44 +00:00
|
|
|
public static function splitTrail( $trail ) {
|
2018-07-29 12:24:54 +00:00
|
|
|
$regex = MediaWikiServices::getInstance()->getContentLanguage()->linkTrail();
|
2005-04-27 07:48:14 +00:00
|
|
|
$inside = '';
|
2019-03-24 14:04:32 +00:00
|
|
|
if ( $trail !== '' && preg_match( $regex, $trail, $m ) ) {
|
2022-04-01 15:58:32 +00:00
|
|
|
[ , $inside, $trail ] = $m;
|
2005-04-27 07:48:14 +00:00
|
|
|
}
|
2016-02-17 09:09:32 +00:00
|
|
|
return [ $inside, $trail ];
|
2005-04-27 07:48:14 +00:00
|
|
|
}
|
2006-11-08 07:12:03 +00:00
|
|
|
|
2006-11-16 22:53:01 +00:00
|
|
|
/**
|
|
|
|
|
* Generate a rollback link for a given revision. Currently it's the
|
|
|
|
|
* caller's responsibility to ensure that the revision is the top one. If
|
|
|
|
|
* it's not, of course, the user will get an error message.
|
|
|
|
|
*
|
|
|
|
|
* If the calling page is called with the parameter &bot=1, all rollback
|
|
|
|
|
* links also get that parameter. It causes the edit itself and the rollback
|
|
|
|
|
* to be marked as "bot" edits. Bot edits are hidden by default from recent
|
|
|
|
|
* changes, so this allows sysops to combat a busy vandal without bothering
|
|
|
|
|
* other users.
|
|
|
|
|
*
|
2022-12-23 03:38:02 +00:00
|
|
|
* This function will return the link only in case the revision can be reverted
|
|
|
|
|
* (not all revisions are by the same user, and the last revision by a different
|
|
|
|
|
* user is visible). Please note that due to performance limitations it might be
|
|
|
|
|
* assumed that a user isn't the only contributor of a page while (s)he is, which
|
|
|
|
|
* will lead to useless rollback links. Furthermore this won't work if
|
|
|
|
|
* $wgShowRollbackEditCount is disabled, so this can only function as an
|
|
|
|
|
* additional check.
|
2012-11-01 20:04:12 +00:00
|
|
|
*
|
2015-09-29 02:53:20 +00:00
|
|
|
* If the option noBrackets is set the rollback link wont be enclosed in "[]".
|
|
|
|
|
*
|
2016-05-04 12:48:58 +00:00
|
|
|
* @since 1.16.3. $context added in 1.20. $options added in 1.21
|
2020-04-06 22:25:41 +00:00
|
|
|
* $rev could be a RevisionRecord since 1.35
|
2016-05-04 12:48:58 +00:00
|
|
|
*
|
2021-04-21 15:43:13 +00:00
|
|
|
* @param RevisionRecord $revRecord (Switched from the old Revision class to RevisionRecord
|
|
|
|
|
* since 1.35)
|
2018-06-26 21:14:43 +00:00
|
|
|
* @param IContextSource|null $context Context to use or null for the main context.
|
2014-04-22 11:07:02 +00:00
|
|
|
* @param array $options
|
2012-02-09 21:35:05 +00:00
|
|
|
* @return string
|
2006-11-16 22:53:01 +00:00
|
|
|
*/
|
2021-04-21 15:43:13 +00:00
|
|
|
public static function generateRollback(
|
|
|
|
|
RevisionRecord $revRecord,
|
|
|
|
|
IContextSource $context = null,
|
2022-12-23 03:38:02 +00:00
|
|
|
$options = []
|
2014-05-15 15:38:28 +00:00
|
|
|
) {
|
2022-12-16 23:48:27 +00:00
|
|
|
$context ??= RequestContext::getMain();
|
2014-05-15 15:38:28 +00:00
|
|
|
|
2022-12-23 03:38:02 +00:00
|
|
|
$editCount = self::getRollbackEditCount( $revRecord );
|
|
|
|
|
if ( $editCount === false ) {
|
|
|
|
|
return '';
|
2012-11-01 20:04:12 +00:00
|
|
|
}
|
|
|
|
|
|
2020-04-06 22:25:41 +00:00
|
|
|
$inner = self::buildRollbackLink( $revRecord, $context, $editCount );
|
2012-11-01 20:04:12 +00:00
|
|
|
|
2020-05-12 10:05:37 +00:00
|
|
|
// Allow extensions to modify the rollback link.
|
|
|
|
|
// Abort further execution if the extension wants full control over the link.
|
|
|
|
|
if ( !Hooks::runner()->onLinkerGenerateRollbackLink(
|
|
|
|
|
$revRecord, $context, $options, $inner ) ) {
|
|
|
|
|
return $inner;
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-03 18:30:54 +00:00
|
|
|
if ( !in_array( 'noBrackets', $options, true ) ) {
|
2014-12-06 10:16:15 +00:00
|
|
|
$inner = $context->msg( 'brackets' )->rawParams( $inner )->escaped();
|
2012-11-01 20:04:12 +00:00
|
|
|
}
|
|
|
|
|
|
2021-03-16 21:43:21 +00:00
|
|
|
if ( MediaWikiServices::getInstance()->getUserOptionsLookup()
|
|
|
|
|
->getBoolOption( $context->getUser(), 'showrollbackconfirmation' )
|
|
|
|
|
) {
|
2019-02-28 16:58:57 +00:00
|
|
|
$stats = MediaWikiServices::getInstance()->getStatsdDataFactory();
|
|
|
|
|
$stats->increment( 'rollbackconfirmation.event.load' );
|
2020-05-23 01:30:33 +00:00
|
|
|
$context->getOutput()->addModules( 'mediawiki.misc-authed-curate' );
|
2019-02-05 13:31:53 +00:00
|
|
|
}
|
|
|
|
|
|
2012-11-01 20:04:12 +00:00
|
|
|
return '<span class="mw-rollback-link">' . $inner . '</span>';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* This function will return the number of revisions which a rollback
|
2022-12-23 03:38:02 +00:00
|
|
|
* would revert and will verify that a revision can be reverted (that
|
|
|
|
|
* the user isn't the only contributor and the revision we might
|
|
|
|
|
* rollback to isn't deleted). These checks can only function as an
|
|
|
|
|
* additional check as this function only checks against the last
|
|
|
|
|
* $wgShowRollbackEditCount edits.
|
2012-11-01 20:04:12 +00:00
|
|
|
*
|
2022-12-23 03:38:02 +00:00
|
|
|
* Returns null if $wgShowRollbackEditCount is disabled or false if
|
|
|
|
|
* the user is the only contributor of the page.
|
2012-11-01 20:04:12 +00:00
|
|
|
*
|
2020-04-18 00:21:26 +00:00
|
|
|
* @todo Unused outside of this file - should it be made private?
|
|
|
|
|
*
|
2021-04-21 15:43:13 +00:00
|
|
|
* @param RevisionRecord $revRecord (Switched from the old Revision class to RevisionRecord
|
|
|
|
|
* since 1.35)
|
2022-12-23 03:38:02 +00:00
|
|
|
* @param bool $verify Deprecated since 1.40, has no effect.
|
2021-10-21 18:09:51 +00:00
|
|
|
* @return int|false|null
|
2012-11-01 20:04:12 +00:00
|
|
|
*/
|
2022-12-23 03:38:02 +00:00
|
|
|
public static function getRollbackEditCount( RevisionRecord $revRecord, $verify = true ) {
|
|
|
|
|
if ( func_num_args() > 1 ) {
|
|
|
|
|
wfDeprecated( __METHOD__ . ' with $verify parameter', '1.40' );
|
|
|
|
|
}
|
2022-04-01 15:58:32 +00:00
|
|
|
$showRollbackEditCount = MediaWikiServices::getInstance()->getMainConfig()
|
|
|
|
|
->get( MainConfigNames::ShowRollbackEditCount );
|
2020-04-06 22:25:41 +00:00
|
|
|
|
2022-01-06 18:44:56 +00:00
|
|
|
if ( !is_int( $showRollbackEditCount ) || !$showRollbackEditCount > 0 ) {
|
2012-11-01 20:04:12 +00:00
|
|
|
// Nothing has happened, indicate this by returning 'null'
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-05 19:55:19 +00:00
|
|
|
$dbr = wfGetDB( DB_REPLICA );
|
2012-11-01 20:04:12 +00:00
|
|
|
|
|
|
|
|
// Up to the value of $wgShowRollbackEditCount revisions are counted
|
2020-03-26 22:40:22 +00:00
|
|
|
$revQuery = MediaWikiServices::getInstance()->getRevisionStore()->getQueryInfo();
|
2022-07-28 21:10:02 +00:00
|
|
|
$res = $dbr->newSelectQueryBuilder()
|
|
|
|
|
->select( [ 'rev_user_text' => $revQuery['fields']['rev_user_text'], 'rev_deleted' ] )
|
|
|
|
|
->tables( $revQuery['tables'] )
|
|
|
|
|
->where( [ 'rev_page' => $revRecord->getPageId() ] )
|
|
|
|
|
->joinConds( $revQuery['joins'] )
|
|
|
|
|
->useIndex( [ 'revision' => 'rev_page_timestamp' ] )
|
|
|
|
|
->orderBy( [ 'rev_timestamp', 'rev_id' ], SelectQueryBuilder::SORT_DESC )
|
|
|
|
|
->limit( $showRollbackEditCount + 1 )
|
|
|
|
|
->caller( __METHOD__ )
|
|
|
|
|
->fetchResultSet();
|
2012-11-01 20:04:12 +00:00
|
|
|
|
2020-04-06 22:25:41 +00:00
|
|
|
$revUser = $revRecord->getUser( RevisionRecord::RAW );
|
|
|
|
|
$revUserText = $revUser ? $revUser->getName() : '';
|
|
|
|
|
|
2012-11-01 20:04:12 +00:00
|
|
|
$editCount = 0;
|
|
|
|
|
$moreRevs = false;
|
|
|
|
|
foreach ( $res as $row ) {
|
2020-04-06 22:25:41 +00:00
|
|
|
if ( $row->rev_user_text != $revUserText ) {
|
2022-12-23 03:38:02 +00:00
|
|
|
if ( $row->rev_deleted & RevisionRecord::DELETED_TEXT
|
|
|
|
|
|| $row->rev_deleted & RevisionRecord::DELETED_USER
|
|
|
|
|
) {
|
2014-05-15 15:38:28 +00:00
|
|
|
// If the user or the text of the revision we might rollback
|
|
|
|
|
// to is deleted in some way we can't rollback. Similar to
|
2021-11-19 23:19:42 +00:00
|
|
|
// the checks in WikiPage::commitRollback.
|
2012-11-01 20:04:12 +00:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
$moreRevs = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
$editCount++;
|
|
|
|
|
}
|
2012-07-05 21:07:58 +00:00
|
|
|
|
2022-12-23 03:38:02 +00:00
|
|
|
if ( $editCount <= $showRollbackEditCount && !$moreRevs ) {
|
2012-11-01 20:04:12 +00:00
|
|
|
// We didn't find at least $wgShowRollbackEditCount revisions made by the current user
|
|
|
|
|
// and there weren't any other revisions. That means that the current user is the only
|
|
|
|
|
// editor, so we can't rollback
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return $editCount;
|
2007-07-07 00:17:51 +00:00
|
|
|
}
|
2008-04-14 07:45:50 +00:00
|
|
|
|
2007-07-07 00:17:51 +00:00
|
|
|
/**
|
|
|
|
|
* Build a raw rollback link, useful for collections of "tool" links
|
|
|
|
|
*
|
2016-05-04 12:48:58 +00:00
|
|
|
* @since 1.16.3. $context added in 1.20. $editCount added in 1.21
|
2020-04-06 22:25:41 +00:00
|
|
|
* $rev could be a RevisionRecord since 1.35
|
|
|
|
|
*
|
2020-04-18 00:21:26 +00:00
|
|
|
* @todo Unused outside of this file - should it be made private?
|
|
|
|
|
*
|
2021-04-21 15:43:13 +00:00
|
|
|
* @param RevisionRecord $revRecord (Switched from the old Revision class to RevisionRecord
|
|
|
|
|
* since 1.35)
|
2014-04-08 15:29:17 +00:00
|
|
|
* @param IContextSource|null $context Context to use or null for the main context.
|
2022-03-01 21:42:03 +00:00
|
|
|
* @param int|false|null $editCount Number of edits that would be reverted
|
2014-04-08 15:29:17 +00:00
|
|
|
* @return string HTML fragment
|
2007-07-07 00:17:51 +00:00
|
|
|
*/
|
2021-04-21 15:43:13 +00:00
|
|
|
public static function buildRollbackLink(
|
|
|
|
|
RevisionRecord $revRecord,
|
|
|
|
|
IContextSource $context = null,
|
2014-05-15 15:38:28 +00:00
|
|
|
$editCount = false
|
|
|
|
|
) {
|
2022-01-21 09:12:27 +00:00
|
|
|
$config = MediaWikiServices::getInstance()->getMainConfig();
|
2022-04-01 15:58:32 +00:00
|
|
|
$showRollbackEditCount = $config->get( MainConfigNames::ShowRollbackEditCount );
|
|
|
|
|
$miserMode = $config->get( MainConfigNames::MiserMode );
|
2014-12-12 08:41:27 +00:00
|
|
|
// To config which pages are affected by miser mode
|
2016-02-17 09:09:32 +00:00
|
|
|
$disableRollbackEditCountSpecialPage = [ 'Recentchanges', 'Watchlist' ];
|
2012-07-29 16:57:11 +00:00
|
|
|
|
2022-12-16 23:48:27 +00:00
|
|
|
$context ??= RequestContext::getMain();
|
2012-07-05 21:07:58 +00:00
|
|
|
|
2020-04-06 22:25:41 +00:00
|
|
|
$title = $revRecord->getPageAsLinkTarget();
|
|
|
|
|
$revUser = $revRecord->getUser();
|
|
|
|
|
$revUserText = $revUser ? $revUser->getName() : '';
|
2019-02-18 13:32:05 +00:00
|
|
|
|
2016-02-17 09:09:32 +00:00
|
|
|
$query = [
|
2008-08-01 00:54:08 +00:00
|
|
|
'action' => 'rollback',
|
2020-04-06 22:25:41 +00:00
|
|
|
'from' => $revUserText,
|
2021-08-05 06:54:11 +00:00
|
|
|
'token' => $context->getUser()->getEditToken( 'rollback' ),
|
2016-02-17 09:09:32 +00:00
|
|
|
];
|
2019-02-18 13:32:05 +00:00
|
|
|
|
2015-09-29 02:53:20 +00:00
|
|
|
$attrs = [
|
|
|
|
|
'data-mw' => 'interface',
|
2019-03-22 11:10:45 +00:00
|
|
|
'title' => $context->msg( 'tooltip-rollback' )->text()
|
2015-09-29 02:53:20 +00:00
|
|
|
];
|
2019-02-18 13:32:05 +00:00
|
|
|
|
2015-09-29 02:53:20 +00:00
|
|
|
$options = [ 'known', 'noclasses' ];
|
|
|
|
|
|
2012-07-05 21:07:58 +00:00
|
|
|
if ( $context->getRequest()->getBool( 'bot' ) ) {
|
2020-05-10 00:09:19 +00:00
|
|
|
// T17999
|
2019-02-18 13:32:05 +00:00
|
|
|
$query['hidediff'] = '1';
|
2008-07-30 21:02:28 +00:00
|
|
|
$query['bot'] = '1';
|
|
|
|
|
}
|
2012-07-29 16:57:11 +00:00
|
|
|
|
2022-01-06 18:44:56 +00:00
|
|
|
if ( $miserMode ) {
|
2013-04-20 22:49:30 +00:00
|
|
|
foreach ( $disableRollbackEditCountSpecialPage as $specialPage ) {
|
|
|
|
|
if ( $context->getTitle()->isSpecial( $specialPage ) ) {
|
2022-01-21 09:12:27 +00:00
|
|
|
$showRollbackEditCount = false;
|
2012-08-01 16:52:30 +00:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-21 09:12:27 +00:00
|
|
|
// The edit count can be 0 on replica lag, fall back to the generic rollbacklink message
|
|
|
|
|
$msg = [ 'rollbacklink' ];
|
|
|
|
|
if ( is_int( $showRollbackEditCount ) && $showRollbackEditCount > 0 ) {
|
2012-11-01 20:04:12 +00:00
|
|
|
if ( !is_numeric( $editCount ) ) {
|
2022-12-23 03:38:02 +00:00
|
|
|
$editCount = self::getRollbackEditCount( $revRecord );
|
2012-07-29 16:57:11 +00:00
|
|
|
}
|
|
|
|
|
|
2022-01-21 09:12:27 +00:00
|
|
|
if ( $editCount > $showRollbackEditCount ) {
|
|
|
|
|
$msg = [ 'rollbacklinkcount-morethan', Message::numParam( $showRollbackEditCount ) ];
|
|
|
|
|
} elseif ( $editCount ) {
|
|
|
|
|
$msg = [ 'rollbacklinkcount', Message::numParam( $editCount ) ];
|
2021-01-17 11:34:37 +00:00
|
|
|
}
|
2012-07-29 16:57:11 +00:00
|
|
|
}
|
2019-02-09 07:25:57 +00:00
|
|
|
|
2022-01-21 09:12:27 +00:00
|
|
|
$html = $context->msg( ...$msg )->parse();
|
2019-02-09 07:25:57 +00:00
|
|
|
return self::link( $title, $html, $attrs, $query, $options );
|
2006-11-16 22:53:01 +00:00
|
|
|
}
|
2006-11-17 03:42:23 +00:00
|
|
|
|
2008-02-25 13:38:21 +00:00
|
|
|
/**
|
|
|
|
|
* Returns HTML for the "hidden categories on this page" list.
|
|
|
|
|
*
|
2016-05-04 12:48:58 +00:00
|
|
|
* @since 1.16.3
|
2020-03-26 00:43:50 +00:00
|
|
|
* @param array $hiddencats Array of hidden categories
|
|
|
|
|
* from {@link WikiPage::getHiddenCategories} or similar
|
2014-04-22 11:07:02 +00:00
|
|
|
* @return string HTML output
|
2008-02-25 13:38:21 +00:00
|
|
|
*/
|
2011-04-03 12:04:04 +00:00
|
|
|
public static function formatHiddenCategories( $hiddencats ) {
|
2008-02-25 13:38:21 +00:00
|
|
|
$outText = '';
|
|
|
|
|
if ( count( $hiddencats ) > 0 ) {
|
|
|
|
|
# Construct the HTML
|
|
|
|
|
$outText = '<div class="mw-hiddenCategoriesExplanation">';
|
2012-08-19 20:44:29 +00:00
|
|
|
$outText .= wfMessage( 'hiddencategories' )->numParams( count( $hiddencats ) )->parseAsBlock();
|
2008-12-28 16:46:05 +00:00
|
|
|
$outText .= "</div><ul>\n";
|
2008-02-25 13:38:21 +00:00
|
|
|
|
|
|
|
|
foreach ( $hiddencats as $titleObj ) {
|
2014-05-15 15:38:28 +00:00
|
|
|
# If it's hidden, it must exist - no need to check with a LinkBatch
|
|
|
|
|
$outText .= '<li>'
|
2016-02-17 09:09:32 +00:00
|
|
|
. self::link( $titleObj, null, [], [], 'known' )
|
2014-05-15 15:38:28 +00:00
|
|
|
. "</li>\n";
|
2008-02-25 13:38:21 +00:00
|
|
|
}
|
|
|
|
|
$outText .= '</ul>';
|
|
|
|
|
}
|
|
|
|
|
return $outText;
|
|
|
|
|
}
|
2008-02-25 19:09:25 +00:00
|
|
|
|
2022-02-17 17:16:47 +00:00
|
|
|
/**
|
|
|
|
|
* @return ContextSource
|
|
|
|
|
*/
|
|
|
|
|
private static function getContextFromMain() {
|
|
|
|
|
$context = RequestContext::getMain();
|
|
|
|
|
$context = new DerivativeContext( $context );
|
|
|
|
|
return $context;
|
|
|
|
|
}
|
|
|
|
|
|
2007-01-10 06:03:14 +00:00
|
|
|
/**
|
2007-01-11 00:57:30 +00:00
|
|
|
* Given the id of an interface element, constructs the appropriate title
|
|
|
|
|
* attribute from the system messages. (Note, this is usually the id but
|
|
|
|
|
* isn't always, because sometimes the accesskey needs to go on a different
|
|
|
|
|
* element than the id, for reverse-compatibility, etc.)
|
2007-01-10 06:03:14 +00:00
|
|
|
*
|
2016-05-04 12:48:58 +00:00
|
|
|
* @since 1.16.3 $msgParams added in 1.27
|
2014-04-08 15:29:17 +00:00
|
|
|
* @param string $name Id of the element, minus prefixes.
|
2018-01-03 14:33:58 +00:00
|
|
|
* @param string|array|null $options Null, string or array with some of the following options:
|
|
|
|
|
* - 'withaccess' to add an access-key hint
|
|
|
|
|
* - 'nonexisting' to add an accessibility hint that page does not exist
|
2016-01-12 13:45:30 +00:00
|
|
|
* @param array $msgParams Parameters to pass to the message
|
2022-02-17 17:16:47 +00:00
|
|
|
* @param MessageLocalizer|null $localizer
|
2016-01-12 13:45:30 +00:00
|
|
|
*
|
2021-10-21 18:09:51 +00:00
|
|
|
* @return string|false Contents of the title attribute (which you must HTML-
|
2008-10-27 23:58:00 +00:00
|
|
|
* escape), or false for no title attribute
|
2007-01-10 06:03:14 +00:00
|
|
|
*/
|
2022-02-17 17:16:47 +00:00
|
|
|
public static function titleAttrib( $name, $options = null, array $msgParams = [], $localizer = null ) {
|
|
|
|
|
if ( !$localizer ) {
|
|
|
|
|
$localizer = self::getContextFromMain();
|
|
|
|
|
}
|
|
|
|
|
$message = $localizer->msg( "tooltip-$name", $msgParams );
|
2022-02-08 04:27:11 +00:00
|
|
|
if ( $message->isDisabled() ) {
|
2008-10-27 23:58:00 +00:00
|
|
|
$tooltip = false;
|
2010-08-05 16:22:42 +00:00
|
|
|
} else {
|
2010-12-25 12:33:17 +00:00
|
|
|
$tooltip = $message->text();
|
2010-08-05 16:22:42 +00:00
|
|
|
# Compatibility: formerly some tooltips had [alt-.] hardcoded
|
|
|
|
|
$tooltip = preg_replace( "/ ?\[alt-.\]$/", '', $tooltip );
|
2008-08-13 15:11:36 +00:00
|
|
|
}
|
|
|
|
|
|
2018-01-03 14:33:58 +00:00
|
|
|
$options = (array)$options;
|
|
|
|
|
|
|
|
|
|
if ( in_array( 'nonexisting', $options ) ) {
|
2022-02-17 17:16:47 +00:00
|
|
|
$tooltip = $localizer->msg( 'red-link-title', $tooltip ?: '' )->text();
|
2018-01-03 14:33:58 +00:00
|
|
|
}
|
|
|
|
|
if ( in_array( 'withaccess', $options ) ) {
|
2022-02-17 17:16:47 +00:00
|
|
|
$accesskey = self::accesskey( $name, $localizer );
|
2010-08-26 19:40:29 +00:00
|
|
|
if ( $accesskey !== false ) {
|
2014-05-13 18:23:05 +00:00
|
|
|
// Should be build the same as in jquery.accessKeyLabel.js
|
2008-10-27 23:58:00 +00:00
|
|
|
if ( $tooltip === false || $tooltip === '' ) {
|
2022-02-17 17:16:47 +00:00
|
|
|
$tooltip = $localizer->msg( 'brackets', $accesskey )->text();
|
2008-10-27 23:58:00 +00:00
|
|
|
} else {
|
2022-02-17 17:16:47 +00:00
|
|
|
$tooltip .= $localizer->msg( 'word-separator' )->text();
|
|
|
|
|
$tooltip .= $localizer->msg( 'brackets', $accesskey )->text();
|
2008-10-27 23:58:00 +00:00
|
|
|
}
|
2008-08-13 15:11:36 +00:00
|
|
|
}
|
2007-01-10 06:03:14 +00:00
|
|
|
}
|
|
|
|
|
|
2008-10-27 23:58:00 +00:00
|
|
|
return $tooltip;
|
|
|
|
|
}
|
|
|
|
|
|
2014-08-02 23:48:54 +00:00
|
|
|
public static $accesskeycache;
|
2011-04-03 11:44:11 +00:00
|
|
|
|
2008-10-27 23:58:00 +00:00
|
|
|
/**
|
|
|
|
|
* Given the id of an interface element, constructs the appropriate
|
|
|
|
|
* accesskey attribute from the system messages. (Note, this is usually
|
|
|
|
|
* the id but isn't always, because sometimes the accesskey needs to go on
|
|
|
|
|
* a different element than the id, for reverse-compatibility, etc.)
|
|
|
|
|
*
|
2016-05-04 12:48:58 +00:00
|
|
|
* @since 1.16.3
|
2014-04-08 15:29:17 +00:00
|
|
|
* @param string $name Id of the element, minus prefixes.
|
2022-02-17 17:16:47 +00:00
|
|
|
* @param MessageLocalizer|null $localizer
|
2021-10-21 18:09:51 +00:00
|
|
|
* @return string|false Contents of the accesskey attribute (which you must HTML-
|
2008-10-27 23:58:00 +00:00
|
|
|
* escape), or false for no accesskey attribute
|
|
|
|
|
*/
|
2022-02-17 17:16:47 +00:00
|
|
|
public static function accesskey( $name, $localizer = null ) {
|
2022-02-25 14:02:18 +00:00
|
|
|
if ( !isset( self::$accesskeycache[$name] ) ) {
|
|
|
|
|
if ( !$localizer ) {
|
|
|
|
|
$localizer = self::getContextFromMain();
|
|
|
|
|
}
|
|
|
|
|
$msg = $localizer->msg( "accesskey-$name" );
|
|
|
|
|
self::$accesskeycache[$name] = $msg->isDisabled() ? false : $msg->plain();
|
2008-10-27 23:58:00 +00:00
|
|
|
}
|
2014-01-01 18:56:08 +00:00
|
|
|
return self::$accesskeycache[$name];
|
2007-01-10 06:03:14 +00:00
|
|
|
}
|
2010-01-07 09:32:09 +00:00
|
|
|
|
2011-08-03 22:37:20 +00:00
|
|
|
/**
|
|
|
|
|
* Get a revision-deletion link, or disabled link, or nothing, depending
|
|
|
|
|
* on user permissions & the settings on the revision.
|
|
|
|
|
*
|
|
|
|
|
* Will use forward-compatible revision ID in the Special:RevDelete link
|
|
|
|
|
* if possible, otherwise the timestamp-based ID which may break after
|
|
|
|
|
* undeletion.
|
|
|
|
|
*
|
2021-02-24 15:46:13 +00:00
|
|
|
* @param Authority $performer
|
2021-04-21 15:43:13 +00:00
|
|
|
* @param RevisionRecord $revRecord (Switched from the old Revision class to RevisionRecord
|
|
|
|
|
* since 1.35)
|
2019-04-15 12:47:32 +00:00
|
|
|
* @param LinkTarget $title
|
2011-08-03 22:37:20 +00:00
|
|
|
* @return string HTML fragment
|
|
|
|
|
*/
|
2021-04-21 15:43:13 +00:00
|
|
|
public static function getRevDeleteLink(
|
|
|
|
|
Authority $performer,
|
|
|
|
|
RevisionRecord $revRecord,
|
|
|
|
|
LinkTarget $title
|
|
|
|
|
) {
|
2021-02-24 15:46:13 +00:00
|
|
|
$canHide = $performer->isAllowed( 'deleterevision' );
|
|
|
|
|
$canHideHistory = $performer->isAllowed( 'deletedhistory' );
|
2020-04-06 22:25:41 +00:00
|
|
|
if ( !$canHide && !( $revRecord->getVisibility() && $canHideHistory ) ) {
|
2011-12-28 18:41:36 +00:00
|
|
|
return '';
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-24 15:46:13 +00:00
|
|
|
if ( !$revRecord->userCan( RevisionRecord::DELETED_RESTRICTED, $performer ) ) {
|
2017-07-23 01:24:09 +00:00
|
|
|
return self::revDeleteLinkDisabled( $canHide ); // revision was hidden from sysops
|
2019-01-03 10:13:19 +00:00
|
|
|
}
|
2019-04-15 12:47:32 +00:00
|
|
|
$prefixedDbKey = MediaWikiServices::getInstance()->getTitleFormatter()->
|
|
|
|
|
getPrefixedDBkey( $title );
|
2020-04-06 22:25:41 +00:00
|
|
|
if ( $revRecord->getId() ) {
|
2019-01-03 10:13:19 +00:00
|
|
|
// RevDelete links using revision ID are stable across
|
|
|
|
|
// page deletion and undeletion; use when possible.
|
|
|
|
|
$query = [
|
|
|
|
|
'type' => 'revision',
|
2019-04-15 12:47:32 +00:00
|
|
|
'target' => $prefixedDbKey,
|
2020-04-06 22:25:41 +00:00
|
|
|
'ids' => $revRecord->getId()
|
2019-01-03 10:13:19 +00:00
|
|
|
];
|
2011-12-28 18:41:36 +00:00
|
|
|
} else {
|
2019-01-03 10:13:19 +00:00
|
|
|
// Older deleted entries didn't save a revision ID.
|
|
|
|
|
// We have to refer to these by timestamp, ick!
|
|
|
|
|
$query = [
|
|
|
|
|
'type' => 'archive',
|
2019-04-15 12:47:32 +00:00
|
|
|
'target' => $prefixedDbKey,
|
2020-04-06 22:25:41 +00:00
|
|
|
'ids' => $revRecord->getTimestamp()
|
2019-01-03 10:13:19 +00:00
|
|
|
];
|
2011-08-03 22:37:20 +00:00
|
|
|
}
|
2020-04-06 22:25:41 +00:00
|
|
|
return self::revDeleteLink(
|
|
|
|
|
$query,
|
|
|
|
|
$revRecord->isDeleted( RevisionRecord::DELETED_RESTRICTED ),
|
|
|
|
|
$canHide
|
|
|
|
|
);
|
2011-08-03 22:37:20 +00:00
|
|
|
}
|
|
|
|
|
|
2009-01-18 21:07:09 +00:00
|
|
|
/**
|
|
|
|
|
* Creates a (show/hide) link for deleting revisions/log entries
|
|
|
|
|
*
|
2021-04-19 16:55:41 +00:00
|
|
|
* This method produces HTML that requires CSS styles in mediawiki.interface.helpers.styles.
|
|
|
|
|
*
|
2014-04-08 15:29:17 +00:00
|
|
|
* @param array $query Query parameters to be passed to link()
|
|
|
|
|
* @param bool $restricted Set to true to use a "<strong>" instead of a "<span>"
|
|
|
|
|
* @param bool $delete Set to true to use (show/hide) rather than (show)
|
2009-01-18 21:07:09 +00:00
|
|
|
*
|
2014-04-08 15:29:17 +00:00
|
|
|
* @return string HTML "<a>" link to Special:Revisiondelete, wrapped in a
|
2009-01-18 21:07:09 +00:00
|
|
|
* span to allow for customization of appearance with CSS
|
|
|
|
|
*/
|
2016-02-17 09:09:32 +00:00
|
|
|
public static function revDeleteLink( $query = [], $restricted = false, $delete = true ) {
|
2009-01-18 21:07:09 +00:00
|
|
|
$sp = SpecialPage::getTitleFor( 'Revisiondelete' );
|
2012-08-19 20:44:29 +00:00
|
|
|
$msgKey = $delete ? 'rev-delundel' : 'rev-showdeleted';
|
|
|
|
|
$html = wfMessage( $msgKey )->escaped();
|
2009-02-22 14:35:29 +00:00
|
|
|
$tag = $restricted ? 'strong' : 'span';
|
2016-02-17 09:09:32 +00:00
|
|
|
$link = self::link( $sp, $html, [], $query, [ 'known', 'noclasses' ] );
|
2014-05-15 15:38:28 +00:00
|
|
|
return Xml::tags(
|
|
|
|
|
$tag,
|
2016-02-17 09:09:32 +00:00
|
|
|
[ 'class' => 'mw-revdelundel-link' ],
|
2014-05-15 15:38:28 +00:00
|
|
|
wfMessage( 'parentheses' )->rawParams( $link )->escaped()
|
|
|
|
|
);
|
2009-01-18 21:07:09 +00:00
|
|
|
}
|
2010-01-07 09:32:09 +00:00
|
|
|
|
2009-10-30 02:14:22 +00:00
|
|
|
/**
|
|
|
|
|
* Creates a dead (show/hide) link for deleting revisions/log entries
|
|
|
|
|
*
|
2021-04-19 16:55:41 +00:00
|
|
|
* This method produces HTML that requires CSS styles in mediawiki.interface.helpers.styles.
|
|
|
|
|
*
|
2016-05-04 12:48:58 +00:00
|
|
|
* @since 1.16.3
|
2014-04-08 15:29:17 +00:00
|
|
|
* @param bool $delete Set to true to use (show/hide) rather than (show)
|
2009-10-30 02:14:22 +00:00
|
|
|
*
|
|
|
|
|
* @return string HTML text wrapped in a span to allow for customization
|
|
|
|
|
* of appearance with CSS
|
|
|
|
|
*/
|
2011-04-03 12:04:04 +00:00
|
|
|
public static function revDeleteLinkDisabled( $delete = true ) {
|
2012-08-19 20:44:29 +00:00
|
|
|
$msgKey = $delete ? 'rev-delundel' : 'rev-showdeleted';
|
|
|
|
|
$html = wfMessage( $msgKey )->escaped();
|
|
|
|
|
$htmlParentheses = wfMessage( 'parentheses' )->rawParams( $html )->escaped();
|
2016-02-17 09:09:32 +00:00
|
|
|
return Xml::tags( 'span', [ 'class' => 'mw-revdelundel-link' ], $htmlParentheses );
|
2009-10-30 02:14:22 +00:00
|
|
|
}
|
2009-06-08 22:33:37 +00:00
|
|
|
|
2022-02-17 17:16:47 +00:00
|
|
|
/**
|
|
|
|
|
* Updates the tooltip message and its parameters if watchlist expiry is enabled.
|
|
|
|
|
*
|
|
|
|
|
* @param string &$tooltip the default tooltip
|
|
|
|
|
* @param array &$msgParams the tooltip message parameters.
|
|
|
|
|
* @param Config|null $config Only needed for generating tooltip for watchlist expiry.
|
|
|
|
|
* @param User|null $user Only needed for generating tooltip for watchlist expiry.
|
|
|
|
|
* @param Title|null $relevantTitle Only needed for generating tooltip for watchlist expiry.
|
|
|
|
|
*
|
|
|
|
|
* @return void
|
|
|
|
|
*/
|
|
|
|
|
private static function updateWatchstarTooltipMessage(
|
|
|
|
|
string &$tooltip, array &$msgParams, $config, $user, $relevantTitle
|
|
|
|
|
): void {
|
|
|
|
|
if ( !$config || !$user || !$relevantTitle ) {
|
|
|
|
|
$mainContext = self::getContextFromMain();
|
|
|
|
|
if ( !$config ) {
|
|
|
|
|
$config = $mainContext->getConfig();
|
|
|
|
|
}
|
|
|
|
|
if ( !$user ) {
|
|
|
|
|
$user = $mainContext->getUser();
|
|
|
|
|
}
|
|
|
|
|
if ( !$relevantTitle ) {
|
|
|
|
|
$relevantTitle = $mainContext->getSkin()->getRelevantTitle();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-01 15:58:32 +00:00
|
|
|
$isWatchlistExpiryEnabled = $config->get( MainConfigNames::WatchlistExpiry );
|
2021-12-07 00:21:47 +00:00
|
|
|
if ( !$isWatchlistExpiryEnabled || !$relevantTitle || !$relevantTitle->canExist() ) {
|
2022-02-17 17:16:47 +00:00
|
|
|
return;
|
|
|
|
|
}
|
2021-12-07 00:21:47 +00:00
|
|
|
|
2022-02-17 17:16:47 +00:00
|
|
|
$watchStore = MediaWikiServices::getInstance()->getWatchedItemStore();
|
2022-03-30 16:17:29 +00:00
|
|
|
$watchedItem = $watchStore->getWatchedItem( $user, $relevantTitle );
|
2022-02-17 17:16:47 +00:00
|
|
|
if ( $watchedItem instanceof WatchedItem && $watchedItem->getExpiry() !== null ) {
|
|
|
|
|
$diffInDays = $watchedItem->getExpiryInDays();
|
|
|
|
|
|
|
|
|
|
if ( $diffInDays ) {
|
|
|
|
|
$msgParams = [ $diffInDays ];
|
|
|
|
|
// Resolves to tooltip-ca-unwatch-expiring message
|
|
|
|
|
$tooltip = 'ca-unwatch-expiring';
|
|
|
|
|
} else { // Resolves to tooltip-ca-unwatch-expiring-hours message
|
|
|
|
|
$tooltip = 'ca-unwatch-expiring-hours';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-06-08 22:33:37 +00:00
|
|
|
/**
|
2010-08-05 16:22:42 +00:00
|
|
|
* Returns the attributes for the tooltip and access key.
|
2016-01-12 13:45:30 +00:00
|
|
|
*
|
2016-05-04 12:48:58 +00:00
|
|
|
* @since 1.16.3. $msgParams introduced in 1.27
|
2014-04-22 11:07:02 +00:00
|
|
|
* @param string $name
|
2016-01-12 13:45:30 +00:00
|
|
|
* @param array $msgParams Params for constructing the message
|
2018-01-03 14:33:58 +00:00
|
|
|
* @param string|array|null $options Options to be passed to titleAttrib.
|
2022-02-17 17:16:47 +00:00
|
|
|
* @param MessageLocalizer|null $localizer
|
|
|
|
|
* @param User|null $user Only needed for generating tooltip for watchlist expiry.
|
|
|
|
|
* @param Config|null $config Only needed for generating tooltip for watchlist expiry.
|
|
|
|
|
* @param Title|null $relevantTitle Only needed for generating tooltip for watchlist expiry.
|
2018-01-03 14:33:58 +00:00
|
|
|
*
|
|
|
|
|
* @see Linker::titleAttrib for what options could be passed to $options.
|
2016-01-12 13:45:30 +00:00
|
|
|
*
|
2012-02-09 21:35:05 +00:00
|
|
|
* @return array
|
2009-06-08 22:33:37 +00:00
|
|
|
*/
|
2018-01-03 14:33:58 +00:00
|
|
|
public static function tooltipAndAccesskeyAttribs(
|
|
|
|
|
$name,
|
|
|
|
|
array $msgParams = [],
|
2022-02-17 17:16:47 +00:00
|
|
|
$options = null,
|
|
|
|
|
$localizer = null,
|
|
|
|
|
$user = null,
|
|
|
|
|
$config = null,
|
|
|
|
|
$relevantTitle = null
|
2018-01-03 14:33:58 +00:00
|
|
|
) {
|
|
|
|
|
$options = (array)$options;
|
|
|
|
|
$options[] = 'withaccess';
|
2020-05-18 19:54:49 +00:00
|
|
|
$tooltipTitle = $name;
|
|
|
|
|
|
2022-02-17 17:16:47 +00:00
|
|
|
// Get optional parameters from global context if any missing.
|
|
|
|
|
if ( !$localizer ) {
|
|
|
|
|
$localizer = self::getContextFromMain();
|
|
|
|
|
}
|
2020-07-23 01:24:42 +00:00
|
|
|
|
2022-02-17 17:16:47 +00:00
|
|
|
// @since 1.35 - add a WatchlistExpiry feature flag to show new watchstar tooltip message
|
|
|
|
|
if ( $name === 'ca-unwatch' ) {
|
|
|
|
|
self::updateWatchstarTooltipMessage( $tooltipTitle, $msgParams, $config, $user, $relevantTitle );
|
2020-05-18 19:54:49 +00:00
|
|
|
}
|
2018-01-03 14:33:58 +00:00
|
|
|
|
2016-02-17 09:09:32 +00:00
|
|
|
$attribs = [
|
2022-02-17 17:16:47 +00:00
|
|
|
'title' => self::titleAttrib( $tooltipTitle, $options, $msgParams, $localizer ),
|
|
|
|
|
'accesskey' => self::accesskey( $name, $localizer )
|
2016-02-17 09:09:32 +00:00
|
|
|
];
|
2009-06-08 22:33:37 +00:00
|
|
|
if ( $attribs['title'] === false ) {
|
|
|
|
|
unset( $attribs['title'] );
|
|
|
|
|
}
|
|
|
|
|
if ( $attribs['accesskey'] === false ) {
|
|
|
|
|
unset( $attribs['accesskey'] );
|
|
|
|
|
}
|
2009-10-18 19:29:35 +00:00
|
|
|
return $attribs;
|
|
|
|
|
}
|
2011-01-06 18:53:53 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns raw bits of HTML, use titleAttrib()
|
2016-05-04 12:48:58 +00:00
|
|
|
* @since 1.16.3
|
2014-04-22 11:07:02 +00:00
|
|
|
* @param string $name
|
|
|
|
|
* @param array|null $options
|
2012-02-09 21:35:05 +00:00
|
|
|
* @return null|string
|
2011-01-06 18:53:53 +00:00
|
|
|
*/
|
2011-04-03 12:04:04 +00:00
|
|
|
public static function tooltip( $name, $options = null ) {
|
2011-04-03 11:44:11 +00:00
|
|
|
$tooltip = self::titleAttrib( $name, $options );
|
2009-06-08 22:33:37 +00:00
|
|
|
if ( $tooltip === false ) {
|
|
|
|
|
return '';
|
|
|
|
|
}
|
2016-02-17 09:09:32 +00:00
|
|
|
return Xml::expandAttributes( [
|
2011-05-06 23:07:20 +00:00
|
|
|
'title' => $tooltip
|
2016-02-17 09:09:32 +00:00
|
|
|
] );
|
2009-06-08 22:33:37 +00:00
|
|
|
}
|
2015-09-07 17:02:24 +00:00
|
|
|
|
2004-12-18 06:29:23 +00:00
|
|
|
}
|
2022-12-05 11:29:37 +00:00
|
|
|
|
|
|
|
|
class_alias( Linker::class, 'Linker' );
|