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
This commit is contained in:
parent
f3cf265e75
commit
f7f84dddb3
55 changed files with 3658 additions and 461 deletions
|
|
@ -142,6 +142,8 @@ because of Phabricator reports.
|
||||||
* PageProps::getInstance() has been deprecated. Use
|
* PageProps::getInstance() has been deprecated. Use
|
||||||
MediaWikiServices::getPageProps() instead.
|
MediaWikiServices::getPageProps() instead.
|
||||||
* User::setOption(), deprecated since 1.35, now emits deprecation warnings.
|
* User::setOption(), deprecated since 1.35, now emits deprecation warnings.
|
||||||
|
* Linker::formatComment(), ::formatLinksInComment(), ::commentBlock() and
|
||||||
|
revComment() were soft-deprecated. Use the new CommentFormatter service.
|
||||||
* Skin::getSkinStylePath has been hard deprecated. Direct string path
|
* Skin::getSkinStylePath has been hard deprecated. Direct string path
|
||||||
should be used instead.
|
should be used instead.
|
||||||
* SkinTemplate::getPersonalToolsList(), deprecated since 1.35, now emits
|
* SkinTemplate::getPersonalToolsList(), deprecated since 1.35, now emits
|
||||||
|
|
|
||||||
194
includes/CommentFormatter/CommentBatch.php
Normal file
194
includes/CommentFormatter/CommentBatch.php
Normal file
|
|
@ -0,0 +1,194 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MediaWiki\CommentFormatter;
|
||||||
|
|
||||||
|
use MediaWiki\Linker\LinkTarget;
|
||||||
|
use Traversable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class provides a fluent interface for formatting a batch of comments.
|
||||||
|
*
|
||||||
|
* @since 1.38
|
||||||
|
*/
|
||||||
|
class CommentBatch {
|
||||||
|
/** @var CommentFormatter */
|
||||||
|
private $formatter;
|
||||||
|
/** @var iterable<CommentItem>|Traversable */
|
||||||
|
private $comments;
|
||||||
|
/** @var bool|null */
|
||||||
|
private $useBlock;
|
||||||
|
/** @var bool|null */
|
||||||
|
private $useParentheses;
|
||||||
|
/** @var LinkTarget|null */
|
||||||
|
private $selfLinkTarget;
|
||||||
|
/** @var bool|null */
|
||||||
|
private $samePage;
|
||||||
|
/** @var string|false|null */
|
||||||
|
private $wikiId;
|
||||||
|
/** @var bool|null */
|
||||||
|
private $enableSectionLinks;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal Use CommentFormatter::createBatch()
|
||||||
|
*
|
||||||
|
* @param CommentFormatter $formatter
|
||||||
|
*/
|
||||||
|
public function __construct( CommentFormatter $formatter ) {
|
||||||
|
$this->formatter = $formatter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the comments to be formatted. This can be an array of CommentItem
|
||||||
|
* objects, or it can be an iterator which generates CommentItem objects.
|
||||||
|
*
|
||||||
|
* Theoretically iterable should imply Traversable, but PHPStorm gives an
|
||||||
|
* error when RowCommentIterator is passed as iterable<CommentItem>.
|
||||||
|
*
|
||||||
|
* @param iterable<CommentItem>|Traversable $comments
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function comments( $comments ) {
|
||||||
|
$this->comments = $comments;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify the comments to be formatted as an array of strings. This is a
|
||||||
|
* simplified wrapper for comments() which does not allow you to set options
|
||||||
|
* on a per-comment basis.
|
||||||
|
*
|
||||||
|
* $strings must be an array -- use comments() if you want to use an iterator.
|
||||||
|
*
|
||||||
|
* @param string[] $strings
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function strings( array $strings ) {
|
||||||
|
$this->comments = new StringCommentIterator( $strings );
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrap each comment in standard punctuation and formatting if it's
|
||||||
|
* non-empty. Empty comments remain empty. This causes the batch to work
|
||||||
|
* like the old Linker::commentBlock().
|
||||||
|
*
|
||||||
|
* If this function is not called, the option is false.
|
||||||
|
*
|
||||||
|
* @param bool $useBlock
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function useBlock( $useBlock = true ) {
|
||||||
|
$this->useBlock = $useBlock;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrap each comment with parentheses. This has no effect if the useBlock
|
||||||
|
* option is not enabled.
|
||||||
|
*
|
||||||
|
* Unlike the legacy Linker::commentBlock(), this option defaults to false
|
||||||
|
* if this method is not called, since that better preserves the fluent
|
||||||
|
* style.
|
||||||
|
*
|
||||||
|
* @param bool $useParentheses
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function useParentheses( $useParentheses = true ) {
|
||||||
|
$this->useParentheses = $useParentheses;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the title to be used for self links in the comments. If there is no
|
||||||
|
* title specified either here or in the item, fragment links are not
|
||||||
|
* expanded.
|
||||||
|
*
|
||||||
|
* @param LinkTarget $selfLinkTarget
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function selfLinkTarget( LinkTarget $selfLinkTarget ) {
|
||||||
|
$this->selfLinkTarget = $selfLinkTarget;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the option to enable/disable section links formatted as C-style
|
||||||
|
* comments, as used in revision comments to indicate the section which
|
||||||
|
* was edited.
|
||||||
|
*
|
||||||
|
* If the method is not called, the option is true. Setting this to false
|
||||||
|
* approximately emulates Linker::formatLinksInComment() except that HTML
|
||||||
|
* in the input is escaped.
|
||||||
|
*
|
||||||
|
* @param bool $enable
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function enableSectionLinks( $enable ) {
|
||||||
|
$this->enableSectionLinks = $enable;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disable section links formatted as C-style comments, as used in revision
|
||||||
|
* comments to indicate the section which was edited. Calling this
|
||||||
|
* approximately emulates Linker::formatLinksInComment() except that HTML
|
||||||
|
* in the input is escaped.
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function disableSectionLinks() {
|
||||||
|
$this->enableSectionLinks = false;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the same-page option. If this is true, section links and fragment-
|
||||||
|
* only wikilinks are rendered with an href that is a fragment-only URL.
|
||||||
|
* If it is false (the default), such links go to the self link title.
|
||||||
|
*
|
||||||
|
* This can also be set per-item using CommentItem::samePage().
|
||||||
|
*
|
||||||
|
* This is equivalent to $local in the old Linker methods.
|
||||||
|
*
|
||||||
|
* @param bool $samePage
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function samePage( $samePage = true ) {
|
||||||
|
$this->samePage = $samePage;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ID of the wiki to link to (if not the local wiki), as used by WikiMap.
|
||||||
|
* This is used to render comments which are loaded from a foreign wiki.
|
||||||
|
* This only affects links which are syntactically internal -- it has no
|
||||||
|
* effect on interwiki links.
|
||||||
|
*
|
||||||
|
* This can also be set per-item using CommentItem::wikiId().
|
||||||
|
*
|
||||||
|
* @param string|false|null $wikiId
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function wikiId( $wikiId ) {
|
||||||
|
$this->wikiId = $wikiId;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format the comments and produce an array of HTML fragments.
|
||||||
|
*
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
public function execute() {
|
||||||
|
return $this->formatter->formatItemsInternal(
|
||||||
|
$this->comments,
|
||||||
|
$this->selfLinkTarget,
|
||||||
|
$this->samePage,
|
||||||
|
$this->wikiId,
|
||||||
|
$this->enableSectionLinks,
|
||||||
|
$this->useBlock,
|
||||||
|
$this->useParentheses
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
408
includes/CommentFormatter/CommentFormatter.php
Normal file
408
includes/CommentFormatter/CommentFormatter.php
Normal file
|
|
@ -0,0 +1,408 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MediaWiki\CommentFormatter;
|
||||||
|
|
||||||
|
use MediaWiki\Linker\LinkTarget;
|
||||||
|
use MediaWiki\Permissions\Authority;
|
||||||
|
use MediaWiki\Revision\RevisionRecord;
|
||||||
|
use Traversable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the main service interface for converting single-line comments from
|
||||||
|
* various DB comment fields into HTML.
|
||||||
|
*
|
||||||
|
* @since 1.38
|
||||||
|
*/
|
||||||
|
class CommentFormatter {
|
||||||
|
/** @var CommentParserFactory */
|
||||||
|
protected $parserFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal Use MediaWikiServices::getCommentFormatter()
|
||||||
|
*
|
||||||
|
* @param CommentParserFactory $parserFactory
|
||||||
|
*/
|
||||||
|
public function __construct( CommentParserFactory $parserFactory ) {
|
||||||
|
$this->parserFactory = $parserFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format comments using a fluent interface.
|
||||||
|
*
|
||||||
|
* @return CommentBatch
|
||||||
|
*/
|
||||||
|
public function createBatch() {
|
||||||
|
return new CommentBatch( $this );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format a single comment. Similar to the old Linker::formatComment().
|
||||||
|
*
|
||||||
|
* @param string $comment
|
||||||
|
* @param LinkTarget|null $selfLinkTarget The title used for fragment-only
|
||||||
|
* and section links, formerly $title.
|
||||||
|
* @param bool $samePage If true, self links are rendered with a fragment-
|
||||||
|
* only URL. Formerly $local.
|
||||||
|
* @param string|false|null $wikiId ID of the wiki to link to (if not the local
|
||||||
|
* wiki), as used by WikiMap.
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function format( string $comment, LinkTarget $selfLinkTarget = null,
|
||||||
|
$samePage = false, $wikiId = false
|
||||||
|
) {
|
||||||
|
return $this->formatInternal( $comment, true, false, false,
|
||||||
|
$selfLinkTarget, $samePage, $wikiId );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrap a comment in standard punctuation and formatting if
|
||||||
|
* it's non-empty, otherwise return an empty string.
|
||||||
|
*
|
||||||
|
* @param string $comment
|
||||||
|
* @param LinkTarget|null $selfLinkTarget The title used for fragment-only
|
||||||
|
* and section links, formerly $title.
|
||||||
|
* @param bool $samePage If true, self links are rendered with a fragment-
|
||||||
|
* only URL. Formerly $local.
|
||||||
|
* @param string|false|null $wikiId ID of the wiki to link to (if not the local
|
||||||
|
* wiki), as used by WikiMap.
|
||||||
|
* @param bool $useParentheses
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function formatBlock( string $comment, LinkTarget $selfLinkTarget = null,
|
||||||
|
$samePage = false, $wikiId = false, $useParentheses = true
|
||||||
|
) {
|
||||||
|
return $this->formatInternal( $comment, true, true, $useParentheses,
|
||||||
|
$selfLinkTarget, $samePage, $wikiId );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format a comment, passing through HTML in the input to the output.
|
||||||
|
* This is unsafe and exists only for backwards compatibility with
|
||||||
|
* Linker::formatLinksInComment().
|
||||||
|
*
|
||||||
|
* In new code, use formatLinks() or createBatch()->disableSectionLinks().
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*
|
||||||
|
* @param string $comment
|
||||||
|
* @param LinkTarget|null $selfLinkTarget The title used for fragment-only
|
||||||
|
* and section links, formerly $title.
|
||||||
|
* @param bool $samePage If true, self links are rendered with a fragment-
|
||||||
|
* only URL. Formerly $local.
|
||||||
|
* @param string|false|null $wikiId ID of the wiki to link to (if not the local
|
||||||
|
* wiki), as used by WikiMap.
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function formatLinksUnsafe( string $comment, LinkTarget $selfLinkTarget = null,
|
||||||
|
$samePage = false, $wikiId = false
|
||||||
|
) {
|
||||||
|
$parser = $this->parserFactory->create();
|
||||||
|
$preprocessed = $parser->preprocessUnsafe( $comment, $selfLinkTarget,
|
||||||
|
$samePage, $wikiId, false );
|
||||||
|
return $parser->finalize( $preprocessed );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format links in a comment, ignoring section links in C-style comments.
|
||||||
|
*
|
||||||
|
* @param string $comment
|
||||||
|
* @param LinkTarget|null $selfLinkTarget The title used for fragment-only
|
||||||
|
* and section links, formerly $title.
|
||||||
|
* @param bool $samePage If true, self links are rendered with a fragment-
|
||||||
|
* only URL. Formerly $local.
|
||||||
|
* @param string|false|null $wikiId ID of the wiki to link to (if not the local
|
||||||
|
* wiki), as used by WikiMap.
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function formatLinks( string $comment, LinkTarget $selfLinkTarget = null,
|
||||||
|
$samePage = false, $wikiId = false
|
||||||
|
) {
|
||||||
|
return $this->formatInternal( $comment, false, false, false,
|
||||||
|
$selfLinkTarget, $samePage, $wikiId );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format a single comment with many ugly boolean parameters.
|
||||||
|
*
|
||||||
|
* @param string $comment
|
||||||
|
* @param bool $enableSectionLinks
|
||||||
|
* @param bool $useBlock
|
||||||
|
* @param bool $useParentheses
|
||||||
|
* @param LinkTarget|null $selfLinkTarget The title used for fragment-only
|
||||||
|
* and section links, formerly $title.
|
||||||
|
* @param bool $samePage If true, self links are rendered with a fragment-
|
||||||
|
* only URL. Formerly $local.
|
||||||
|
* @param string|false|null $wikiId ID of the wiki to link to (if not the local
|
||||||
|
* wiki), as used by WikiMap.
|
||||||
|
* @return string|string[]
|
||||||
|
*/
|
||||||
|
private function formatInternal( $comment, $enableSectionLinks, $useBlock, $useParentheses,
|
||||||
|
$selfLinkTarget = null, $samePage = false, $wikiId = false
|
||||||
|
) {
|
||||||
|
$parser = $this->parserFactory->create();
|
||||||
|
$preprocessed = $parser->preprocess( $comment, $selfLinkTarget, $samePage, $wikiId,
|
||||||
|
$enableSectionLinks );
|
||||||
|
$output = $parser->finalize( $preprocessed );
|
||||||
|
if ( $useBlock ) {
|
||||||
|
$output = $this->wrapCommentWithBlock( $output, $useParentheses );
|
||||||
|
}
|
||||||
|
return $output;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format comments which are provided as strings and all have the same
|
||||||
|
* self-link target and other options.
|
||||||
|
*
|
||||||
|
* If you need a different title for each comment, use createBatch().
|
||||||
|
*
|
||||||
|
* @param string[] $strings
|
||||||
|
* @param LinkTarget|null $selfLinkTarget The title used for fragment-only
|
||||||
|
* and section links, formerly $title.
|
||||||
|
* @param bool $samePage If true, self links are rendered with a fragment-
|
||||||
|
* only URL. Formerly $local.
|
||||||
|
* @param string|false|null $wikiId ID of the wiki to link to (if not the local
|
||||||
|
* wiki), as used by WikiMap.
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
public function formatStrings( $strings, LinkTarget $selfLinkTarget = null,
|
||||||
|
$samePage = false, $wikiId = false
|
||||||
|
) {
|
||||||
|
$parser = $this->parserFactory->create();
|
||||||
|
$outputs = [];
|
||||||
|
foreach ( $strings as $i => $comment ) {
|
||||||
|
$outputs[$i] = $parser->preprocess( $comment, $selfLinkTarget, $samePage, $wikiId );
|
||||||
|
}
|
||||||
|
return $parser->finalize( $outputs );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given an array of comments as strings which all have the same self link
|
||||||
|
* target, format the comments and wrap them in standard punctuation and
|
||||||
|
* formatting.
|
||||||
|
*
|
||||||
|
* If you need a different title for each comment, use createBatch().
|
||||||
|
*
|
||||||
|
* @param string[] $strings
|
||||||
|
* @param LinkTarget|null $selfLinkTarget The title used for fragment-only
|
||||||
|
* and section links, formerly $title.
|
||||||
|
* @param bool $samePage If true, self links are rendered with a fragment-
|
||||||
|
* only URL. Formerly $local.
|
||||||
|
* @param string|false|null $wikiId ID of the wiki to link to (if not the local
|
||||||
|
* wiki), as used by WikiMap.
|
||||||
|
* @param bool $useParentheses
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
public function formatStringsAsBlock( $strings, LinkTarget $selfLinkTarget = null,
|
||||||
|
$samePage = false, $wikiId = false, $useParentheses = true
|
||||||
|
) {
|
||||||
|
$parser = $this->parserFactory->create();
|
||||||
|
$outputs = [];
|
||||||
|
foreach ( $strings as $i => $comment ) {
|
||||||
|
$outputs[$i] = $this->wrapCommentWithBlock(
|
||||||
|
$parser->preprocess( $comment, $selfLinkTarget, $samePage, $wikiId ),
|
||||||
|
$useParentheses );
|
||||||
|
}
|
||||||
|
return $parser->finalize( $outputs );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrap and format the given revision's comment block, if the specified
|
||||||
|
* user is allowed to view it.
|
||||||
|
*
|
||||||
|
* This method produces HTML that requires CSS styles in mediawiki.interface.helpers.styles.
|
||||||
|
*
|
||||||
|
* NOTE: revision comments are special. This is not the same as getting a
|
||||||
|
* revision comment as a string and then formatting it with format().
|
||||||
|
*
|
||||||
|
* @param RevisionRecord $revision The revision to extract the comment and
|
||||||
|
* title from. The title should always be populated, to avoid an additional
|
||||||
|
* DB query.
|
||||||
|
* @param Authority $authority The user viewing the comment
|
||||||
|
* @param bool $samePage If true, self links are rendered with a fragment-
|
||||||
|
* only URL. Formerly $local.
|
||||||
|
* @param bool $isPublic Show only if all users can see it
|
||||||
|
* @param bool $useParentheses Whether the comment is wrapped in parentheses
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function formatRevision(
|
||||||
|
RevisionRecord $revision,
|
||||||
|
Authority $authority,
|
||||||
|
$samePage = false,
|
||||||
|
$isPublic = false,
|
||||||
|
$useParentheses = true
|
||||||
|
) {
|
||||||
|
$parser = $this->parserFactory->create();
|
||||||
|
return $parser->finalize( $this->preprocessRevComment(
|
||||||
|
$parser, $authority, $revision, $samePage, $isPublic, $useParentheses ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format multiple revision comments.
|
||||||
|
*
|
||||||
|
* @see CommentFormatter::formatRevision()
|
||||||
|
*
|
||||||
|
* @param iterable<RevisionRecord> $revisions
|
||||||
|
* @param Authority $authority
|
||||||
|
* @param bool $samePage
|
||||||
|
* @param bool $isPublic
|
||||||
|
* @param bool $useParentheses
|
||||||
|
* @param bool $indexById
|
||||||
|
* @return string|string[]
|
||||||
|
*/
|
||||||
|
public function formatRevisions(
|
||||||
|
$revisions,
|
||||||
|
Authority $authority,
|
||||||
|
$samePage = false,
|
||||||
|
$isPublic = false,
|
||||||
|
$useParentheses = true,
|
||||||
|
$indexById = false
|
||||||
|
) {
|
||||||
|
$parser = $this->parserFactory->create();
|
||||||
|
$outputs = [];
|
||||||
|
foreach ( $revisions as $i => $rev ) {
|
||||||
|
if ( $indexById ) {
|
||||||
|
$key = $rev->getId();
|
||||||
|
} else {
|
||||||
|
$key = $i;
|
||||||
|
}
|
||||||
|
$outputs[$key] = $this->preprocessRevComment(
|
||||||
|
$parser, $authority, $rev, $samePage, $isPublic, $useParentheses );
|
||||||
|
}
|
||||||
|
return $parser->finalize( $outputs );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format a batch of revision comments using a fluent interface.
|
||||||
|
*
|
||||||
|
* @return RevisionCommentBatch
|
||||||
|
*/
|
||||||
|
public function createRevisionBatch() {
|
||||||
|
return new RevisionCommentBatch( $this );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format an iterator over CommentItem objects
|
||||||
|
*
|
||||||
|
* A shortcut for createBatch()->comments()->execute() for when you
|
||||||
|
* need to pass no other options.
|
||||||
|
*
|
||||||
|
* @param iterable<CommentItem>|Traversable $items
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
public function formatItems( $items ) {
|
||||||
|
return $this->formatItemsInternal( $items );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal For use by CommentBatch
|
||||||
|
*
|
||||||
|
* Format comments with nullable batch options.
|
||||||
|
*
|
||||||
|
* @param iterable<CommentItem> $items
|
||||||
|
* @param LinkTarget|null $selfLinkTarget
|
||||||
|
* @param bool|null $samePage
|
||||||
|
* @param string|false|null $wikiId
|
||||||
|
* @param bool|null $enableSectionLinks
|
||||||
|
* @param bool|null $useBlock
|
||||||
|
* @param bool|null $useParentheses
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
public function formatItemsInternal( $items, $selfLinkTarget = null,
|
||||||
|
$samePage = null, $wikiId = null, $enableSectionLinks = null,
|
||||||
|
$useBlock = null, $useParentheses = null
|
||||||
|
) {
|
||||||
|
$outputs = [];
|
||||||
|
$parser = $this->parserFactory->create();
|
||||||
|
foreach ( $items as $index => $item ) {
|
||||||
|
$preprocessed = $parser->preprocess(
|
||||||
|
$item->comment,
|
||||||
|
$item->selfLinkTarget ?? $selfLinkTarget,
|
||||||
|
$item->samePage ?? $samePage ?? false,
|
||||||
|
$item->wikiId ?? $wikiId ?? false,
|
||||||
|
$enableSectionLinks ?? true
|
||||||
|
);
|
||||||
|
if ( $useBlock ?? false ) {
|
||||||
|
$preprocessed = $this->wrapCommentWithBlock(
|
||||||
|
$preprocessed,
|
||||||
|
$useParentheses ?? true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$outputs[$index] = $preprocessed;
|
||||||
|
}
|
||||||
|
return $parser->finalize( $outputs );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrap a comment in standard punctuation and formatting if
|
||||||
|
* it's non-empty, otherwise return empty string.
|
||||||
|
*
|
||||||
|
* @param string $formatted
|
||||||
|
* @param bool $useParentheses Whether the comment is wrapped in parentheses
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function wrapCommentWithBlock(
|
||||||
|
$formatted, $useParentheses
|
||||||
|
) {
|
||||||
|
// '*' used to be the comment inserted by the software way back
|
||||||
|
// in antiquity in case none was provided, here for backwards
|
||||||
|
// compatibility, acc. to brion -ævar
|
||||||
|
if ( $formatted == '' || $formatted == '*' ) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
if ( $useParentheses ) {
|
||||||
|
$formatted = wfMessage( 'parentheses' )->rawParams( $formatted )->escaped();
|
||||||
|
$classNames = 'comment';
|
||||||
|
} else {
|
||||||
|
$classNames = 'comment comment--without-parentheses';
|
||||||
|
}
|
||||||
|
return " <span class=\"$classNames\">$formatted</span>";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Preprocess and wrap a revision comment.
|
||||||
|
*
|
||||||
|
* @param CommentParser $parser
|
||||||
|
* @param Authority $authority
|
||||||
|
* @param RevisionRecord $revRecord
|
||||||
|
* @param bool $samePage Whether section links should refer to local page
|
||||||
|
* @param bool $isPublic Show only if all users can see it
|
||||||
|
* @param bool $useParentheses (optional) Wrap comments in parentheses where needed
|
||||||
|
* @return string HTML fragment with link markers
|
||||||
|
*/
|
||||||
|
private function preprocessRevComment(
|
||||||
|
CommentParser $parser,
|
||||||
|
Authority $authority,
|
||||||
|
RevisionRecord $revRecord,
|
||||||
|
$samePage = false,
|
||||||
|
$isPublic = false,
|
||||||
|
$useParentheses = true
|
||||||
|
) {
|
||||||
|
if ( $revRecord->getComment( RevisionRecord::RAW ) === null ) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
if ( $revRecord->audienceCan(
|
||||||
|
RevisionRecord::DELETED_COMMENT,
|
||||||
|
$isPublic ? RevisionRecord::FOR_PUBLIC : RevisionRecord::FOR_THIS_USER,
|
||||||
|
$authority )
|
||||||
|
) {
|
||||||
|
$comment = $revRecord->getComment( RevisionRecord::FOR_THIS_USER, $authority );
|
||||||
|
$block = $parser->preprocess(
|
||||||
|
$comment ? $comment->text : '',
|
||||||
|
$revRecord->getPageAsLinkTarget(),
|
||||||
|
$samePage,
|
||||||
|
null,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
$block = $this->wrapCommentWithBlock( $block, $useParentheses );
|
||||||
|
} else {
|
||||||
|
$block = " <span class=\"comment\">" . wfMessage( 'rev-deleted-comment' )->escaped() . "</span>";
|
||||||
|
}
|
||||||
|
if ( $revRecord->isDeleted( RevisionRecord::DELETED_COMMENT ) ) {
|
||||||
|
$class = \Linker::getRevisionDeletedClass( $revRecord );
|
||||||
|
return " <span class=\"$class comment\">$block</span>";
|
||||||
|
}
|
||||||
|
return $block;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
82
includes/CommentFormatter/CommentItem.php
Normal file
82
includes/CommentFormatter/CommentItem.php
Normal file
|
|
@ -0,0 +1,82 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MediaWiki\CommentFormatter;
|
||||||
|
|
||||||
|
use MediaWiki\Linker\LinkTarget;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An object to represent one of the inputs to a batch formatting operation.
|
||||||
|
*
|
||||||
|
* @since 1.38
|
||||||
|
* @newable
|
||||||
|
*/
|
||||||
|
class CommentItem {
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
public $comment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var LinkTarget|null
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
public $selfLinkTarget;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool|null
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
public $samePage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string|false|null
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
public $wikiId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $comment The comment to format
|
||||||
|
*/
|
||||||
|
public function __construct( string $comment ) {
|
||||||
|
$this->comment = $comment;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the self-link target.
|
||||||
|
*
|
||||||
|
* @param LinkTarget $selfLinkTarget The title used for fragment-only
|
||||||
|
* and section links, formerly $title.
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function selfLinkTarget( LinkTarget $selfLinkTarget ) {
|
||||||
|
$this->selfLinkTarget = $selfLinkTarget;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the same-page flag.
|
||||||
|
*
|
||||||
|
* @param bool $samePage If true, self links are rendered with a fragment-
|
||||||
|
* only URL. Formerly $local.
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function samePage( $samePage = true ) {
|
||||||
|
$this->samePage = $samePage;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ID of the wiki to link to (if not the local wiki), as used by WikiMap.
|
||||||
|
* This is used to render comments which are loaded from a foreign wiki.
|
||||||
|
* This only affects links which are syntactically internal -- it has no
|
||||||
|
* effect on interwiki links.
|
||||||
|
*
|
||||||
|
* @param string|false|null $wikiId
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function wikiId( $wikiId ) {
|
||||||
|
$this->wikiId = $wikiId;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
||||||
528
includes/CommentFormatter/CommentParser.php
Normal file
528
includes/CommentFormatter/CommentParser.php
Normal file
|
|
@ -0,0 +1,528 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MediaWiki\CommentFormatter;
|
||||||
|
|
||||||
|
use File;
|
||||||
|
use HtmlArmor;
|
||||||
|
use Language;
|
||||||
|
use LinkBatch;
|
||||||
|
use LinkCache;
|
||||||
|
use Linker;
|
||||||
|
use MalformedTitleException;
|
||||||
|
use MediaWiki\Cache\LinkBatchFactory;
|
||||||
|
use MediaWiki\HookContainer\HookContainer;
|
||||||
|
use MediaWiki\HookContainer\HookRunner;
|
||||||
|
use MediaWiki\Linker\LinkRenderer;
|
||||||
|
use MediaWiki\Linker\LinkTarget;
|
||||||
|
use NamespaceInfo;
|
||||||
|
use Parser;
|
||||||
|
use RepoGroup;
|
||||||
|
use Title;
|
||||||
|
use TitleParser;
|
||||||
|
use TitleValue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The text processing backend for CommentFormatter.
|
||||||
|
*
|
||||||
|
* CommentParser objects should be discarded after the comment batch is
|
||||||
|
* complete, in order to reduce memory usage.
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
class CommentParser {
|
||||||
|
/** @var LinkRenderer */
|
||||||
|
private $linkRenderer;
|
||||||
|
/** @var LinkBatchFactory */
|
||||||
|
private $linkBatchFactory;
|
||||||
|
/** @var RepoGroup */
|
||||||
|
private $repoGroup;
|
||||||
|
/** @var Language */
|
||||||
|
private $userLang;
|
||||||
|
/** @var Language */
|
||||||
|
private $contLang;
|
||||||
|
/** @var TitleParser */
|
||||||
|
private $titleParser;
|
||||||
|
/** @var NamespaceInfo */
|
||||||
|
private $namespaceInfo;
|
||||||
|
/** @var HookRunner */
|
||||||
|
private $hookRunner;
|
||||||
|
/** @var LinkCache */
|
||||||
|
private $linkCache;
|
||||||
|
|
||||||
|
/** @var callable[] */
|
||||||
|
private $links = [];
|
||||||
|
/** @var LinkBatch|null */
|
||||||
|
private $linkBatch;
|
||||||
|
|
||||||
|
/** @var array Input to RepoGroup::findFiles() */
|
||||||
|
private $fileBatch;
|
||||||
|
/** @var File[] Resolved File objects indexed by DB key */
|
||||||
|
private $files = [];
|
||||||
|
|
||||||
|
/** @var int The maximum number of digits in a marker ID */
|
||||||
|
private const MAX_ID_SIZE = 7;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param LinkRenderer $linkRenderer
|
||||||
|
* @param LinkBatchFactory $linkBatchFactory
|
||||||
|
* @param LinkCache $linkCache
|
||||||
|
* @param RepoGroup $repoGroup
|
||||||
|
* @param Language $userLang
|
||||||
|
* @param Language $contLang
|
||||||
|
* @param TitleParser $titleParser
|
||||||
|
* @param NamespaceInfo $namespaceInfo
|
||||||
|
* @param HookContainer $hookContainer
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
LinkRenderer $linkRenderer,
|
||||||
|
LinkBatchFactory $linkBatchFactory,
|
||||||
|
LinkCache $linkCache,
|
||||||
|
RepoGroup $repoGroup,
|
||||||
|
Language $userLang,
|
||||||
|
Language $contLang,
|
||||||
|
TitleParser $titleParser,
|
||||||
|
NamespaceInfo $namespaceInfo,
|
||||||
|
HookContainer $hookContainer
|
||||||
|
) {
|
||||||
|
$this->linkRenderer = $linkRenderer;
|
||||||
|
$this->linkBatchFactory = $linkBatchFactory;
|
||||||
|
$this->linkCache = $linkCache;
|
||||||
|
$this->repoGroup = $repoGroup;
|
||||||
|
$this->userLang = $userLang;
|
||||||
|
$this->contLang = $contLang;
|
||||||
|
$this->titleParser = $titleParser;
|
||||||
|
$this->namespaceInfo = $namespaceInfo;
|
||||||
|
$this->hookRunner = new HookRunner( $hookContainer );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a comment to HTML, but replace links with markers which are
|
||||||
|
* resolved later.
|
||||||
|
*
|
||||||
|
* @param string $comment
|
||||||
|
* @param LinkTarget|null $selfLinkTarget
|
||||||
|
* @param bool $samePage
|
||||||
|
* @param string|false|null $wikiId
|
||||||
|
* @param bool $enableSectionLinks
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function preprocess( string $comment, LinkTarget $selfLinkTarget = null,
|
||||||
|
$samePage = false, $wikiId = false, $enableSectionLinks = true
|
||||||
|
) {
|
||||||
|
return $this->preprocessInternal( $comment, false, $selfLinkTarget,
|
||||||
|
$samePage, $wikiId, $enableSectionLinks );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a comment in pseudo-HTML format to HTML, replacing links with markers.
|
||||||
|
*
|
||||||
|
* @param string $comment
|
||||||
|
* @param LinkTarget|null $selfLinkTarget
|
||||||
|
* @param bool $samePage
|
||||||
|
* @param string|false|null $wikiId
|
||||||
|
* @param bool $enableSectionLinks
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function preprocessUnsafe( $comment, LinkTarget $selfLinkTarget = null,
|
||||||
|
$samePage = false, $wikiId = false, $enableSectionLinks = true
|
||||||
|
) {
|
||||||
|
return $this->preprocessInternal( $comment, true, $selfLinkTarget,
|
||||||
|
$samePage, $wikiId, $enableSectionLinks );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute pending batch queries and replace markers in the specified
|
||||||
|
* string(s) with actual links.
|
||||||
|
*
|
||||||
|
* @param string|string[] $comments
|
||||||
|
* @return string|string[]
|
||||||
|
*/
|
||||||
|
public function finalize( $comments ) {
|
||||||
|
$this->flushLinkBatches();
|
||||||
|
return preg_replace_callback(
|
||||||
|
'/\x1b([0-9]{' . self::MAX_ID_SIZE . '})/',
|
||||||
|
function ( $m ) {
|
||||||
|
$callback = $this->links[(int)$m[1]] ?? null;
|
||||||
|
if ( $callback ) {
|
||||||
|
return $callback();
|
||||||
|
} else {
|
||||||
|
return '<!-- MISSING -->';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
$comments
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $comment
|
||||||
|
* @param bool $unsafe
|
||||||
|
* @param LinkTarget|null $selfLinkTarget
|
||||||
|
* @param bool $samePage
|
||||||
|
* @param string|false|null $wikiId
|
||||||
|
* @param bool $enableSectionLinks
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private function preprocessInternal( $comment, $unsafe, $selfLinkTarget, $samePage, $wikiId,
|
||||||
|
$enableSectionLinks
|
||||||
|
) {
|
||||||
|
// Sanitize text a bit
|
||||||
|
// \x1b needs to be stripped because it is used for link markers
|
||||||
|
$comment = strtr( $comment, "\n\x1b", " " );
|
||||||
|
// Allow HTML entities (for T15815)
|
||||||
|
if ( !$unsafe ) {
|
||||||
|
$comment = \Sanitizer::escapeHtmlAllowEntities( $comment );
|
||||||
|
}
|
||||||
|
if ( $enableSectionLinks ) {
|
||||||
|
$comment = $this->doSectionLinks( $comment, $selfLinkTarget, $samePage, $wikiId );
|
||||||
|
}
|
||||||
|
return $this->doWikiLinks( $comment, $selfLinkTarget, $samePage, $wikiId );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts C-style comments in edit summaries into section links.
|
||||||
|
*
|
||||||
|
* Too many things are called "comments", so these are mostly now called
|
||||||
|
* section links rather than autocomments.
|
||||||
|
*
|
||||||
|
* We look for all comments, match any text before and after the comment,
|
||||||
|
* add a separator where needed and format the comment itself with CSS.
|
||||||
|
*
|
||||||
|
* @param string $comment Comment text
|
||||||
|
* @param LinkTarget|null $selfLinkTarget An optional LinkTarget object used to links to sections
|
||||||
|
* @param bool $samePage Whether section links should refer to local page
|
||||||
|
* @param string|false|null $wikiId Id of the wiki to link to (if not the local wiki),
|
||||||
|
* as used by WikiMap.
|
||||||
|
* @return string Preprocessed comment
|
||||||
|
*/
|
||||||
|
private function doSectionLinks(
|
||||||
|
$comment,
|
||||||
|
$selfLinkTarget = null,
|
||||||
|
$samePage = false,
|
||||||
|
$wikiId = false
|
||||||
|
) {
|
||||||
|
// @todo $append here is something of a hack to preserve the status
|
||||||
|
// quo. Someone who knows more about bidi and such should decide
|
||||||
|
// (1) what sane rendering even *is* for an LTR edit summary on an RTL
|
||||||
|
// wiki, both when autocomments exist and when they don't, and
|
||||||
|
// (2) what markup will make that actually happen.
|
||||||
|
$append = '';
|
||||||
|
$comment = preg_replace_callback(
|
||||||
|
// To detect the presence of content before or after the
|
||||||
|
// auto-comment, we use capturing groups inside optional zero-width
|
||||||
|
// assertions. But older versions of PCRE can't directly make
|
||||||
|
// zero-width assertions optional, so wrap them in a non-capturing
|
||||||
|
// group.
|
||||||
|
'!(?:(?<=(.)))?/\*\s*(.*?)\s*\*/(?:(?=(.)))?!',
|
||||||
|
function ( $match ) use ( &$append, $selfLinkTarget, $samePage, $wikiId ) {
|
||||||
|
// Ensure all match positions are defined
|
||||||
|
$match += [ '', '', '', '' ];
|
||||||
|
|
||||||
|
$pre = $match[1] !== '';
|
||||||
|
$auto = $match[2];
|
||||||
|
$post = $match[3] !== '';
|
||||||
|
$comment = null;
|
||||||
|
|
||||||
|
$this->hookRunner->onFormatAutocomments(
|
||||||
|
$comment, $pre, $auto, $post,
|
||||||
|
Title::castFromLinkTarget( $selfLinkTarget ),
|
||||||
|
$samePage,
|
||||||
|
$wikiId );
|
||||||
|
if ( $comment !== null ) {
|
||||||
|
return $comment;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $selfLinkTarget ) {
|
||||||
|
$section = $auto;
|
||||||
|
# Remove links that a user may have manually put in the autosummary
|
||||||
|
# This could be improved by copying as much of Parser::stripSectionName as desired.
|
||||||
|
$section = str_replace( [
|
||||||
|
'[[:',
|
||||||
|
'[[',
|
||||||
|
']]'
|
||||||
|
], '', $section );
|
||||||
|
|
||||||
|
// We don't want any links in the auto text to be linked, but we still
|
||||||
|
// want to show any [[ ]]
|
||||||
|
$sectionText = str_replace( '[[', '[[', $auto );
|
||||||
|
|
||||||
|
$section = substr( Parser::guessSectionNameFromStrippedText( $section ), 1 );
|
||||||
|
if ( $section !== '' ) {
|
||||||
|
if ( $samePage ) {
|
||||||
|
$sectionTitle = new TitleValue( NS_MAIN, '', $section );
|
||||||
|
} else {
|
||||||
|
$sectionTitle = $selfLinkTarget->createFragmentTarget( $section );
|
||||||
|
}
|
||||||
|
$auto = $this->makeSectionLink(
|
||||||
|
$sectionTitle,
|
||||||
|
$this->userLang->getArrow() . $this->userLang->getDirMark() . $sectionText,
|
||||||
|
$wikiId
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( $pre ) {
|
||||||
|
# written summary $presep autocomment (summary /* section */)
|
||||||
|
$pre = wfMessage( 'autocomment-prefix' )->inContentLanguage()->escaped();
|
||||||
|
}
|
||||||
|
if ( $post ) {
|
||||||
|
# autocomment $postsep written summary (/* section */ summary)
|
||||||
|
$auto .= wfMessage( 'colon-separator' )->inContentLanguage()->escaped();
|
||||||
|
}
|
||||||
|
if ( $auto ) {
|
||||||
|
$auto = '<span dir="auto"><span class="autocomment">' . $auto . '</span>';
|
||||||
|
$append .= '</span>';
|
||||||
|
}
|
||||||
|
$comment = $pre . $auto;
|
||||||
|
return $comment;
|
||||||
|
},
|
||||||
|
$comment
|
||||||
|
);
|
||||||
|
return $comment . $append;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make a section link. These don't need to go into the LinkBatch, since
|
||||||
|
* the link class does not depend on whether the link is known.
|
||||||
|
*
|
||||||
|
* @param LinkTarget $target
|
||||||
|
* @param string $text
|
||||||
|
* @param string|false|null $wikiId Id of the wiki to link to (if not the local wiki),
|
||||||
|
* as used by WikiMap.
|
||||||
|
*
|
||||||
|
* @return string HTML link
|
||||||
|
*/
|
||||||
|
private function makeSectionLink(
|
||||||
|
LinkTarget $target, $text, $wikiId
|
||||||
|
) {
|
||||||
|
if ( $wikiId !== null && $wikiId !== false && !$target->isExternal() ) {
|
||||||
|
return Linker::makeExternalLink(
|
||||||
|
\WikiMap::getForeignURL(
|
||||||
|
$wikiId,
|
||||||
|
$target->getNamespace() === 0
|
||||||
|
? $target->getDBkey()
|
||||||
|
: $this->namespaceInfo->getCanonicalName( $target->getNamespace() ) .
|
||||||
|
':' . $target->getDBkey(),
|
||||||
|
$target->getFragment()
|
||||||
|
),
|
||||||
|
$text,
|
||||||
|
/* escape = */ false // Already escaped
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return $this->linkRenderer->makePreloadedLink( $target, new HtmlArmor( $text ), '' );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats wiki links and media links in text; all other wiki formatting
|
||||||
|
* is ignored
|
||||||
|
*
|
||||||
|
* @todo FIXME: Doesn't handle sub-links as in image thumb texts like the main parser
|
||||||
|
*
|
||||||
|
* @param string $comment Text to format links in. WARNING! Since the output of this
|
||||||
|
* function is html, $comment must be sanitized for use as html. You probably want
|
||||||
|
* to pass $comment through Sanitizer::escapeHtmlAllowEntities() before calling
|
||||||
|
* this function.
|
||||||
|
* as used by WikiMap.
|
||||||
|
* @param LinkTarget|null $selfLinkTarget An optional LinkTarget object used to links to sections
|
||||||
|
* @param bool $samePage Whether section links should refer to local page
|
||||||
|
* @param string|false|null $wikiId Id of the wiki to link to (if not the local wiki),
|
||||||
|
* as used by WikiMap.
|
||||||
|
*
|
||||||
|
* @return string HTML
|
||||||
|
*/
|
||||||
|
private function doWikiLinks( $comment, $selfLinkTarget = null, $samePage = false, $wikiId = false ) {
|
||||||
|
return preg_replace_callback(
|
||||||
|
'/
|
||||||
|
\[\[
|
||||||
|
\s*+ # ignore leading whitespace, the *+ quantifier disallows backtracking
|
||||||
|
:? # ignore optional leading colon
|
||||||
|
([^[\]|]+) # 1. link target; page names cannot include [, ] or |
|
||||||
|
(?:\|
|
||||||
|
# 2. link text
|
||||||
|
# Stop matching at ]] without relying on backtracking.
|
||||||
|
((?:]?[^\]])*+)
|
||||||
|
)?
|
||||||
|
\]\]
|
||||||
|
([^[]*) # 3. link trail (the text up until the next link)
|
||||||
|
/x',
|
||||||
|
function ( $match ) use ( $selfLinkTarget, $samePage, $wikiId ) {
|
||||||
|
$medians = '(?:';
|
||||||
|
$medians .= preg_quote(
|
||||||
|
$this->namespaceInfo->getCanonicalName( NS_MEDIA ), '/' );
|
||||||
|
$medians .= '|';
|
||||||
|
$medians .= preg_quote(
|
||||||
|
$this->contLang->getNsText( NS_MEDIA ),
|
||||||
|
'/'
|
||||||
|
) . '):';
|
||||||
|
|
||||||
|
$comment = $match[0];
|
||||||
|
|
||||||
|
// Fix up urlencoded title texts (copied from Parser::replaceInternalLinks)
|
||||||
|
if ( strpos( $match[1], '%' ) !== false ) {
|
||||||
|
$match[1] = strtr(
|
||||||
|
rawurldecode( $match[1] ),
|
||||||
|
[ '<' => '<', '>' => '>' ]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle link renaming [[foo|text]] will show link as "text"
|
||||||
|
if ( $match[2] != "" ) {
|
||||||
|
$text = $match[2];
|
||||||
|
} else {
|
||||||
|
$text = $match[1];
|
||||||
|
}
|
||||||
|
$submatch = [];
|
||||||
|
$linkMarker = null;
|
||||||
|
if ( preg_match( '/^' . $medians . '(.*)$/i', $match[1], $submatch ) ) {
|
||||||
|
// Media link; trail not supported.
|
||||||
|
$linkRegexp = '/\[\[(.*?)\]\]/';
|
||||||
|
$linkTarget = $this->titleParser->makeTitleValueSafe( NS_FILE, $submatch[1] );
|
||||||
|
if ( $linkTarget ) {
|
||||||
|
$linkMarker = $this->addFileLink( $linkTarget, $text );
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Other kind of link
|
||||||
|
// Make sure its target is non-empty
|
||||||
|
if ( isset( $match[1][0] ) && $match[1][0] == ':' ) {
|
||||||
|
$match[1] = substr( $match[1], 1 );
|
||||||
|
}
|
||||||
|
if ( $match[1] !== false && $match[1] !== '' ) {
|
||||||
|
if ( preg_match(
|
||||||
|
$this->contLang->linkTrail(),
|
||||||
|
$match[3],
|
||||||
|
$submatch
|
||||||
|
) ) {
|
||||||
|
$trail = $submatch[1];
|
||||||
|
} else {
|
||||||
|
$trail = "";
|
||||||
|
}
|
||||||
|
$linkRegexp = '/\[\[(.*?)\]\]' . preg_quote( $trail, '/' ) . '/';
|
||||||
|
list( $inside, $trail ) = Linker::splitTrail( $trail );
|
||||||
|
|
||||||
|
$linkText = $text;
|
||||||
|
$linkTarget = Linker::normalizeSubpageLink( $selfLinkTarget, $match[1], $linkText );
|
||||||
|
|
||||||
|
try {
|
||||||
|
$target = $this->titleParser->parseTitle( $linkTarget );
|
||||||
|
|
||||||
|
if ( $target->getText() == '' && !$target->isExternal()
|
||||||
|
&& !$samePage && $selfLinkTarget
|
||||||
|
) {
|
||||||
|
$target = $selfLinkTarget->createFragmentTarget( $target->getFragment() );
|
||||||
|
}
|
||||||
|
|
||||||
|
$linkMarker = $this->addPageLink( $target, $linkText . $inside, $wikiId );
|
||||||
|
$linkMarker .= $trail;
|
||||||
|
} catch ( MalformedTitleException $e ) {
|
||||||
|
// Fall through
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( $linkMarker ) {
|
||||||
|
// If the link is still valid, go ahead and replace it in!
|
||||||
|
$comment = preg_replace(
|
||||||
|
$linkRegexp,
|
||||||
|
$linkMarker,
|
||||||
|
$comment,
|
||||||
|
1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $comment;
|
||||||
|
},
|
||||||
|
$comment
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a deferred link to the list and return its marker.
|
||||||
|
*
|
||||||
|
* @param callable $callback
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private function addLinkMarker( $callback ) {
|
||||||
|
$nextId = count( $this->links );
|
||||||
|
if ( strlen( $nextId ) > self::MAX_ID_SIZE ) {
|
||||||
|
throw new \RuntimeException( 'Too many links in comment batch' );
|
||||||
|
}
|
||||||
|
$this->links[] = $callback;
|
||||||
|
return sprintf( "\x1b%0" . self::MAX_ID_SIZE . 'd', $nextId );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Link to a LinkTarget. Return either HTML or a marker depending on whether
|
||||||
|
* existence checks are deferred.
|
||||||
|
*
|
||||||
|
* @param LinkTarget $target
|
||||||
|
* @param string $text
|
||||||
|
* @param string|false|null $wikiId
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private function addPageLink( LinkTarget $target, $text, $wikiId ) {
|
||||||
|
// Handle external links (not including interwiki links)
|
||||||
|
if ( $wikiId !== null && $wikiId !== false && !$target->isExternal() ) {
|
||||||
|
return Linker::makeExternalLink(
|
||||||
|
\WikiMap::getForeignURL(
|
||||||
|
$wikiId,
|
||||||
|
$target->getNamespace() === 0
|
||||||
|
? $target->getDBkey()
|
||||||
|
: $this->namespaceInfo->getCanonicalName( $target->getNamespace() ) .
|
||||||
|
':' . $target->getDBkey(),
|
||||||
|
$target->getFragment()
|
||||||
|
),
|
||||||
|
$text,
|
||||||
|
/* escape = */ false // Already escaped
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $this->linkCache->getGoodLinkID( $target ) ) {
|
||||||
|
// Already known
|
||||||
|
return $this->linkRenderer->makeKnownLink( $target, new HtmlArmor( $text ) );
|
||||||
|
} elseif ( $this->linkCache->isBadLink( $target ) ) {
|
||||||
|
// Already cached as unknown
|
||||||
|
return $this->linkRenderer->makeBrokenLink( $target, new HtmlArmor( $text ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Defer page link
|
||||||
|
if ( !$this->linkBatch ) {
|
||||||
|
$this->linkBatch = $this->linkBatchFactory->newLinkBatch();
|
||||||
|
}
|
||||||
|
$this->linkBatch->addObj( $target );
|
||||||
|
return $this->addLinkMarker( function () use ( $target, $text ) {
|
||||||
|
return $this->linkRenderer->makeLink( $target, new HtmlArmor( $text ) );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Link to a file, returning a marker.
|
||||||
|
*
|
||||||
|
* @param LinkTarget $target The name of the file.
|
||||||
|
* @param string $html The inner HTML of the link
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private function addFileLink( LinkTarget $target, $html ) {
|
||||||
|
$this->fileBatch[] = [
|
||||||
|
'title' => $target
|
||||||
|
];
|
||||||
|
return $this->addLinkMarker( function () use ( $target, $html ) {
|
||||||
|
return Linker::makeMediaLinkFile(
|
||||||
|
$target,
|
||||||
|
$this->files[$target->getDBkey()] ?? false,
|
||||||
|
$html
|
||||||
|
);
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute any pending link batch or file batch
|
||||||
|
*/
|
||||||
|
private function flushLinkBatches() {
|
||||||
|
if ( $this->linkBatch ) {
|
||||||
|
$this->linkBatch->execute();
|
||||||
|
$this->linkBatch = null;
|
||||||
|
}
|
||||||
|
if ( $this->fileBatch ) {
|
||||||
|
$this->files += $this->repoGroup->findFiles( $this->fileBatch );
|
||||||
|
$this->fileBatch = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
87
includes/CommentFormatter/CommentParserFactory.php
Normal file
87
includes/CommentFormatter/CommentParserFactory.php
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MediaWiki\CommentFormatter;
|
||||||
|
|
||||||
|
use Language;
|
||||||
|
use LinkCache;
|
||||||
|
use MediaWiki\Cache\LinkBatchFactory;
|
||||||
|
use MediaWiki\HookContainer\HookContainer;
|
||||||
|
use MediaWiki\Linker\LinkRenderer;
|
||||||
|
use NamespaceInfo;
|
||||||
|
use RepoGroup;
|
||||||
|
use TitleParser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
class CommentParserFactory {
|
||||||
|
/** @var LinkRenderer */
|
||||||
|
private $linkRenderer;
|
||||||
|
/** @var LinkBatchFactory */
|
||||||
|
private $linkBatchFactory;
|
||||||
|
/** @var LinkCache */
|
||||||
|
private $linkCache;
|
||||||
|
/** @var RepoGroup */
|
||||||
|
private $repoGroup;
|
||||||
|
/** @var Language */
|
||||||
|
private $userLang;
|
||||||
|
/** @var Language */
|
||||||
|
private $contLang;
|
||||||
|
/** @var TitleParser */
|
||||||
|
private $titleParser;
|
||||||
|
/** @var NamespaceInfo */
|
||||||
|
private $namespaceInfo;
|
||||||
|
/** @var HookContainer */
|
||||||
|
private $hookContainer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param LinkRenderer $linkRenderer
|
||||||
|
* @param LinkBatchFactory $linkBatchFactory
|
||||||
|
* @param LinkCache $linkCache
|
||||||
|
* @param RepoGroup $repoGroup
|
||||||
|
* @param Language $userLang
|
||||||
|
* @param Language $contLang
|
||||||
|
* @param TitleParser $titleParser
|
||||||
|
* @param NamespaceInfo $namespaceInfo
|
||||||
|
* @param HookContainer $hookContainer
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
LinkRenderer $linkRenderer,
|
||||||
|
LinkBatchFactory $linkBatchFactory,
|
||||||
|
LinkCache $linkCache,
|
||||||
|
RepoGroup $repoGroup,
|
||||||
|
Language $userLang,
|
||||||
|
Language $contLang,
|
||||||
|
TitleParser $titleParser,
|
||||||
|
NamespaceInfo $namespaceInfo,
|
||||||
|
HookContainer $hookContainer
|
||||||
|
) {
|
||||||
|
$this->linkRenderer = $linkRenderer;
|
||||||
|
$this->linkBatchFactory = $linkBatchFactory;
|
||||||
|
$this->linkCache = $linkCache;
|
||||||
|
$this->repoGroup = $repoGroup;
|
||||||
|
$this->userLang = $userLang;
|
||||||
|
$this->contLang = $contLang;
|
||||||
|
$this->titleParser = $titleParser;
|
||||||
|
$this->namespaceInfo = $namespaceInfo;
|
||||||
|
$this->hookContainer = $hookContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return CommentParser
|
||||||
|
*/
|
||||||
|
public function create() {
|
||||||
|
return new CommentParser(
|
||||||
|
$this->linkRenderer,
|
||||||
|
$this->linkBatchFactory,
|
||||||
|
$this->linkCache,
|
||||||
|
$this->repoGroup,
|
||||||
|
$this->userLang,
|
||||||
|
$this->contLang,
|
||||||
|
$this->titleParser,
|
||||||
|
$this->namespaceInfo,
|
||||||
|
$this->hookContainer
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
133
includes/CommentFormatter/RevisionCommentBatch.php
Normal file
133
includes/CommentFormatter/RevisionCommentBatch.php
Normal file
|
|
@ -0,0 +1,133 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MediaWiki\CommentFormatter;
|
||||||
|
|
||||||
|
use MediaWiki\Permissions\Authority;
|
||||||
|
use MediaWiki\Revision\RevisionRecord;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fluent interface for revision comment batch inputs.
|
||||||
|
*
|
||||||
|
* @since 1.38
|
||||||
|
*/
|
||||||
|
class RevisionCommentBatch {
|
||||||
|
/** @var CommentFormatter */
|
||||||
|
private $formatter;
|
||||||
|
/** @var Authority|null */
|
||||||
|
private $authority;
|
||||||
|
/** @var iterable<RevisionRecord> */
|
||||||
|
private $revisions;
|
||||||
|
/** @var bool */
|
||||||
|
private $samePage = false;
|
||||||
|
/** @var bool */
|
||||||
|
private $isPublic = false;
|
||||||
|
/** @var bool */
|
||||||
|
private $useParentheses = false;
|
||||||
|
/** @var bool */
|
||||||
|
private $indexById = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param CommentFormatter $formatter
|
||||||
|
*/
|
||||||
|
public function __construct( CommentFormatter $formatter ) {
|
||||||
|
$this->formatter = $formatter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the authority to use for permission checks. This must be called
|
||||||
|
* prior to execute().
|
||||||
|
*
|
||||||
|
* @param Authority $authority
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function authority( Authority $authority ) {
|
||||||
|
$this->authority = $authority;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the revisions to extract comments from.
|
||||||
|
*
|
||||||
|
* @param iterable<RevisionRecord> $revisions
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function revisions( $revisions ) {
|
||||||
|
$this->revisions = $revisions;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the same-page option. If this is true, section links and fragment-
|
||||||
|
* only wikilinks are rendered with an href that is a fragment-only URL.
|
||||||
|
* If it is false (the default), such links go to the self link title.
|
||||||
|
*
|
||||||
|
* This is equivalent to $local in the old Linker methods.
|
||||||
|
*
|
||||||
|
* @param bool $samePage
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function samePage( $samePage = true ) {
|
||||||
|
$this->samePage = $samePage;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrap the comment with parentheses. This has no effect if the useBlock
|
||||||
|
* option is not enabled.
|
||||||
|
*
|
||||||
|
* Unlike the legacy Linker::commentBlock(), this option defaults to false
|
||||||
|
* if this method is not called, since that better preserves the fluent
|
||||||
|
* style.
|
||||||
|
*
|
||||||
|
* @param bool $useParentheses
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function useParentheses( $useParentheses = true ) {
|
||||||
|
$this->useParentheses = $useParentheses;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If this is true, show the comment only if all users can see it.
|
||||||
|
*
|
||||||
|
* We'll call it hideIfDeleted() since public is a keyword and isPublic()
|
||||||
|
* has an inappropriate verb.
|
||||||
|
*
|
||||||
|
* @param bool $isPublic
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function hideIfDeleted( $isPublic = true ) {
|
||||||
|
$this->isPublic = $isPublic;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If this is true, the array keys in the return value will be the revision
|
||||||
|
* IDs instead of the keys from the input array.
|
||||||
|
*
|
||||||
|
* @param bool $indexById
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function indexById( $indexById = true ) {
|
||||||
|
$this->indexById = $indexById;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format the comments.
|
||||||
|
*
|
||||||
|
* @return string[] Formatted comments. The array key is either the field
|
||||||
|
* value specified by indexField(), or if that was not called, it is the
|
||||||
|
* key from the array passed to revisions().
|
||||||
|
*/
|
||||||
|
public function execute() {
|
||||||
|
return $this->formatter->formatRevisions(
|
||||||
|
$this->revisions,
|
||||||
|
$this->authority,
|
||||||
|
$this->samePage,
|
||||||
|
$this->isPublic,
|
||||||
|
$this->useParentheses,
|
||||||
|
$this->indexById
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
88
includes/CommentFormatter/RowCommentFormatter.php
Normal file
88
includes/CommentFormatter/RowCommentFormatter.php
Normal file
|
|
@ -0,0 +1,88 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MediaWiki\CommentFormatter;
|
||||||
|
|
||||||
|
use CommentStore;
|
||||||
|
use Traversable;
|
||||||
|
use Wikimedia\Rdbms\IResultWrapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is basically a CommentFormatter with a CommentStore dependency, allowing
|
||||||
|
* it to retrieve comment texts directly from database result wrappers.
|
||||||
|
*
|
||||||
|
* @since 1.38
|
||||||
|
*/
|
||||||
|
class RowCommentFormatter extends CommentFormatter {
|
||||||
|
/** @var CommentStore */
|
||||||
|
private $commentStore;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal Use MediaWikiServices::getRowCommentFormatter()
|
||||||
|
*
|
||||||
|
* @param CommentParserFactory $commentParserFactory
|
||||||
|
* @param CommentStore $commentStore
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
CommentParserFactory $commentParserFactory,
|
||||||
|
CommentStore $commentStore
|
||||||
|
) {
|
||||||
|
parent::__construct( $commentParserFactory );
|
||||||
|
$this->commentStore = $commentStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format DB rows using a fluent interface. Pass the return value of this
|
||||||
|
* function to CommentBatch::comments().
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* $comments = $rowCommentFormatter->createBatch()
|
||||||
|
* ->comments(
|
||||||
|
* $rowCommentFormatter->rows( $rows )
|
||||||
|
* ->commentField( 'img_comment' )
|
||||||
|
* )
|
||||||
|
* ->useBlock( true )
|
||||||
|
* ->execute();
|
||||||
|
*
|
||||||
|
* @param Traversable|array $rows
|
||||||
|
* @return RowCommentIterator
|
||||||
|
*/
|
||||||
|
public function rows( $rows ) {
|
||||||
|
return new RowCommentIterator( $this->commentStore, $rows );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format DB rows using a parametric interface.
|
||||||
|
*
|
||||||
|
* @param iterable<\stdClass>|IResultWrapper $rows
|
||||||
|
* @param string $commentKey The comment key to pass through to CommentStore,
|
||||||
|
* typically a legacy field name.
|
||||||
|
* @param string|null $namespaceField The namespace field for the self-link
|
||||||
|
* target, or null to have no self-link target.
|
||||||
|
* @param string|null $titleField The title field for the self-link target,
|
||||||
|
* or null to have no self-link target.
|
||||||
|
* @param string|null $indexField The field to use for array keys in the
|
||||||
|
* result, or null to use the same keys as in the input $rows
|
||||||
|
* @param bool $useBlock Wrap the output in standard punctuation and
|
||||||
|
* formatting if it's non-empty.
|
||||||
|
* @param bool $useParentheses Wrap the output with parentheses. Has no
|
||||||
|
* effect if $useBlock is false.
|
||||||
|
* @return string[] The formatted comment. The key will be the value of the
|
||||||
|
* index field if an index field was specified, or the key from the
|
||||||
|
* corresponding element of $rows if no index field was specified.
|
||||||
|
*/
|
||||||
|
public function formatRows( $rows, $commentKey, $namespaceField = null, $titleField = null,
|
||||||
|
$indexField = null, $useBlock = false, $useParentheses = true
|
||||||
|
) {
|
||||||
|
return $this->createBatch()
|
||||||
|
->comments(
|
||||||
|
$this->rows( $rows )
|
||||||
|
->commentKey( $commentKey )
|
||||||
|
->namespaceField( $namespaceField )
|
||||||
|
->titleField( $titleField )
|
||||||
|
->indexField( $indexField )
|
||||||
|
)
|
||||||
|
->useBlock( $useBlock )
|
||||||
|
->useParentheses( $useParentheses )
|
||||||
|
->execute();
|
||||||
|
}
|
||||||
|
}
|
||||||
124
includes/CommentFormatter/RowCommentIterator.php
Normal file
124
includes/CommentFormatter/RowCommentIterator.php
Normal file
|
|
@ -0,0 +1,124 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MediaWiki\CommentFormatter;
|
||||||
|
|
||||||
|
use ArrayIterator;
|
||||||
|
use CommentStore;
|
||||||
|
use IteratorIterator;
|
||||||
|
use TitleValue;
|
||||||
|
use Traversable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An adaptor which converts a row iterator into a CommentItem iterator for
|
||||||
|
* batch formatting.
|
||||||
|
*
|
||||||
|
* Fluent-style mutators are provided to configure how comment text is extracted
|
||||||
|
* from rows.
|
||||||
|
*
|
||||||
|
* Using an iterator for this configuration, instead of putting the
|
||||||
|
* options in CommentBatch, allows CommentBatch to be a simple single
|
||||||
|
* class without a CommentStore dependency.
|
||||||
|
*
|
||||||
|
* @since 1.38
|
||||||
|
*/
|
||||||
|
class RowCommentIterator extends IteratorIterator {
|
||||||
|
/** @var CommentStore */
|
||||||
|
private $commentStore;
|
||||||
|
/** @var string|null */
|
||||||
|
private $commentKey;
|
||||||
|
/** @var string|null */
|
||||||
|
private $namespaceField;
|
||||||
|
/** @var string|null */
|
||||||
|
private $titleField;
|
||||||
|
/** @var string|null */
|
||||||
|
private $indexField;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal Use RowCommentFormatter::rows()
|
||||||
|
* @param CommentStore $commentStore
|
||||||
|
* @param Traversable|array $rows
|
||||||
|
*/
|
||||||
|
public function __construct( CommentStore $commentStore, $rows ) {
|
||||||
|
if ( is_array( $rows ) ) {
|
||||||
|
parent::__construct( new ArrayIterator( $rows ) );
|
||||||
|
} else {
|
||||||
|
parent::__construct( $rows );
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->commentStore = $commentStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set what CommentStore calls the key -- typically a legacy field name
|
||||||
|
* which once held a comment. This must be called before attempting
|
||||||
|
* iteration.
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function commentKey( $key ) {
|
||||||
|
$this->commentKey = $key;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the namespace field. If this is not called, the item will not have
|
||||||
|
* a self-link target, although it may be provided by the batch.
|
||||||
|
*
|
||||||
|
* @param string $field
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function namespaceField( $field ) {
|
||||||
|
$this->namespaceField = $field;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the title field. If this is not called, the item will not have
|
||||||
|
* a self-link target, although it may be provided by the batch.
|
||||||
|
*
|
||||||
|
* @param string $field
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function titleField( $field ) {
|
||||||
|
$this->titleField = $field;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the index field. Values from this field will appear as array keys
|
||||||
|
* in the final formatted comment array. If unset, the array will be
|
||||||
|
* numerically indexed.
|
||||||
|
*
|
||||||
|
* @param string $field
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function indexField( $field ) {
|
||||||
|
$this->indexField = $field;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function key() {
|
||||||
|
if ( $this->indexField ) {
|
||||||
|
return parent::current()->{$this->indexField};
|
||||||
|
} else {
|
||||||
|
return parent::key();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function current() {
|
||||||
|
if ( $this->commentKey === null ) {
|
||||||
|
throw new \RuntimeException( __METHOD__ . ': commentKey must be specified' );
|
||||||
|
}
|
||||||
|
$row = parent::current();
|
||||||
|
$comment = $this->commentStore->getComment( $this->commentKey, $row );
|
||||||
|
$item = new CommentItem( $comment->text );
|
||||||
|
if ( $this->namespaceField && $this->titleField ) {
|
||||||
|
$item->selfLinkTarget( new TitleValue(
|
||||||
|
(int)$row->{$this->namespaceField},
|
||||||
|
$row->{$this->titleField}
|
||||||
|
) );
|
||||||
|
}
|
||||||
|
return $item;
|
||||||
|
}
|
||||||
|
}
|
||||||
25
includes/CommentFormatter/StringCommentIterator.php
Normal file
25
includes/CommentFormatter/StringCommentIterator.php
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MediaWiki\CommentFormatter;
|
||||||
|
|
||||||
|
use ArrayIterator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An adaptor which converts an array of strings to an iterator of CommentItem
|
||||||
|
* objects.
|
||||||
|
*
|
||||||
|
* @since 1.38
|
||||||
|
*/
|
||||||
|
class StringCommentIterator extends ArrayIterator {
|
||||||
|
/**
|
||||||
|
* @internal Use CommentBatch::strings()
|
||||||
|
* @param string[] $strings
|
||||||
|
*/
|
||||||
|
public function __construct( $strings ) {
|
||||||
|
parent::__construct( $strings );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function current() {
|
||||||
|
return new CommentItem( parent::current() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -283,20 +283,6 @@ class DummyLinker {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function makeCommentLink(
|
|
||||||
Title $title,
|
|
||||||
$text,
|
|
||||||
$wikiId = null,
|
|
||||||
$options = []
|
|
||||||
) {
|
|
||||||
return Linker::makeCommentLink(
|
|
||||||
$title,
|
|
||||||
$text,
|
|
||||||
$wikiId,
|
|
||||||
$options
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function normalizeSubpageLink( $contextTitle, $target, &$text ) {
|
public function normalizeSubpageLink( $contextTitle, $target, &$text ) {
|
||||||
return Linker::normalizeSubpageLink(
|
return Linker::normalizeSubpageLink(
|
||||||
$contextTitle,
|
$contextTitle,
|
||||||
|
|
|
||||||
|
|
@ -68,9 +68,11 @@ class FeedUtils {
|
||||||
*
|
*
|
||||||
* @param stdClass $row Row from the recentchanges table, including fields as
|
* @param stdClass $row Row from the recentchanges table, including fields as
|
||||||
* appropriate for CommentStore
|
* appropriate for CommentStore
|
||||||
|
* @param string|null $formattedComment rc_comment in HTML format, or null
|
||||||
|
* to format it on demand.
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public static function formatDiff( $row ) {
|
public static function formatDiff( $row, $formattedComment = null ) {
|
||||||
$titleObj = Title::makeTitle( $row->rc_namespace, $row->rc_title );
|
$titleObj = Title::makeTitle( $row->rc_namespace, $row->rc_title );
|
||||||
$timestamp = wfTimestamp( TS_MW, $row->rc_timestamp );
|
$timestamp = wfTimestamp( TS_MW, $row->rc_timestamp );
|
||||||
$actiontext = '';
|
$actiontext = '';
|
||||||
|
|
@ -78,12 +80,16 @@ class FeedUtils {
|
||||||
$rcRow = (array)$row; // newFromRow() only accepts arrays for RC rows
|
$rcRow = (array)$row; // newFromRow() only accepts arrays for RC rows
|
||||||
$actiontext = LogFormatter::newFromRow( $rcRow )->getActionText();
|
$actiontext = LogFormatter::newFromRow( $rcRow )->getActionText();
|
||||||
}
|
}
|
||||||
return self::formatDiffRow( $titleObj,
|
if ( $row->rc_deleted & RevisionRecord::DELETED_COMMENT ) {
|
||||||
|
$formattedComment = wfMessage( 'rev-deleted-comment' )->escaped();
|
||||||
|
} elseif ( $formattedComment === null ) {
|
||||||
|
$formattedComment = Linker::formatComment(
|
||||||
|
CommentStore::getStore()->getComment( 'rc_comment', $row )->text );
|
||||||
|
}
|
||||||
|
return self::formatDiffRow2( $titleObj,
|
||||||
$row->rc_last_oldid, $row->rc_this_oldid,
|
$row->rc_last_oldid, $row->rc_this_oldid,
|
||||||
$timestamp,
|
$timestamp,
|
||||||
$row->rc_deleted & RevisionRecord::DELETED_COMMENT
|
$formattedComment,
|
||||||
? wfMessage( 'rev-deleted-comment' )->escaped()
|
|
||||||
: CommentStore::getStore()->getComment( 'rc_comment', $row )->text,
|
|
||||||
$actiontext
|
$actiontext
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -91,6 +97,8 @@ class FeedUtils {
|
||||||
/**
|
/**
|
||||||
* Really format a diff for the newsfeed
|
* Really format a diff for the newsfeed
|
||||||
*
|
*
|
||||||
|
* @deprecated since 1.38 use formatDiffRow2
|
||||||
|
*
|
||||||
* @param Title $title
|
* @param Title $title
|
||||||
* @param int $oldid Old revision's id
|
* @param int $oldid Old revision's id
|
||||||
* @param int $newid New revision's id
|
* @param int $newid New revision's id
|
||||||
|
|
@ -101,13 +109,34 @@ class FeedUtils {
|
||||||
*/
|
*/
|
||||||
public static function formatDiffRow( $title, $oldid, $newid, $timestamp,
|
public static function formatDiffRow( $title, $oldid, $newid, $timestamp,
|
||||||
$comment, $actiontext = ''
|
$comment, $actiontext = ''
|
||||||
|
) {
|
||||||
|
$formattedComment = MediaWikiServices::getInstance()->getCommentFormatter()
|
||||||
|
->format( $comment );
|
||||||
|
return self::formatDiffRow2( $title, $oldid, $newid, $timestamp,
|
||||||
|
$formattedComment, $actiontext );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Really really format a diff for the newsfeed. Same as formatDiffRow()
|
||||||
|
* except with preformatted comments.
|
||||||
|
*
|
||||||
|
* @param Title $title
|
||||||
|
* @param int $oldid Old revision's id
|
||||||
|
* @param int $newid New revision's id
|
||||||
|
* @param int $timestamp New revision's timestamp
|
||||||
|
* @param string $formattedComment New revision's comment in HTML format
|
||||||
|
* @param string $actiontext Text of the action; in case of log event
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function formatDiffRow2( $title, $oldid, $newid, $timestamp,
|
||||||
|
$formattedComment, $actiontext = ''
|
||||||
) {
|
) {
|
||||||
global $wgFeedDiffCutoff;
|
global $wgFeedDiffCutoff;
|
||||||
|
|
||||||
// log entries
|
// log entries
|
||||||
$unwrappedText = implode(
|
$unwrappedText = implode(
|
||||||
' ',
|
' ',
|
||||||
array_filter( [ $actiontext, Linker::formatComment( $comment ) ] )
|
array_filter( [ $actiontext, $formattedComment ] )
|
||||||
);
|
);
|
||||||
$completeText = Html::rawElement( 'p', [], $unwrappedText ) . "\n";
|
$completeText = Html::rawElement( 'p', [], $unwrappedText ) . "\n";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1357,8 +1357,8 @@ class Linker {
|
||||||
*
|
*
|
||||||
* This method produces HTML that can require CSS styles in mediawiki.interface.helpers.styles.
|
* This method produces HTML that can require CSS styles in mediawiki.interface.helpers.styles.
|
||||||
*
|
*
|
||||||
* @author Erik Moeller <moeller@scireview.de>
|
|
||||||
* @since 1.16.3. $wikiId added in 1.26
|
* @since 1.16.3. $wikiId added in 1.26
|
||||||
|
* @deprecated since 1.38 use CommentFormatter
|
||||||
*
|
*
|
||||||
* @param string $comment
|
* @param string $comment
|
||||||
* @param LinkTarget|null $title LinkTarget object (to generate link to the section in
|
* @param LinkTarget|null $title LinkTarget object (to generate link to the section in
|
||||||
|
|
@ -1372,113 +1372,8 @@ class Linker {
|
||||||
public static function formatComment(
|
public static function formatComment(
|
||||||
$comment, $title = null, $local = false, $wikiId = null
|
$comment, $title = null, $local = false, $wikiId = null
|
||||||
) {
|
) {
|
||||||
# Sanitize text a bit:
|
$formatter = MediaWikiServices::getInstance()->getCommentFormatter();
|
||||||
$comment = str_replace( "\n", " ", $comment );
|
return $formatter->format( $comment, $title, $local, $wikiId );
|
||||||
# Allow HTML entities (for T15815)
|
|
||||||
$comment = Sanitizer::escapeHtmlAllowEntities( $comment );
|
|
||||||
|
|
||||||
# Render autocomments and make links:
|
|
||||||
$comment = self::formatAutocomments( $comment, $title, $local, $wikiId );
|
|
||||||
return self::formatLinksInComment( $comment, $title, $local, $wikiId );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts autogenerated comments in edit summaries into section links.
|
|
||||||
*
|
|
||||||
* The pattern for autogen comments is / * foo * /, which makes for
|
|
||||||
* some nasty regex.
|
|
||||||
* We look for all comments, match any text before and after the comment,
|
|
||||||
* add a separator where needed and format the comment itself with CSS
|
|
||||||
* Called by Linker::formatComment.
|
|
||||||
*
|
|
||||||
* @param string $comment Comment text
|
|
||||||
* @param LinkTarget|null $title An optional LinkTarget object used to links to sections
|
|
||||||
* @param bool $local Whether section links should refer to local page
|
|
||||||
* @param string|null $wikiId Id of the wiki to link to (if not the local wiki),
|
|
||||||
* as used by WikiMap.
|
|
||||||
*
|
|
||||||
* @return string Formatted comment (wikitext)
|
|
||||||
*/
|
|
||||||
private static function formatAutocomments(
|
|
||||||
$comment, $title = null, $local = false, $wikiId = null
|
|
||||||
) {
|
|
||||||
// @todo $append here is something of a hack to preserve the status
|
|
||||||
// quo. Someone who knows more about bidi and such should decide
|
|
||||||
// (1) what sane rendering even *is* for an LTR edit summary on an RTL
|
|
||||||
// wiki, both when autocomments exist and when they don't, and
|
|
||||||
// (2) what markup will make that actually happen.
|
|
||||||
$append = '';
|
|
||||||
$comment = preg_replace_callback(
|
|
||||||
// To detect the presence of content before or after the
|
|
||||||
// auto-comment, we use capturing groups inside optional zero-width
|
|
||||||
// assertions. But older versions of PCRE can't directly make
|
|
||||||
// zero-width assertions optional, so wrap them in a non-capturing
|
|
||||||
// group.
|
|
||||||
'!(?:(?<=(.)))?/\*\s*(.*?)\s*\*/(?:(?=(.)))?!',
|
|
||||||
static function ( $match ) use ( $title, $local, $wikiId, &$append ) {
|
|
||||||
global $wgLang;
|
|
||||||
|
|
||||||
// Ensure all match positions are defined
|
|
||||||
$match += [ '', '', '', '' ];
|
|
||||||
|
|
||||||
$pre = $match[1] !== '';
|
|
||||||
$auto = $match[2];
|
|
||||||
$post = $match[3] !== '';
|
|
||||||
$comment = null;
|
|
||||||
|
|
||||||
Hooks::runner()->onFormatAutocomments(
|
|
||||||
$comment, $pre, $auto, $post, Title::castFromLinkTarget( $title ), $local,
|
|
||||||
$wikiId );
|
|
||||||
|
|
||||||
if ( $comment === null ) {
|
|
||||||
if ( $title ) {
|
|
||||||
$section = $auto;
|
|
||||||
# Remove links that a user may have manually put in the autosummary
|
|
||||||
# This could be improved by copying as much of Parser::stripSectionName as desired.
|
|
||||||
$section = str_replace( [
|
|
||||||
'[[:',
|
|
||||||
'[[',
|
|
||||||
']]'
|
|
||||||
], '', $section );
|
|
||||||
|
|
||||||
// We don't want any links in the auto text to be linked, but we still
|
|
||||||
// want to show any [[ ]]
|
|
||||||
$sectionText = str_replace( '[[', '[[', $auto );
|
|
||||||
|
|
||||||
$section = substr( Parser::guessSectionNameFromStrippedText( $section ), 1 );
|
|
||||||
if ( $section !== '' ) {
|
|
||||||
if ( $local ) {
|
|
||||||
$sectionTitle = new TitleValue( NS_MAIN, '', $section );
|
|
||||||
} else {
|
|
||||||
$sectionTitle = $title->createFragmentTarget( $section );
|
|
||||||
}
|
|
||||||
$auto = Linker::makeCommentLink(
|
|
||||||
$sectionTitle,
|
|
||||||
$wgLang->getArrow() . $wgLang->getDirMark() . $sectionText,
|
|
||||||
$wikiId,
|
|
||||||
'noclasses'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( $pre ) {
|
|
||||||
# written summary $presep autocomment (summary /* section */)
|
|
||||||
$pre = wfMessage( 'autocomment-prefix' )->inContentLanguage()->escaped();
|
|
||||||
}
|
|
||||||
if ( $post ) {
|
|
||||||
# autocomment $postsep written summary (/* section */ summary)
|
|
||||||
$auto .= wfMessage( 'colon-separator' )->inContentLanguage()->escaped();
|
|
||||||
}
|
|
||||||
if ( $auto ) {
|
|
||||||
$auto = '<span dir="auto"><span class="autocomment">' . $auto . '</span>';
|
|
||||||
$append .= '</span>';
|
|
||||||
}
|
|
||||||
$comment = $pre . $auto;
|
|
||||||
}
|
|
||||||
return $comment;
|
|
||||||
},
|
|
||||||
$comment
|
|
||||||
);
|
|
||||||
return $comment . $append;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -1486,7 +1381,7 @@ class Linker {
|
||||||
* is ignored
|
* is ignored
|
||||||
*
|
*
|
||||||
* @since 1.16.3. $wikiId added in 1.26
|
* @since 1.16.3. $wikiId added in 1.26
|
||||||
* @todo FIXME: Doesn't handle sub-links as in image thumb texts like the main parser
|
* @deprecated since 1.38 use CommentFormatter
|
||||||
*
|
*
|
||||||
* @param string $comment Text to format links in. WARNING! Since the output of this
|
* @param string $comment Text to format links in. WARNING! Since the output of this
|
||||||
* function is html, $comment must be sanitized for use as html. You probably want
|
* function is html, $comment must be sanitized for use as html. You probably want
|
||||||
|
|
@ -1503,146 +1398,8 @@ class Linker {
|
||||||
public static function formatLinksInComment(
|
public static function formatLinksInComment(
|
||||||
$comment, $title = null, $local = false, $wikiId = null
|
$comment, $title = null, $local = false, $wikiId = null
|
||||||
) {
|
) {
|
||||||
return preg_replace_callback(
|
$formatter = MediaWikiServices::getInstance()->getCommentFormatter();
|
||||||
'/
|
return $formatter->formatLinksUnsafe( $comment, $title, $local, $wikiId );
|
||||||
\[\[
|
|
||||||
\s*+ # ignore leading whitespace, the *+ quantifier disallows backtracking
|
|
||||||
:? # ignore optional leading colon
|
|
||||||
([^[\]|]+) # 1. link target; page names cannot include [, ] or |
|
|
||||||
(?:\|
|
|
||||||
# 2. link text
|
|
||||||
# Stop matching at ]] without relying on backtracking.
|
|
||||||
((?:]?[^\]])*+)
|
|
||||||
)?
|
|
||||||
\]\]
|
|
||||||
([^[]*) # 3. link trail (the text up until the next link)
|
|
||||||
/x',
|
|
||||||
static function ( $match ) use ( $title, $local, $wikiId ) {
|
|
||||||
$services = MediaWikiServices::getInstance();
|
|
||||||
|
|
||||||
$medians = '(?:';
|
|
||||||
$medians .= preg_quote(
|
|
||||||
$services->getNamespaceInfo()->getCanonicalName( NS_MEDIA ), '/' );
|
|
||||||
$medians .= '|';
|
|
||||||
$medians .= preg_quote(
|
|
||||||
$services->getContentLanguage()->getNsText( NS_MEDIA ),
|
|
||||||
'/'
|
|
||||||
) . '):';
|
|
||||||
|
|
||||||
$comment = $match[0];
|
|
||||||
|
|
||||||
# fix up urlencoded title texts (copied from Parser::replaceInternalLinks)
|
|
||||||
if ( strpos( $match[1], '%' ) !== false ) {
|
|
||||||
$match[1] = strtr(
|
|
||||||
rawurldecode( $match[1] ),
|
|
||||||
[ '<' => '<', '>' => '>' ]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
# Handle link renaming [[foo|text]] will show link as "text"
|
|
||||||
if ( $match[2] != "" ) {
|
|
||||||
$text = $match[2];
|
|
||||||
} else {
|
|
||||||
$text = $match[1];
|
|
||||||
}
|
|
||||||
$submatch = [];
|
|
||||||
$thelink = null;
|
|
||||||
if ( preg_match( '/^' . $medians . '(.*)$/i', $match[1], $submatch ) ) {
|
|
||||||
# Media link; trail not supported.
|
|
||||||
$linkRegexp = '/\[\[(.*?)\]\]/';
|
|
||||||
$title = Title::makeTitleSafe( NS_FILE, $submatch[1] );
|
|
||||||
if ( $title ) {
|
|
||||||
$thelink = Linker::makeMediaLinkObj( $title, $text );
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
# Other kind of link
|
|
||||||
# Make sure its target is non-empty
|
|
||||||
if ( isset( $match[1][0] ) && $match[1][0] == ':' ) {
|
|
||||||
$match[1] = substr( $match[1], 1 );
|
|
||||||
}
|
|
||||||
if ( $match[1] !== false && $match[1] !== '' ) {
|
|
||||||
if ( preg_match(
|
|
||||||
$services->getContentLanguage()->linkTrail(),
|
|
||||||
$match[3],
|
|
||||||
$submatch
|
|
||||||
) ) {
|
|
||||||
$trail = $submatch[1];
|
|
||||||
} else {
|
|
||||||
$trail = "";
|
|
||||||
}
|
|
||||||
$linkRegexp = '/\[\[(.*?)\]\]' . preg_quote( $trail, '/' ) . '/';
|
|
||||||
list( $inside, $trail ) = Linker::splitTrail( $trail );
|
|
||||||
|
|
||||||
$linkText = $text;
|
|
||||||
$linkTarget = Linker::normalizeSubpageLink( $title, $match[1], $linkText );
|
|
||||||
|
|
||||||
try {
|
|
||||||
$target = $services->getTitleParser()->
|
|
||||||
parseTitle( $linkTarget );
|
|
||||||
|
|
||||||
if ( $target->getText() == '' && !$target->isExternal()
|
|
||||||
&& !$local && $title
|
|
||||||
) {
|
|
||||||
$target = $title->createFragmentTarget( $target->getFragment() );
|
|
||||||
}
|
|
||||||
|
|
||||||
$thelink = Linker::makeCommentLink( $target, $linkText . $inside, $wikiId ) . $trail;
|
|
||||||
} catch ( MalformedTitleException $e ) {
|
|
||||||
// Fall through
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( $thelink ) {
|
|
||||||
// If the link is still valid, go ahead and replace it in!
|
|
||||||
$comment = preg_replace(
|
|
||||||
$linkRegexp,
|
|
||||||
StringUtils::escapeRegexReplacement( $thelink ),
|
|
||||||
$comment,
|
|
||||||
1
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $comment;
|
|
||||||
},
|
|
||||||
$comment
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates a link to the given LinkTarget
|
|
||||||
*
|
|
||||||
* @note This is only public for technical reasons. It's not intended for use outside Linker.
|
|
||||||
*
|
|
||||||
* @param LinkTarget $linkTarget
|
|
||||||
* @param string $text
|
|
||||||
* @param string|null $wikiId Id of the wiki to link to (if not the local wiki),
|
|
||||||
* as used by WikiMap.
|
|
||||||
* @param string|string[] $options See the $options parameter in Linker::link.
|
|
||||||
*
|
|
||||||
* @return string HTML link
|
|
||||||
*/
|
|
||||||
public static function makeCommentLink(
|
|
||||||
LinkTarget $linkTarget, $text, $wikiId = null, $options = []
|
|
||||||
) {
|
|
||||||
if ( $wikiId !== null && !$linkTarget->isExternal() ) {
|
|
||||||
$link = self::makeExternalLink(
|
|
||||||
WikiMap::getForeignURL(
|
|
||||||
$wikiId,
|
|
||||||
$linkTarget->getNamespace() === 0
|
|
||||||
? $linkTarget->getDBkey()
|
|
||||||
: MediaWikiServices::getInstance()->getNamespaceInfo()->
|
|
||||||
getCanonicalName( $linkTarget->getNamespace() ) .
|
|
||||||
':' . $linkTarget->getDBkey(),
|
|
||||||
$linkTarget->getFragment()
|
|
||||||
),
|
|
||||||
$text,
|
|
||||||
/* escape = */ false // Already escaped
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
$link = self::link( $linkTarget, $text, [], [], $options );
|
|
||||||
}
|
|
||||||
|
|
||||||
return $link;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -1736,6 +1493,8 @@ class Linker {
|
||||||
* This method produces HTML that requires CSS styles in mediawiki.interface.helpers.styles.
|
* This method produces HTML that requires CSS styles in mediawiki.interface.helpers.styles.
|
||||||
*
|
*
|
||||||
* @since 1.16.3. $wikiId added in 1.26
|
* @since 1.16.3. $wikiId added in 1.26
|
||||||
|
* @deprecated since 1.38 use CommentFormatter
|
||||||
|
*
|
||||||
* @param string $comment
|
* @param string $comment
|
||||||
* @param LinkTarget|null $title LinkTarget object (to generate link to section in autocomment)
|
* @param LinkTarget|null $title LinkTarget object (to generate link to section in autocomment)
|
||||||
* or null
|
* or null
|
||||||
|
|
@ -1749,20 +1508,8 @@ class Linker {
|
||||||
public static function commentBlock(
|
public static function commentBlock(
|
||||||
$comment, $title = null, $local = false, $wikiId = null, $useParentheses = true
|
$comment, $title = null, $local = false, $wikiId = null, $useParentheses = true
|
||||||
) {
|
) {
|
||||||
// '*' used to be the comment inserted by the software way back
|
return MediaWikiServices::getInstance()->getCommentFormatter()
|
||||||
// in antiquity in case none was provided, here for backwards
|
->formatBlock( $comment, $title, $local, $wikiId, $useParentheses );
|
||||||
// compatibility, acc. to brion -ævar
|
|
||||||
if ( $comment == '' || $comment == '*' ) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
$formatted = self::formatComment( $comment, $title, $local, $wikiId );
|
|
||||||
if ( $useParentheses ) {
|
|
||||||
$formatted = wfMessage( 'parentheses' )->rawParams( $formatted )->escaped();
|
|
||||||
$classNames = 'comment';
|
|
||||||
} else {
|
|
||||||
$classNames = 'comment comment--without-parentheses';
|
|
||||||
}
|
|
||||||
return " <span class=\"$classNames\">$formatted</span>";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -1772,6 +1519,7 @@ class Linker {
|
||||||
* This method produces HTML that requires CSS styles in mediawiki.interface.helpers.styles.
|
* This method produces HTML that requires CSS styles in mediawiki.interface.helpers.styles.
|
||||||
*
|
*
|
||||||
* @since 1.16.3
|
* @since 1.16.3
|
||||||
|
* @deprecated since 1.38 use CommentFormatter
|
||||||
* @param RevisionRecord $revRecord (Switched from the old Revision class to RevisionRecord
|
* @param RevisionRecord $revRecord (Switched from the old Revision class to RevisionRecord
|
||||||
* since 1.35)
|
* since 1.35)
|
||||||
* @param bool $local Whether section links should refer to local page
|
* @param bool $local Whether section links should refer to local page
|
||||||
|
|
@ -1785,31 +1533,9 @@ class Linker {
|
||||||
$isPublic = false,
|
$isPublic = false,
|
||||||
$useParentheses = true
|
$useParentheses = true
|
||||||
) {
|
) {
|
||||||
// TODO inject authority
|
|
||||||
$authority = RequestContext::getMain()->getAuthority();
|
$authority = RequestContext::getMain()->getAuthority();
|
||||||
|
$formatter = MediaWikiServices::getInstance()->getCommentFormatter();
|
||||||
if ( $revRecord->getComment( RevisionRecord::RAW ) === null ) {
|
return $formatter->formatRevision( $revRecord, $authority, $local, $isPublic, $useParentheses );
|
||||||
return "";
|
|
||||||
}
|
|
||||||
if ( $revRecord->isDeleted( RevisionRecord::DELETED_COMMENT ) && $isPublic ) {
|
|
||||||
$block = " <span class=\"comment\">" . wfMessage( 'rev-deleted-comment' )->escaped() . "</span>";
|
|
||||||
} elseif ( $revRecord->userCan( RevisionRecord::DELETED_COMMENT, $authority ) ) {
|
|
||||||
$comment = $revRecord->getComment( RevisionRecord::FOR_THIS_USER, $authority );
|
|
||||||
$block = self::commentBlock(
|
|
||||||
$comment ? $comment->text : null,
|
|
||||||
$revRecord->getPageAsLinkTarget(),
|
|
||||||
$local,
|
|
||||||
null,
|
|
||||||
$useParentheses
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
$block = " <span class=\"comment\">" . wfMessage( 'rev-deleted-comment' )->escaped() . "</span>";
|
|
||||||
}
|
|
||||||
if ( $revRecord->isDeleted( RevisionRecord::DELETED_COMMENT ) ) {
|
|
||||||
$class = self::getRevisionDeletedClass( $revRecord );
|
|
||||||
return " <span class=\"$class comment\">$block</span>";
|
|
||||||
}
|
|
||||||
return $block;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,8 @@ use MediaWiki\Block\UnblockUserFactory;
|
||||||
use MediaWiki\Cache\BacklinkCacheFactory;
|
use MediaWiki\Cache\BacklinkCacheFactory;
|
||||||
use MediaWiki\Cache\LinkBatchFactory;
|
use MediaWiki\Cache\LinkBatchFactory;
|
||||||
use MediaWiki\Collation\CollationFactory;
|
use MediaWiki\Collation\CollationFactory;
|
||||||
|
use MediaWiki\CommentFormatter\CommentFormatter;
|
||||||
|
use MediaWiki\CommentFormatter\RowCommentFormatter;
|
||||||
use MediaWiki\Config\ConfigRepository;
|
use MediaWiki\Config\ConfigRepository;
|
||||||
use MediaWiki\Content\IContentHandlerFactory;
|
use MediaWiki\Content\IContentHandlerFactory;
|
||||||
use MediaWiki\Content\Transform\ContentTransformer;
|
use MediaWiki\Content\Transform\ContentTransformer;
|
||||||
|
|
@ -756,6 +758,14 @@ class MediaWikiServices extends ServiceContainer {
|
||||||
return $this->getService( 'CollationFactory' );
|
return $this->getService( 'CollationFactory' );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return CommentFormatter
|
||||||
|
* @since 1.38
|
||||||
|
*/
|
||||||
|
public function getCommentFormatter(): CommentFormatter {
|
||||||
|
return $this->getService( 'CommentFormatter' );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @since 1.31
|
* @since 1.31
|
||||||
* @return CommentStore
|
* @return CommentStore
|
||||||
|
|
@ -1474,6 +1484,14 @@ class MediaWikiServices extends ServiceContainer {
|
||||||
return $this->getService( 'RollbackPageFactory' );
|
return $this->getService( 'RollbackPageFactory' );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 1.38
|
||||||
|
* @return RowCommentFormatter
|
||||||
|
*/
|
||||||
|
public function getRowCommentFormatter(): RowCommentFormatter {
|
||||||
|
return $this->getService( 'RowCommentFormatter' );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @since 1.27
|
* @since 1.27
|
||||||
* @return SearchEngine
|
* @return SearchEngine
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ use ContribsPager;
|
||||||
use FauxRequest;
|
use FauxRequest;
|
||||||
use IContextSource;
|
use IContextSource;
|
||||||
use MediaWiki\Cache\LinkBatchFactory;
|
use MediaWiki\Cache\LinkBatchFactory;
|
||||||
|
use MediaWiki\CommentFormatter\CommentFormatter;
|
||||||
use MediaWiki\HookContainer\HookContainer;
|
use MediaWiki\HookContainer\HookContainer;
|
||||||
use MediaWiki\Linker\LinkRendererFactory;
|
use MediaWiki\Linker\LinkRendererFactory;
|
||||||
use MediaWiki\Permissions\Authority;
|
use MediaWiki\Permissions\Authority;
|
||||||
|
|
@ -43,6 +44,9 @@ class ContributionsLookup {
|
||||||
/** @var NamespaceInfo */
|
/** @var NamespaceInfo */
|
||||||
private $namespaceInfo;
|
private $namespaceInfo;
|
||||||
|
|
||||||
|
/** @var CommentFormatter */
|
||||||
|
private $commentFormatter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param RevisionStore $revisionStore
|
* @param RevisionStore $revisionStore
|
||||||
* @param LinkRendererFactory $linkRendererFactory
|
* @param LinkRendererFactory $linkRendererFactory
|
||||||
|
|
@ -51,6 +55,7 @@ class ContributionsLookup {
|
||||||
* @param ILoadBalancer $loadBalancer
|
* @param ILoadBalancer $loadBalancer
|
||||||
* @param ActorMigration $actorMigration
|
* @param ActorMigration $actorMigration
|
||||||
* @param NamespaceInfo $namespaceInfo
|
* @param NamespaceInfo $namespaceInfo
|
||||||
|
* @param CommentFormatter $commentFormatter
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
RevisionStore $revisionStore,
|
RevisionStore $revisionStore,
|
||||||
|
|
@ -59,7 +64,8 @@ class ContributionsLookup {
|
||||||
HookContainer $hookContainer,
|
HookContainer $hookContainer,
|
||||||
ILoadBalancer $loadBalancer,
|
ILoadBalancer $loadBalancer,
|
||||||
ActorMigration $actorMigration,
|
ActorMigration $actorMigration,
|
||||||
NamespaceInfo $namespaceInfo
|
NamespaceInfo $namespaceInfo,
|
||||||
|
CommentFormatter $commentFormatter
|
||||||
) {
|
) {
|
||||||
$this->revisionStore = $revisionStore;
|
$this->revisionStore = $revisionStore;
|
||||||
$this->linkRendererFactory = $linkRendererFactory;
|
$this->linkRendererFactory = $linkRendererFactory;
|
||||||
|
|
@ -68,6 +74,7 @@ class ContributionsLookup {
|
||||||
$this->loadBalancer = $loadBalancer;
|
$this->loadBalancer = $loadBalancer;
|
||||||
$this->actorMigration = $actorMigration;
|
$this->actorMigration = $actorMigration;
|
||||||
$this->namespaceInfo = $namespaceInfo;
|
$this->namespaceInfo = $namespaceInfo;
|
||||||
|
$this->commentFormatter = $commentFormatter;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -272,7 +279,8 @@ class ContributionsLookup {
|
||||||
$this->actorMigration,
|
$this->actorMigration,
|
||||||
$this->revisionStore,
|
$this->revisionStore,
|
||||||
$this->namespaceInfo,
|
$this->namespaceInfo,
|
||||||
$targetUser
|
$targetUser,
|
||||||
|
$this->commentFormatter
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,9 @@ use MediaWiki\Block\UserBlockCommandFactory;
|
||||||
use MediaWiki\Cache\BacklinkCacheFactory;
|
use MediaWiki\Cache\BacklinkCacheFactory;
|
||||||
use MediaWiki\Cache\LinkBatchFactory;
|
use MediaWiki\Cache\LinkBatchFactory;
|
||||||
use MediaWiki\Collation\CollationFactory;
|
use MediaWiki\Collation\CollationFactory;
|
||||||
|
use MediaWiki\CommentFormatter\CommentFormatter;
|
||||||
|
use MediaWiki\CommentFormatter\CommentParserFactory;
|
||||||
|
use MediaWiki\CommentFormatter\RowCommentFormatter;
|
||||||
use MediaWiki\Config\ConfigRepository;
|
use MediaWiki\Config\ConfigRepository;
|
||||||
use MediaWiki\Config\ServiceOptions;
|
use MediaWiki\Config\ServiceOptions;
|
||||||
use MediaWiki\Content\ContentHandlerFactory;
|
use MediaWiki\Content\ContentHandlerFactory;
|
||||||
|
|
@ -338,6 +341,21 @@ return [
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
'CommentFormatter' => static function ( MediaWikiServices $services ): CommentFormatter {
|
||||||
|
$parserFactory = new CommentParserFactory(
|
||||||
|
$services->getLinkRenderer(),
|
||||||
|
$services->getLinkBatchFactory(),
|
||||||
|
$services->getLinkCache(),
|
||||||
|
$services->getRepoGroup(),
|
||||||
|
RequestContext::getMain()->getLanguage(),
|
||||||
|
$services->getContentLanguage(),
|
||||||
|
$services->getTitleParser(),
|
||||||
|
$services->getNamespaceInfo(),
|
||||||
|
$services->getHookContainer()
|
||||||
|
);
|
||||||
|
return new CommentFormatter( $parserFactory );
|
||||||
|
},
|
||||||
|
|
||||||
'CommentStore' => static function ( MediaWikiServices $services ): CommentStore {
|
'CommentStore' => static function ( MediaWikiServices $services ): CommentStore {
|
||||||
return new CommentStore(
|
return new CommentStore(
|
||||||
$services->getContentLanguage(),
|
$services->getContentLanguage(),
|
||||||
|
|
@ -404,7 +422,8 @@ return [
|
||||||
$services->getHookContainer(),
|
$services->getHookContainer(),
|
||||||
$services->getDBLoadBalancer(),
|
$services->getDBLoadBalancer(),
|
||||||
$services->getActorMigration(),
|
$services->getActorMigration(),
|
||||||
$services->getNamespaceInfo()
|
$services->getNamespaceInfo(),
|
||||||
|
$services->getCommentFormatter()
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -1447,6 +1466,24 @@ return [
|
||||||
return $services->get( '_PageCommandFactory' );
|
return $services->get( '_PageCommandFactory' );
|
||||||
},
|
},
|
||||||
|
|
||||||
|
'RowCommentFormatter' => static function ( MediaWikiServices $services ): RowCommentFormatter {
|
||||||
|
$parserFactory = new CommentParserFactory(
|
||||||
|
$services->getLinkRenderer(),
|
||||||
|
$services->getLinkBatchFactory(),
|
||||||
|
$services->getLinkCache(),
|
||||||
|
$services->getRepoGroup(),
|
||||||
|
RequestContext::getMain()->getLanguage(),
|
||||||
|
$services->getContentLanguage(),
|
||||||
|
$services->getTitleParser(),
|
||||||
|
$services->getNamespaceInfo(),
|
||||||
|
$services->getHookContainer()
|
||||||
|
);
|
||||||
|
return new RowCommentFormatter(
|
||||||
|
$parserFactory,
|
||||||
|
$services->getCommentStore()
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
'SearchEngineConfig' => static function ( MediaWikiServices $services ): SearchEngineConfig {
|
'SearchEngineConfig' => static function ( MediaWikiServices $services ): SearchEngineConfig {
|
||||||
// @todo This should not take a Config object, but it's not so easy to remove because it
|
// @todo This should not take a Config object, but it's not so easy to remove because it
|
||||||
// exposes it in a getter, which is actually used.
|
// exposes it in a getter, which is actually used.
|
||||||
|
|
|
||||||
|
|
@ -174,6 +174,7 @@ class ActionFactory {
|
||||||
'RollbackPageFactory',
|
'RollbackPageFactory',
|
||||||
'UserOptionsLookup',
|
'UserOptionsLookup',
|
||||||
'WatchlistManager',
|
'WatchlistManager',
|
||||||
|
'CommentFormatter'
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
'unwatch' => [
|
'unwatch' => [
|
||||||
|
|
|
||||||
|
|
@ -312,7 +312,8 @@ class HistoryAction extends FormlessAction {
|
||||||
$conds,
|
$conds,
|
||||||
$d,
|
$d,
|
||||||
$services->getLinkBatchFactory(),
|
$services->getLinkBatchFactory(),
|
||||||
$watchlistManager
|
$watchlistManager,
|
||||||
|
$services->getCommentFormatter()
|
||||||
);
|
);
|
||||||
$out->addHTML(
|
$out->addHTML(
|
||||||
$pager->getNavigationBar() .
|
$pager->getNavigationBar() .
|
||||||
|
|
@ -405,11 +406,15 @@ class HistoryAction extends FormlessAction {
|
||||||
|
|
||||||
$items = $this->fetchRevisions( $limit, 0, self::DIR_NEXT );
|
$items = $this->fetchRevisions( $limit, 0, self::DIR_NEXT );
|
||||||
|
|
||||||
|
// Preload comments
|
||||||
|
$formattedComments = MediaWikiServices::getInstance()->getRowCommentFormatter()
|
||||||
|
->formatRows( $items, 'rev_comment' );
|
||||||
|
|
||||||
// Generate feed elements enclosed between header and footer.
|
// Generate feed elements enclosed between header and footer.
|
||||||
$feed->outHeader();
|
$feed->outHeader();
|
||||||
if ( $items->numRows() ) {
|
if ( $items->numRows() ) {
|
||||||
foreach ( $items as $row ) {
|
foreach ( $items as $i => $row ) {
|
||||||
$feed->outItem( $this->feedItem( $row ) );
|
$feed->outItem( $this->feedItem( $row, $formattedComments[$i] ) );
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$feed->outItem( $this->feedEmpty() );
|
$feed->outItem( $this->feedEmpty() );
|
||||||
|
|
@ -434,19 +439,20 @@ class HistoryAction extends FormlessAction {
|
||||||
* includes a diff to the previous revision (if any).
|
* includes a diff to the previous revision (if any).
|
||||||
*
|
*
|
||||||
* @param stdClass|array $row Database row
|
* @param stdClass|array $row Database row
|
||||||
|
* @param string $formattedComment The comment in HTML format
|
||||||
* @return FeedItem
|
* @return FeedItem
|
||||||
*/
|
*/
|
||||||
private function feedItem( $row ) {
|
private function feedItem( $row, $formattedComment ) {
|
||||||
$revisionStore = MediaWikiServices::getInstance()->getRevisionStore();
|
$revisionStore = MediaWikiServices::getInstance()->getRevisionStore();
|
||||||
$rev = $revisionStore->newRevisionFromRow( $row, 0, $this->getTitle() );
|
$rev = $revisionStore->newRevisionFromRow( $row, 0, $this->getTitle() );
|
||||||
$prevRev = $revisionStore->getPreviousRevision( $rev );
|
$prevRev = $revisionStore->getPreviousRevision( $rev );
|
||||||
$revComment = $rev->getComment() === null ? null : $rev->getComment()->text;
|
$revComment = $rev->getComment() === null ? null : $rev->getComment()->text;
|
||||||
$text = FeedUtils::formatDiffRow(
|
$text = FeedUtils::formatDiffRow2(
|
||||||
$this->getTitle(),
|
$this->getTitle(),
|
||||||
$prevRev ? $prevRev->getId() : false,
|
$prevRev ? $prevRev->getId() : false,
|
||||||
$rev->getId(),
|
$rev->getId(),
|
||||||
$rev->getTimestamp(),
|
$rev->getTimestamp(),
|
||||||
$revComment
|
$formattedComment
|
||||||
);
|
);
|
||||||
$revUserText = $rev->getUser() ? $rev->getUser()->getName() : '';
|
$revUserText = $rev->getUser() ? $rev->getUser()->getName() : '';
|
||||||
if ( $revComment == '' ) {
|
if ( $revComment == '' ) {
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@
|
||||||
* @ingroup Actions
|
* @ingroup Actions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use MediaWiki\CommentFormatter\CommentFormatter;
|
||||||
use MediaWiki\Content\IContentHandlerFactory;
|
use MediaWiki\Content\IContentHandlerFactory;
|
||||||
use MediaWiki\Page\RollbackPageFactory;
|
use MediaWiki\Page\RollbackPageFactory;
|
||||||
use MediaWiki\Revision\RevisionRecord;
|
use MediaWiki\Revision\RevisionRecord;
|
||||||
|
|
@ -46,6 +47,9 @@ class RollbackAction extends FormAction {
|
||||||
/** @var WatchlistManager */
|
/** @var WatchlistManager */
|
||||||
private $watchlistManager;
|
private $watchlistManager;
|
||||||
|
|
||||||
|
/** @var CommentFormatter */
|
||||||
|
private $commentFormatter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Page $page
|
* @param Page $page
|
||||||
* @param IContextSource|null $context
|
* @param IContextSource|null $context
|
||||||
|
|
@ -53,6 +57,7 @@ class RollbackAction extends FormAction {
|
||||||
* @param RollbackPageFactory $rollbackPageFactory
|
* @param RollbackPageFactory $rollbackPageFactory
|
||||||
* @param UserOptionsLookup $userOptionsLookup
|
* @param UserOptionsLookup $userOptionsLookup
|
||||||
* @param WatchlistManager $watchlistManager
|
* @param WatchlistManager $watchlistManager
|
||||||
|
* @param CommentFormatter $commentFormatter
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
Page $page,
|
Page $page,
|
||||||
|
|
@ -60,13 +65,15 @@ class RollbackAction extends FormAction {
|
||||||
IContentHandlerFactory $contentHandlerFactory,
|
IContentHandlerFactory $contentHandlerFactory,
|
||||||
RollbackPageFactory $rollbackPageFactory,
|
RollbackPageFactory $rollbackPageFactory,
|
||||||
UserOptionsLookup $userOptionsLookup,
|
UserOptionsLookup $userOptionsLookup,
|
||||||
WatchlistManager $watchlistManager
|
WatchlistManager $watchlistManager,
|
||||||
|
CommentFormatter $commentFormatter
|
||||||
) {
|
) {
|
||||||
parent::__construct( $page, $context );
|
parent::__construct( $page, $context );
|
||||||
$this->contentHandlerFactory = $contentHandlerFactory;
|
$this->contentHandlerFactory = $contentHandlerFactory;
|
||||||
$this->rollbackPageFactory = $rollbackPageFactory;
|
$this->rollbackPageFactory = $rollbackPageFactory;
|
||||||
$this->userOptionsLookup = $userOptionsLookup;
|
$this->userOptionsLookup = $userOptionsLookup;
|
||||||
$this->watchlistManager = $watchlistManager;
|
$this->watchlistManager = $watchlistManager;
|
||||||
|
$this->commentFormatter = $commentFormatter;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getName() {
|
public function getName() {
|
||||||
|
|
@ -188,9 +195,8 @@ class RollbackAction extends FormAction {
|
||||||
$this->getOutput()->addWikiMsg(
|
$this->getOutput()->addWikiMsg(
|
||||||
'editcomment',
|
'editcomment',
|
||||||
Message::rawParam(
|
Message::rawParam(
|
||||||
Linker::formatComment(
|
$this->commentFormatter
|
||||||
$current->getComment()->text
|
->format( $current->getComment()->text )
|
||||||
)
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use MediaWiki\Cache\LinkBatchFactory;
|
use MediaWiki\Cache\LinkBatchFactory;
|
||||||
|
use MediaWiki\CommentFormatter\CommentFormatter;
|
||||||
use MediaWiki\MediaWikiServices;
|
use MediaWiki\MediaWikiServices;
|
||||||
use MediaWiki\Revision\RevisionRecord;
|
use MediaWiki\Revision\RevisionRecord;
|
||||||
use MediaWiki\Revision\RevisionStore;
|
use MediaWiki\Revision\RevisionStore;
|
||||||
|
|
@ -62,6 +63,19 @@ class HistoryPager extends ReverseChronologicalPager {
|
||||||
/** @var LinkBatchFactory */
|
/** @var LinkBatchFactory */
|
||||||
private $linkBatchFactory;
|
private $linkBatchFactory;
|
||||||
|
|
||||||
|
/** @var CommentFormatter */
|
||||||
|
private $commentFormatter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var RevisionRecord[] Revisions, with the key being their result offset
|
||||||
|
*/
|
||||||
|
private $revisions = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string[] Formatted comments, with the key being their result offset as for $revisions
|
||||||
|
*/
|
||||||
|
private $formattedComments = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param HistoryAction $historyPage
|
* @param HistoryAction $historyPage
|
||||||
* @param string $year
|
* @param string $year
|
||||||
|
|
@ -71,6 +85,7 @@ class HistoryPager extends ReverseChronologicalPager {
|
||||||
* @param string $day
|
* @param string $day
|
||||||
* @param LinkBatchFactory|null $linkBatchFactory
|
* @param LinkBatchFactory|null $linkBatchFactory
|
||||||
* @param WatchlistManager|null $watchlistManager
|
* @param WatchlistManager|null $watchlistManager
|
||||||
|
* @param CommentFormatter|null $commentFormatter
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
HistoryAction $historyPage,
|
HistoryAction $historyPage,
|
||||||
|
|
@ -80,7 +95,8 @@ class HistoryPager extends ReverseChronologicalPager {
|
||||||
array $conds = [],
|
array $conds = [],
|
||||||
$day = '',
|
$day = '',
|
||||||
LinkBatchFactory $linkBatchFactory = null,
|
LinkBatchFactory $linkBatchFactory = null,
|
||||||
WatchlistManager $watchlistManager = null
|
WatchlistManager $watchlistManager = null,
|
||||||
|
CommentFormatter $commentFormatter = null
|
||||||
) {
|
) {
|
||||||
parent::__construct( $historyPage->getContext() );
|
parent::__construct( $historyPage->getContext() );
|
||||||
$this->historyPage = $historyPage;
|
$this->historyPage = $historyPage;
|
||||||
|
|
@ -93,6 +109,7 @@ class HistoryPager extends ReverseChronologicalPager {
|
||||||
$this->linkBatchFactory = $linkBatchFactory ?? $services->getLinkBatchFactory();
|
$this->linkBatchFactory = $linkBatchFactory ?? $services->getLinkBatchFactory();
|
||||||
$this->watchlistManager = $watchlistManager
|
$this->watchlistManager = $watchlistManager
|
||||||
?? $services->getWatchlistManager();
|
?? $services->getWatchlistManager();
|
||||||
|
$this->commentFormatter = $commentFormatter ?? $services->getCommentFormatter();
|
||||||
}
|
}
|
||||||
|
|
||||||
// For hook compatibility...
|
// For hook compatibility...
|
||||||
|
|
@ -148,7 +165,7 @@ class HistoryPager extends ReverseChronologicalPager {
|
||||||
*/
|
*/
|
||||||
public function formatRow( $row ) {
|
public function formatRow( $row ) {
|
||||||
if ( $this->lastRow ) {
|
if ( $this->lastRow ) {
|
||||||
$firstInList = $this->counter == 1;
|
$resultOffset = $this->counter - 1;
|
||||||
$this->counter++;
|
$this->counter++;
|
||||||
|
|
||||||
$notifTimestamp = $this->getConfig()->get( 'ShowUpdatedMarker' )
|
$notifTimestamp = $this->getConfig()->get( 'ShowUpdatedMarker' )
|
||||||
|
|
@ -156,7 +173,7 @@ class HistoryPager extends ReverseChronologicalPager {
|
||||||
->getTitleNotificationTimestamp( $this->getUser(), $this->getTitle() )
|
->getTitleNotificationTimestamp( $this->getUser(), $this->getTitle() )
|
||||||
: false;
|
: false;
|
||||||
|
|
||||||
$s = $this->historyLine( $this->lastRow, $row, $notifTimestamp, false, $firstInList );
|
$s = $this->historyLine( $this->lastRow, $row, $notifTimestamp, $resultOffset );
|
||||||
} else {
|
} else {
|
||||||
$s = '';
|
$s = '';
|
||||||
}
|
}
|
||||||
|
|
@ -171,9 +188,9 @@ class HistoryPager extends ReverseChronologicalPager {
|
||||||
}
|
}
|
||||||
|
|
||||||
# Do a link batch query
|
# Do a link batch query
|
||||||
$this->mResult->seek( 0 );
|
|
||||||
$batch = $this->linkBatchFactory->newLinkBatch();
|
$batch = $this->linkBatchFactory->newLinkBatch();
|
||||||
$revIds = [];
|
$revIds = [];
|
||||||
|
$title = $this->getTitle();
|
||||||
foreach ( $this->mResult as $row ) {
|
foreach ( $this->mResult as $row ) {
|
||||||
if ( $row->rev_parent_id ) {
|
if ( $row->rev_parent_id ) {
|
||||||
$revIds[] = $row->rev_parent_id;
|
$revIds[] = $row->rev_parent_id;
|
||||||
|
|
@ -185,9 +202,24 @@ class HistoryPager extends ReverseChronologicalPager {
|
||||||
$batch->add( NS_USER, $row->rev_user_text );
|
$batch->add( NS_USER, $row->rev_user_text );
|
||||||
$batch->add( NS_USER_TALK, $row->rev_user_text );
|
$batch->add( NS_USER_TALK, $row->rev_user_text );
|
||||||
}
|
}
|
||||||
|
$this->revisions[] = $this->revisionStore->newRevisionFromRow(
|
||||||
|
$row,
|
||||||
|
RevisionStore::READ_NORMAL,
|
||||||
|
$title
|
||||||
|
);
|
||||||
}
|
}
|
||||||
$this->parentLens = $this->revisionStore->getRevisionSizes( $revIds );
|
$this->parentLens = $this->revisionStore->getRevisionSizes( $revIds );
|
||||||
$batch->execute();
|
$batch->execute();
|
||||||
|
|
||||||
|
# The keys of $this->formattedComments will be the same as the keys of $this->revisions
|
||||||
|
$this->formattedComments = $this->commentFormatter->createRevisionBatch()
|
||||||
|
->revisions( $this->revisions )
|
||||||
|
->authority( $this->getAuthority() )
|
||||||
|
->samePage( false )
|
||||||
|
->hideIfDeleted( true )
|
||||||
|
->useParentheses( false )
|
||||||
|
->execute();
|
||||||
|
|
||||||
$this->mResult->seek( 0 );
|
$this->mResult->seek( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -278,7 +310,7 @@ class HistoryPager extends ReverseChronologicalPager {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( $this->lastRow ) {
|
if ( $this->lastRow ) {
|
||||||
$firstInList = $this->counter == 1;
|
$resultOffset = $this->counter - 1;
|
||||||
if ( $this->mIsBackwards ) {
|
if ( $this->mIsBackwards ) {
|
||||||
# Next row is unknown, but for UI reasons, probably exists if an offset has been specified
|
# Next row is unknown, but for UI reasons, probably exists if an offset has been specified
|
||||||
if ( $this->mOffset == '' ) {
|
if ( $this->mOffset == '' ) {
|
||||||
|
|
@ -297,7 +329,7 @@ class HistoryPager extends ReverseChronologicalPager {
|
||||||
->getTitleNotificationTimestamp( $this->getUser(), $this->getTitle() )
|
->getTitleNotificationTimestamp( $this->getUser(), $this->getTitle() )
|
||||||
: false;
|
: false;
|
||||||
|
|
||||||
$s = $this->historyLine( $this->lastRow, $next, $notifTimestamp, false, $firstInList );
|
$s = $this->historyLine( $this->lastRow, $next, $notifTimestamp, $resultOffset );
|
||||||
} else {
|
} else {
|
||||||
$s = '';
|
$s = '';
|
||||||
}
|
}
|
||||||
|
|
@ -335,18 +367,12 @@ class HistoryPager extends ReverseChronologicalPager {
|
||||||
* @param mixed $next The database row corresponding to the next line
|
* @param mixed $next The database row corresponding to the next line
|
||||||
* (chronologically previous)
|
* (chronologically previous)
|
||||||
* @param bool|string $notificationtimestamp
|
* @param bool|string $notificationtimestamp
|
||||||
* @param bool $dummy Unused.
|
* @param int $resultOffset The offset into the current result set
|
||||||
* @param bool $firstInList Whether this row corresponds to the first
|
|
||||||
* displayed on this history page.
|
|
||||||
* @return string HTML output for the row
|
* @return string HTML output for the row
|
||||||
*/
|
*/
|
||||||
private function historyLine( $row, $next, $notificationtimestamp = false,
|
private function historyLine( $row, $next, $notificationtimestamp, $resultOffset ) {
|
||||||
$dummy = false, $firstInList = false ) {
|
$firstInList = $resultOffset === 0;
|
||||||
$revRecord = $this->revisionStore->newRevisionFromRow(
|
$revRecord = $this->revisions[$resultOffset];
|
||||||
$row,
|
|
||||||
RevisionStore::READ_NORMAL,
|
|
||||||
$this->getTitle()
|
|
||||||
);
|
|
||||||
|
|
||||||
if ( is_object( $next ) ) {
|
if ( is_object( $next ) ) {
|
||||||
$previousRevRecord = $this->revisionStore->newRevisionFromRow(
|
$previousRevRecord = $this->revisionStore->newRevisionFromRow(
|
||||||
|
|
@ -440,7 +466,7 @@ class HistoryPager extends ReverseChronologicalPager {
|
||||||
}
|
}
|
||||||
|
|
||||||
# Text following the character difference is added just before running hooks
|
# Text following the character difference is added just before running hooks
|
||||||
$s2 = Linker::revComment( $revRecord, false, true, false );
|
$s2 = $this->formattedComments[$resultOffset];
|
||||||
|
|
||||||
if ( $notificationtimestamp && ( $row->rev_timestamp >= $notificationtimestamp ) ) {
|
if ( $notificationtimestamp && ( $row->rev_timestamp >= $notificationtimestamp ) ) {
|
||||||
$s2 .= ' <span class="updatedmarker">' . $this->msg( 'updatedmarker' )->escaped() . '</span>';
|
$s2 .= ' <span class="updatedmarker">' . $this->msg( 'updatedmarker' )->escaped() . '</span>';
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@
|
||||||
* @file
|
* @file
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use MediaWiki\CommentFormatter\CommentFormatter;
|
||||||
use MediaWiki\Content\IContentHandlerFactory;
|
use MediaWiki\Content\IContentHandlerFactory;
|
||||||
use MediaWiki\Content\Transform\ContentTransformer;
|
use MediaWiki\Content\Transform\ContentTransformer;
|
||||||
use MediaWiki\Revision\MutableRevisionRecord;
|
use MediaWiki\Revision\MutableRevisionRecord;
|
||||||
|
|
@ -48,6 +49,9 @@ class ApiComparePages extends ApiBase {
|
||||||
/** @var ContentTransformer */
|
/** @var ContentTransformer */
|
||||||
private $contentTransformer;
|
private $contentTransformer;
|
||||||
|
|
||||||
|
/** @var CommentFormatter */
|
||||||
|
private $commentFormatter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param ApiMain $mainModule
|
* @param ApiMain $mainModule
|
||||||
* @param string $moduleName
|
* @param string $moduleName
|
||||||
|
|
@ -55,6 +59,7 @@ class ApiComparePages extends ApiBase {
|
||||||
* @param SlotRoleRegistry $slotRoleRegistry
|
* @param SlotRoleRegistry $slotRoleRegistry
|
||||||
* @param IContentHandlerFactory $contentHandlerFactory
|
* @param IContentHandlerFactory $contentHandlerFactory
|
||||||
* @param ContentTransformer $contentTransformer
|
* @param ContentTransformer $contentTransformer
|
||||||
|
* @param CommentFormatter $commentFormatter
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
ApiMain $mainModule,
|
ApiMain $mainModule,
|
||||||
|
|
@ -62,13 +67,15 @@ class ApiComparePages extends ApiBase {
|
||||||
RevisionStore $revisionStore,
|
RevisionStore $revisionStore,
|
||||||
SlotRoleRegistry $slotRoleRegistry,
|
SlotRoleRegistry $slotRoleRegistry,
|
||||||
IContentHandlerFactory $contentHandlerFactory,
|
IContentHandlerFactory $contentHandlerFactory,
|
||||||
ContentTransformer $contentTransformer
|
ContentTransformer $contentTransformer,
|
||||||
|
CommentFormatter $commentFormatter
|
||||||
) {
|
) {
|
||||||
parent::__construct( $mainModule, $moduleName );
|
parent::__construct( $mainModule, $moduleName );
|
||||||
$this->revisionStore = $revisionStore;
|
$this->revisionStore = $revisionStore;
|
||||||
$this->slotRoleRegistry = $slotRoleRegistry;
|
$this->slotRoleRegistry = $slotRoleRegistry;
|
||||||
$this->contentHandlerFactory = $contentHandlerFactory;
|
$this->contentHandlerFactory = $contentHandlerFactory;
|
||||||
$this->contentTransformer = $contentTransformer;
|
$this->contentTransformer = $contentTransformer;
|
||||||
|
$this->commentFormatter = $commentFormatter;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function execute() {
|
public function execute() {
|
||||||
|
|
@ -639,7 +646,7 @@ class ApiComparePages extends ApiBase {
|
||||||
if ( isset( $this->props['comment'] ) ) {
|
if ( isset( $this->props['comment'] ) ) {
|
||||||
$vals["{$prefix}comment"] = $comment->text;
|
$vals["{$prefix}comment"] = $comment->text;
|
||||||
}
|
}
|
||||||
$vals["{$prefix}parsedcomment"] = Linker::formatComment(
|
$vals["{$prefix}parsedcomment"] = $this->commentFormatter->format(
|
||||||
$comment->text, $title
|
$comment->text, $title
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@
|
||||||
|
|
||||||
use MediaWiki\Api\ApiHookRunner;
|
use MediaWiki\Api\ApiHookRunner;
|
||||||
use MediaWiki\Cache\LinkBatchFactory;
|
use MediaWiki\Cache\LinkBatchFactory;
|
||||||
|
use MediaWiki\CommentFormatter\CommentFormatter;
|
||||||
use MediaWiki\HookContainer\HookContainer;
|
use MediaWiki\HookContainer\HookContainer;
|
||||||
use MediaWiki\Linker\LinkRenderer;
|
use MediaWiki\Linker\LinkRenderer;
|
||||||
use MediaWiki\ParamValidator\TypeDef\UserDef;
|
use MediaWiki\ParamValidator\TypeDef\UserDef;
|
||||||
|
|
@ -64,6 +65,9 @@ class ApiFeedContributions extends ApiBase {
|
||||||
/** @var UserFactory */
|
/** @var UserFactory */
|
||||||
private $userFactory;
|
private $userFactory;
|
||||||
|
|
||||||
|
/** @var CommentFormatter */
|
||||||
|
private $commentFormatter;
|
||||||
|
|
||||||
/** @var ApiHookRunner */
|
/** @var ApiHookRunner */
|
||||||
private $hookRunner;
|
private $hookRunner;
|
||||||
|
|
||||||
|
|
@ -79,6 +83,7 @@ class ApiFeedContributions extends ApiBase {
|
||||||
* @param NamespaceInfo $namespaceInfo
|
* @param NamespaceInfo $namespaceInfo
|
||||||
* @param ActorMigration $actorMigration
|
* @param ActorMigration $actorMigration
|
||||||
* @param UserFactory $userFactory
|
* @param UserFactory $userFactory
|
||||||
|
* @param CommentFormatter $commentFormatter
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
ApiMain $main,
|
ApiMain $main,
|
||||||
|
|
@ -91,7 +96,8 @@ class ApiFeedContributions extends ApiBase {
|
||||||
ILoadBalancer $loadBalancer,
|
ILoadBalancer $loadBalancer,
|
||||||
NamespaceInfo $namespaceInfo,
|
NamespaceInfo $namespaceInfo,
|
||||||
ActorMigration $actorMigration,
|
ActorMigration $actorMigration,
|
||||||
UserFactory $userFactory
|
UserFactory $userFactory,
|
||||||
|
CommentFormatter $commentFormatter
|
||||||
) {
|
) {
|
||||||
parent::__construct( $main, $action );
|
parent::__construct( $main, $action );
|
||||||
$this->revisionStore = $revisionStore;
|
$this->revisionStore = $revisionStore;
|
||||||
|
|
@ -103,6 +109,7 @@ class ApiFeedContributions extends ApiBase {
|
||||||
$this->namespaceInfo = $namespaceInfo;
|
$this->namespaceInfo = $namespaceInfo;
|
||||||
$this->actorMigration = $actorMigration;
|
$this->actorMigration = $actorMigration;
|
||||||
$this->userFactory = $userFactory;
|
$this->userFactory = $userFactory;
|
||||||
|
$this->commentFormatter = $commentFormatter;
|
||||||
|
|
||||||
$this->hookRunner = new ApiHookRunner( $hookContainer );
|
$this->hookRunner = new ApiHookRunner( $hookContainer );
|
||||||
}
|
}
|
||||||
|
|
@ -178,7 +185,8 @@ class ApiFeedContributions extends ApiBase {
|
||||||
$this->actorMigration,
|
$this->actorMigration,
|
||||||
$this->revisionStore,
|
$this->revisionStore,
|
||||||
$this->namespaceInfo,
|
$this->namespaceInfo,
|
||||||
$targetUser
|
$targetUser,
|
||||||
|
$this->commentFormatter
|
||||||
);
|
);
|
||||||
|
|
||||||
$feedLimit = $this->getConfig()->get( 'FeedLimit' );
|
$feedLimit = $this->getConfig()->get( 'FeedLimit' );
|
||||||
|
|
|
||||||
|
|
@ -132,6 +132,7 @@ class ApiMain extends ApiBase {
|
||||||
'Parser',
|
'Parser',
|
||||||
'WikiPageFactory',
|
'WikiPageFactory',
|
||||||
'ContentTransformer',
|
'ContentTransformer',
|
||||||
|
'CommentFormatter',
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
'stashedit' => [
|
'stashedit' => [
|
||||||
|
|
@ -164,6 +165,7 @@ class ApiMain extends ApiBase {
|
||||||
'NamespaceInfo',
|
'NamespaceInfo',
|
||||||
'ActorMigration',
|
'ActorMigration',
|
||||||
'UserFactory',
|
'UserFactory',
|
||||||
|
'CommentFormatter',
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
'feedrecentchanges' => [
|
'feedrecentchanges' => [
|
||||||
|
|
@ -200,6 +202,7 @@ class ApiMain extends ApiBase {
|
||||||
'SlotRoleRegistry',
|
'SlotRoleRegistry',
|
||||||
'ContentHandlerFactory',
|
'ContentHandlerFactory',
|
||||||
'ContentTransformer',
|
'ContentTransformer',
|
||||||
|
'CommentFormatter',
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
'checktoken' => [
|
'checktoken' => [
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use MediaWiki\Cache\LinkBatchFactory;
|
use MediaWiki\Cache\LinkBatchFactory;
|
||||||
|
use MediaWiki\CommentFormatter\CommentFormatter;
|
||||||
use MediaWiki\Content\IContentHandlerFactory;
|
use MediaWiki\Content\IContentHandlerFactory;
|
||||||
use MediaWiki\Content\Transform\ContentTransformer;
|
use MediaWiki\Content\Transform\ContentTransformer;
|
||||||
use MediaWiki\Languages\LanguageNameUtils;
|
use MediaWiki\Languages\LanguageNameUtils;
|
||||||
|
|
@ -73,6 +74,9 @@ class ApiParse extends ApiBase {
|
||||||
/** @var ContentTransformer */
|
/** @var ContentTransformer */
|
||||||
private $contentTransformer;
|
private $contentTransformer;
|
||||||
|
|
||||||
|
/** @var CommentFormatter */
|
||||||
|
private $commentFormatter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param ApiMain $main
|
* @param ApiMain $main
|
||||||
* @param string $action
|
* @param string $action
|
||||||
|
|
@ -85,6 +89,7 @@ class ApiParse extends ApiBase {
|
||||||
* @param Parser $parser
|
* @param Parser $parser
|
||||||
* @param WikiPageFactory $wikiPageFactory
|
* @param WikiPageFactory $wikiPageFactory
|
||||||
* @param ContentTransformer $contentTransformer
|
* @param ContentTransformer $contentTransformer
|
||||||
|
* @param CommentFormatter $commentFormatter
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
ApiMain $main,
|
ApiMain $main,
|
||||||
|
|
@ -97,7 +102,8 @@ class ApiParse extends ApiBase {
|
||||||
IContentHandlerFactory $contentHandlerFactory,
|
IContentHandlerFactory $contentHandlerFactory,
|
||||||
Parser $parser,
|
Parser $parser,
|
||||||
WikiPageFactory $wikiPageFactory,
|
WikiPageFactory $wikiPageFactory,
|
||||||
ContentTransformer $contentTransformer
|
ContentTransformer $contentTransformer,
|
||||||
|
CommentFormatter $commentFormatter
|
||||||
) {
|
) {
|
||||||
parent::__construct( $main, $action );
|
parent::__construct( $main, $action );
|
||||||
$this->revisionLookup = $revisionLookup;
|
$this->revisionLookup = $revisionLookup;
|
||||||
|
|
@ -109,6 +115,7 @@ class ApiParse extends ApiBase {
|
||||||
$this->parser = $parser;
|
$this->parser = $parser;
|
||||||
$this->wikiPageFactory = $wikiPageFactory;
|
$this->wikiPageFactory = $wikiPageFactory;
|
||||||
$this->contentTransformer = $contentTransformer;
|
$this->contentTransformer = $contentTransformer;
|
||||||
|
$this->commentFormatter = $commentFormatter;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getPoolKey(): string {
|
private function getPoolKey(): string {
|
||||||
|
|
@ -792,7 +799,7 @@ class ApiParse extends ApiBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This mimicks the behavior of EditPage in formatting a summary
|
* This mimics the behavior of EditPage in formatting a summary
|
||||||
*
|
*
|
||||||
* @param Title $title of the page being parsed
|
* @param Title $title of the page being parsed
|
||||||
* @param array $params The API parameters of the request
|
* @param array $params The API parameters of the request
|
||||||
|
|
@ -812,7 +819,7 @@ class ApiParse extends ApiBase {
|
||||||
->inContentLanguage()->text();
|
->inContentLanguage()->text();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Linker::formatComment( $summary, $title, $this->section === 'new' );
|
return $this->commentFormatter->format( $summary, $title, $this->section === 'new' );
|
||||||
}
|
}
|
||||||
|
|
||||||
private function formatLangLinks( $links ) {
|
private function formatLangLinks( $links ) {
|
||||||
|
|
|
||||||
|
|
@ -273,6 +273,7 @@ class ApiQuery extends ApiBase {
|
||||||
'class' => ApiQueryDeletedrevs::class,
|
'class' => ApiQueryDeletedrevs::class,
|
||||||
'services' => [
|
'services' => [
|
||||||
'CommentStore',
|
'CommentStore',
|
||||||
|
'RowCommentFormatter',
|
||||||
'RevisionStore',
|
'RevisionStore',
|
||||||
'ChangeTagDefStore',
|
'ChangeTagDefStore',
|
||||||
'LinkBatchFactory',
|
'LinkBatchFactory',
|
||||||
|
|
@ -288,6 +289,7 @@ class ApiQuery extends ApiBase {
|
||||||
'class' => ApiQueryFilearchive::class,
|
'class' => ApiQueryFilearchive::class,
|
||||||
'services' => [
|
'services' => [
|
||||||
'CommentStore',
|
'CommentStore',
|
||||||
|
'CommentFormatter',
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
'imageusage' => [
|
'imageusage' => [
|
||||||
|
|
@ -303,6 +305,7 @@ class ApiQuery extends ApiBase {
|
||||||
'class' => ApiQueryLogEvents::class,
|
'class' => ApiQueryLogEvents::class,
|
||||||
'services' => [
|
'services' => [
|
||||||
'CommentStore',
|
'CommentStore',
|
||||||
|
'RowCommentFormatter',
|
||||||
'ChangeTagDefStore',
|
'ChangeTagDefStore',
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
|
@ -323,6 +326,7 @@ class ApiQuery extends ApiBase {
|
||||||
'class' => ApiQueryProtectedTitles::class,
|
'class' => ApiQueryProtectedTitles::class,
|
||||||
'services' => [
|
'services' => [
|
||||||
'CommentStore',
|
'CommentStore',
|
||||||
|
'RowCommentFormatter'
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
'querypage' => [
|
'querypage' => [
|
||||||
|
|
@ -338,6 +342,7 @@ class ApiQuery extends ApiBase {
|
||||||
'class' => ApiQueryRecentChanges::class,
|
'class' => ApiQueryRecentChanges::class,
|
||||||
'services' => [
|
'services' => [
|
||||||
'CommentStore',
|
'CommentStore',
|
||||||
|
'RowCommentFormatter',
|
||||||
'ChangeTagDefStore',
|
'ChangeTagDefStore',
|
||||||
'SlotRoleStore',
|
'SlotRoleStore',
|
||||||
'SlotRoleRegistry',
|
'SlotRoleRegistry',
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use MediaWiki\Cache\LinkBatchFactory;
|
use MediaWiki\Cache\LinkBatchFactory;
|
||||||
|
use MediaWiki\CommentFormatter\RowCommentFormatter;
|
||||||
use MediaWiki\ParamValidator\TypeDef\UserDef;
|
use MediaWiki\ParamValidator\TypeDef\UserDef;
|
||||||
use MediaWiki\Revision\RevisionRecord;
|
use MediaWiki\Revision\RevisionRecord;
|
||||||
use MediaWiki\Revision\RevisionStore;
|
use MediaWiki\Revision\RevisionStore;
|
||||||
|
|
@ -41,6 +42,9 @@ class ApiQueryDeletedrevs extends ApiQueryBase {
|
||||||
/** @var CommentStore */
|
/** @var CommentStore */
|
||||||
private $commentStore;
|
private $commentStore;
|
||||||
|
|
||||||
|
/** @var RowCommentFormatter */
|
||||||
|
private $commentFormatter;
|
||||||
|
|
||||||
/** @var RevisionStore */
|
/** @var RevisionStore */
|
||||||
private $revisionStore;
|
private $revisionStore;
|
||||||
|
|
||||||
|
|
@ -54,6 +58,7 @@ class ApiQueryDeletedrevs extends ApiQueryBase {
|
||||||
* @param ApiQuery $query
|
* @param ApiQuery $query
|
||||||
* @param string $moduleName
|
* @param string $moduleName
|
||||||
* @param CommentStore $commentStore
|
* @param CommentStore $commentStore
|
||||||
|
* @param RowCommentFormatter $commentFormatter
|
||||||
* @param RevisionStore $revisionStore
|
* @param RevisionStore $revisionStore
|
||||||
* @param NameTableStore $changeTagDefStore
|
* @param NameTableStore $changeTagDefStore
|
||||||
* @param LinkBatchFactory $linkBatchFactory
|
* @param LinkBatchFactory $linkBatchFactory
|
||||||
|
|
@ -62,12 +67,14 @@ class ApiQueryDeletedrevs extends ApiQueryBase {
|
||||||
ApiQuery $query,
|
ApiQuery $query,
|
||||||
$moduleName,
|
$moduleName,
|
||||||
CommentStore $commentStore,
|
CommentStore $commentStore,
|
||||||
|
RowCommentFormatter $commentFormatter,
|
||||||
RevisionStore $revisionStore,
|
RevisionStore $revisionStore,
|
||||||
NameTableStore $changeTagDefStore,
|
NameTableStore $changeTagDefStore,
|
||||||
LinkBatchFactory $linkBatchFactory
|
LinkBatchFactory $linkBatchFactory
|
||||||
) {
|
) {
|
||||||
parent::__construct( $query, $moduleName, 'dr' );
|
parent::__construct( $query, $moduleName, 'dr' );
|
||||||
$this->commentStore = $commentStore;
|
$this->commentStore = $commentStore;
|
||||||
|
$this->commentFormatter = $commentFormatter;
|
||||||
$this->revisionStore = $revisionStore;
|
$this->revisionStore = $revisionStore;
|
||||||
$this->changeTagDefStore = $changeTagDefStore;
|
$this->changeTagDefStore = $changeTagDefStore;
|
||||||
$this->linkBatchFactory = $linkBatchFactory;
|
$this->linkBatchFactory = $linkBatchFactory;
|
||||||
|
|
@ -295,6 +302,18 @@ class ApiQueryDeletedrevs extends ApiQueryBase {
|
||||||
$this->addWhereRange( 'ar_id', $dir, null, null );
|
$this->addWhereRange( 'ar_id', $dir, null, null );
|
||||||
}
|
}
|
||||||
$res = $this->select( __METHOD__ );
|
$res = $this->select( __METHOD__ );
|
||||||
|
|
||||||
|
$formattedComments = [];
|
||||||
|
if ( $fld_parsedcomment ) {
|
||||||
|
$formattedComments = $this->commentFormatter->formatItems(
|
||||||
|
$this->commentFormatter->rows( $res )
|
||||||
|
->indexField( 'ar_id' )
|
||||||
|
->commentKey( 'ar_comment' )
|
||||||
|
->namespaceField( 'ar_namespace' )
|
||||||
|
->titleField( 'ar_title' )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
$pageMap = []; // Maps ns&title to (fake) pageid
|
$pageMap = []; // Maps ns&title to (fake) pageid
|
||||||
$count = 0;
|
$count = 0;
|
||||||
$newPageID = 0;
|
$newPageID = 0;
|
||||||
|
|
@ -355,8 +374,7 @@ class ApiQueryDeletedrevs extends ApiQueryBase {
|
||||||
$rev['comment'] = $comment;
|
$rev['comment'] = $comment;
|
||||||
}
|
}
|
||||||
if ( $fld_parsedcomment ) {
|
if ( $fld_parsedcomment ) {
|
||||||
$title = Title::makeTitle( $row->ar_namespace, $row->ar_title );
|
$rev['parsedcomment'] = $formattedComments[$row->ar_id];
|
||||||
$rev['parsedcomment'] = Linker::formatComment( $comment, $title );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,8 @@
|
||||||
* @file
|
* @file
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use MediaWiki\CommentFormatter\CommentFormatter;
|
||||||
|
use MediaWiki\CommentFormatter\CommentItem;
|
||||||
use MediaWiki\Revision\RevisionRecord;
|
use MediaWiki\Revision\RevisionRecord;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -36,18 +38,24 @@ class ApiQueryFilearchive extends ApiQueryBase {
|
||||||
/** @var CommentStore */
|
/** @var CommentStore */
|
||||||
private $commentStore;
|
private $commentStore;
|
||||||
|
|
||||||
|
/** @var CommentFormatter */
|
||||||
|
private $commentFormatter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param ApiQuery $query
|
* @param ApiQuery $query
|
||||||
* @param string $moduleName
|
* @param string $moduleName
|
||||||
* @param CommentStore $commentStore
|
* @param CommentStore $commentStore
|
||||||
|
* @param CommentFormatter $commentFormatter
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
ApiQuery $query,
|
ApiQuery $query,
|
||||||
$moduleName,
|
$moduleName,
|
||||||
CommentStore $commentStore
|
CommentStore $commentStore,
|
||||||
|
CommentFormatter $commentFormatter
|
||||||
) {
|
) {
|
||||||
parent::__construct( $query, $moduleName, 'fa' );
|
parent::__construct( $query, $moduleName, 'fa' );
|
||||||
$this->commentStore = $commentStore;
|
$this->commentStore = $commentStore;
|
||||||
|
$this->commentFormatter = $commentFormatter;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function execute() {
|
public function execute() {
|
||||||
|
|
@ -63,6 +71,7 @@ class ApiQueryFilearchive extends ApiQueryBase {
|
||||||
$fld_size = isset( $prop['size'] );
|
$fld_size = isset( $prop['size'] );
|
||||||
$fld_dimensions = isset( $prop['dimensions'] );
|
$fld_dimensions = isset( $prop['dimensions'] );
|
||||||
$fld_description = isset( $prop['description'] ) || isset( $prop['parseddescription'] );
|
$fld_description = isset( $prop['description'] ) || isset( $prop['parseddescription'] );
|
||||||
|
$fld_parseddescription = isset( $prop['parseddescription'] );
|
||||||
$fld_mime = isset( $prop['mime'] );
|
$fld_mime = isset( $prop['mime'] );
|
||||||
$fld_mediatype = isset( $prop['mediatype'] );
|
$fld_mediatype = isset( $prop['mediatype'] );
|
||||||
$fld_metadata = isset( $prop['metadata'] );
|
$fld_metadata = isset( $prop['metadata'] );
|
||||||
|
|
@ -151,6 +160,22 @@ class ApiQueryFilearchive extends ApiQueryBase {
|
||||||
|
|
||||||
$res = $this->select( __METHOD__ );
|
$res = $this->select( __METHOD__ );
|
||||||
|
|
||||||
|
// Format descriptions in a batch
|
||||||
|
$formattedDescriptions = [];
|
||||||
|
$descriptions = [];
|
||||||
|
if ( $fld_parseddescription ) {
|
||||||
|
$commentItems = [];
|
||||||
|
foreach ( $res as $row ) {
|
||||||
|
$desc = $this->commentStore->getComment( 'fa_description', $row )->text;
|
||||||
|
$descriptions[$row->fa_id] = $desc;
|
||||||
|
$commentItems[$row->fa_id] = ( new CommentItem( $desc ) )
|
||||||
|
->selfLinkTarget( new TitleValue( NS_FILE, $row->fa_name ) );
|
||||||
|
}
|
||||||
|
$formattedDescriptions = $this->commentFormatter->createBatch()
|
||||||
|
->comments( $commentItems )
|
||||||
|
->execute();
|
||||||
|
}
|
||||||
|
|
||||||
$count = 0;
|
$count = 0;
|
||||||
$result = $this->getResult();
|
$result = $this->getResult();
|
||||||
foreach ( $res as $row ) {
|
foreach ( $res as $row ) {
|
||||||
|
|
@ -174,10 +199,11 @@ class ApiQueryFilearchive extends ApiQueryBase {
|
||||||
if ( $fld_description &&
|
if ( $fld_description &&
|
||||||
RevisionRecord::userCanBitfield( $row->fa_deleted, File::DELETED_COMMENT, $user )
|
RevisionRecord::userCanBitfield( $row->fa_deleted, File::DELETED_COMMENT, $user )
|
||||||
) {
|
) {
|
||||||
$file['description'] = $this->commentStore->getComment( 'fa_description', $row )->text;
|
|
||||||
if ( isset( $prop['parseddescription'] ) ) {
|
if ( isset( $prop['parseddescription'] ) ) {
|
||||||
$file['parseddescription'] = Linker::formatComment(
|
$file['parseddescription'] = $formattedDescriptions[$row->fa_id];
|
||||||
$file['description'], $title );
|
$file['description'] = $descriptions[$row->fa_id];
|
||||||
|
} else {
|
||||||
|
$file['description'] = $this->commentStore->getComment( 'fa_description', $row )->text;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( $fld_user &&
|
if ( $fld_user &&
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,8 @@
|
||||||
* @file
|
* @file
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use MediaWiki\CommentFormatter\CommentFormatter;
|
||||||
|
use MediaWiki\CommentFormatter\RowCommentFormatter;
|
||||||
use MediaWiki\ParamValidator\TypeDef\UserDef;
|
use MediaWiki\ParamValidator\TypeDef\UserDef;
|
||||||
use MediaWiki\Storage\NameTableAccessException;
|
use MediaWiki\Storage\NameTableAccessException;
|
||||||
use MediaWiki\Storage\NameTableStore;
|
use MediaWiki\Storage\NameTableStore;
|
||||||
|
|
@ -34,23 +36,32 @@ class ApiQueryLogEvents extends ApiQueryBase {
|
||||||
/** @var CommentStore */
|
/** @var CommentStore */
|
||||||
private $commentStore;
|
private $commentStore;
|
||||||
|
|
||||||
|
/** @var CommentFormatter */
|
||||||
|
private $commentFormatter;
|
||||||
|
|
||||||
/** @var NameTableStore */
|
/** @var NameTableStore */
|
||||||
private $changeTagDefStore;
|
private $changeTagDefStore;
|
||||||
|
|
||||||
|
/** @var string[]|null */
|
||||||
|
private $formattedComments;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param ApiQuery $query
|
* @param ApiQuery $query
|
||||||
* @param string $moduleName
|
* @param string $moduleName
|
||||||
* @param CommentStore $commentStore
|
* @param CommentStore $commentStore
|
||||||
|
* @param RowCommentFormatter $commentFormatter
|
||||||
* @param NameTableStore $changeTagDefStore
|
* @param NameTableStore $changeTagDefStore
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
ApiQuery $query,
|
ApiQuery $query,
|
||||||
$moduleName,
|
$moduleName,
|
||||||
CommentStore $commentStore,
|
CommentStore $commentStore,
|
||||||
|
RowCommentFormatter $commentFormatter,
|
||||||
NameTableStore $changeTagDefStore
|
NameTableStore $changeTagDefStore
|
||||||
) {
|
) {
|
||||||
parent::__construct( $query, $moduleName, 'le' );
|
parent::__construct( $query, $moduleName, 'le' );
|
||||||
$this->commentStore = $commentStore;
|
$this->commentStore = $commentStore;
|
||||||
|
$this->commentFormatter = $commentFormatter;
|
||||||
$this->changeTagDefStore = $changeTagDefStore;
|
$this->changeTagDefStore = $changeTagDefStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -254,6 +265,15 @@ class ApiQueryLogEvents extends ApiQueryBase {
|
||||||
if ( $this->fld_title ) {
|
if ( $this->fld_title ) {
|
||||||
$this->executeGenderCacheFromResultWrapper( $res, __METHOD__, 'log' );
|
$this->executeGenderCacheFromResultWrapper( $res, __METHOD__, 'log' );
|
||||||
}
|
}
|
||||||
|
if ( $this->fld_parsedcomment ) {
|
||||||
|
$this->formattedComments = $this->commentFormatter->formatItems(
|
||||||
|
$this->commentFormatter->rows( $res )
|
||||||
|
->commentKey( 'log_comment' )
|
||||||
|
->indexField( 'log_id' )
|
||||||
|
->namespaceField( 'log_namespace' )
|
||||||
|
->titleField( 'log_title' )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
$result = $this->getResult();
|
$result = $this->getResult();
|
||||||
foreach ( $res as $row ) {
|
foreach ( $res as $row ) {
|
||||||
|
|
@ -286,7 +306,7 @@ class ApiQueryLogEvents extends ApiQueryBase {
|
||||||
$vals['logid'] = (int)$row->log_id;
|
$vals['logid'] = (int)$row->log_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( $this->fld_title || $this->fld_parsedcomment ) {
|
if ( $this->fld_title ) {
|
||||||
$title = Title::makeTitle( $row->log_namespace, $row->log_title );
|
$title = Title::makeTitle( $row->log_namespace, $row->log_title );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -342,13 +362,13 @@ class ApiQueryLogEvents extends ApiQueryBase {
|
||||||
$anyHidden = true;
|
$anyHidden = true;
|
||||||
}
|
}
|
||||||
if ( LogEventsList::userCan( $row, LogPage::DELETED_COMMENT, $user ) ) {
|
if ( LogEventsList::userCan( $row, LogPage::DELETED_COMMENT, $user ) ) {
|
||||||
$comment = $this->commentStore->getComment( 'log_comment', $row )->text;
|
|
||||||
if ( $this->fld_comment ) {
|
if ( $this->fld_comment ) {
|
||||||
$vals['comment'] = $comment;
|
$vals['comment'] = $this->commentStore->getComment( 'log_comment', $row )->text;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( $this->fld_parsedcomment ) {
|
if ( $this->fld_parsedcomment ) {
|
||||||
$vals['parsedcomment'] = Linker::formatComment( $comment, $title );
|
// @phan-suppress-next-line PhanTypeArraySuspiciousNullable
|
||||||
|
$vals['parsedcomment'] = $this->formattedComments[$row->log_id];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,8 @@
|
||||||
* @file
|
* @file
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use MediaWiki\CommentFormatter\RowCommentFormatter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Query module to enumerate all create-protected pages.
|
* Query module to enumerate all create-protected pages.
|
||||||
*
|
*
|
||||||
|
|
@ -30,18 +32,24 @@ class ApiQueryProtectedTitles extends ApiQueryGeneratorBase {
|
||||||
/** @var CommentStore */
|
/** @var CommentStore */
|
||||||
private $commentStore;
|
private $commentStore;
|
||||||
|
|
||||||
|
/** @var RowCommentFormatter */
|
||||||
|
private $commentFormatter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param ApiQuery $query
|
* @param ApiQuery $query
|
||||||
* @param string $moduleName
|
* @param string $moduleName
|
||||||
* @param CommentStore $commentStore
|
* @param CommentStore $commentStore
|
||||||
|
* @param RowCommentFormatter $commentFormatter
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
ApiQuery $query,
|
ApiQuery $query,
|
||||||
$moduleName,
|
$moduleName,
|
||||||
CommentStore $commentStore
|
CommentStore $commentStore,
|
||||||
|
RowCommentFormatter $commentFormatter
|
||||||
) {
|
) {
|
||||||
parent::__construct( $query, $moduleName, 'pt' );
|
parent::__construct( $query, $moduleName, 'pt' );
|
||||||
$this->commentStore = $commentStore;
|
$this->commentStore = $commentStore;
|
||||||
|
$this->commentFormatter = $commentFormatter;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function execute() {
|
public function execute() {
|
||||||
|
|
@ -112,6 +120,14 @@ class ApiQueryProtectedTitles extends ApiQueryGeneratorBase {
|
||||||
|
|
||||||
if ( $resultPageSet === null ) {
|
if ( $resultPageSet === null ) {
|
||||||
$this->executeGenderCacheFromResultWrapper( $res, __METHOD__, 'pt' );
|
$this->executeGenderCacheFromResultWrapper( $res, __METHOD__, 'pt' );
|
||||||
|
if ( isset( $prop['parsedcomment'] ) ) {
|
||||||
|
$formattedComments = $this->commentFormatter->formatItems(
|
||||||
|
$this->commentFormatter->rows( $res )
|
||||||
|
->commentKey( 'pt_reason' )
|
||||||
|
->namespaceField( 'pt_namespace' )
|
||||||
|
->titleField( 'pt_title' )
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$count = 0;
|
$count = 0;
|
||||||
|
|
@ -119,7 +135,7 @@ class ApiQueryProtectedTitles extends ApiQueryGeneratorBase {
|
||||||
|
|
||||||
$titles = [];
|
$titles = [];
|
||||||
|
|
||||||
foreach ( $res as $row ) {
|
foreach ( $res as $rowOffset => $row ) {
|
||||||
if ( ++$count > $params['limit'] ) {
|
if ( ++$count > $params['limit'] ) {
|
||||||
// We've reached the one extra which shows that there are
|
// We've reached the one extra which shows that there are
|
||||||
// additional pages to be had. Stop here...
|
// additional pages to be had. Stop here...
|
||||||
|
|
@ -150,9 +166,8 @@ class ApiQueryProtectedTitles extends ApiQueryGeneratorBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( isset( $prop['parsedcomment'] ) ) {
|
if ( isset( $prop['parsedcomment'] ) ) {
|
||||||
$vals['parsedcomment'] = Linker::formatComment(
|
// @phan-suppress-next-line PhanTypeArraySuspiciousNullable
|
||||||
$this->commentStore->getComment( 'pt_reason', $row )->text
|
$vals['parsedcomment'] = $formattedComments[$rowOffset];
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( isset( $prop['expiry'] ) ) {
|
if ( isset( $prop['expiry'] ) ) {
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@
|
||||||
* @file
|
* @file
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use MediaWiki\CommentFormatter\RowCommentFormatter;
|
||||||
use MediaWiki\ParamValidator\TypeDef\UserDef;
|
use MediaWiki\ParamValidator\TypeDef\UserDef;
|
||||||
use MediaWiki\Revision\RevisionRecord;
|
use MediaWiki\Revision\RevisionRecord;
|
||||||
use MediaWiki\Revision\SlotRoleRegistry;
|
use MediaWiki\Revision\SlotRoleRegistry;
|
||||||
|
|
@ -37,6 +38,9 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
|
||||||
/** @var CommentStore */
|
/** @var CommentStore */
|
||||||
private $commentStore;
|
private $commentStore;
|
||||||
|
|
||||||
|
/** @var RowCommentFormatter */
|
||||||
|
private $commentFormatter;
|
||||||
|
|
||||||
/** @var NameTableStore */
|
/** @var NameTableStore */
|
||||||
private $changeTagDefStore;
|
private $changeTagDefStore;
|
||||||
|
|
||||||
|
|
@ -46,10 +50,13 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
|
||||||
/** @var SlotRoleRegistry */
|
/** @var SlotRoleRegistry */
|
||||||
private $slotRoleRegistry;
|
private $slotRoleRegistry;
|
||||||
|
|
||||||
|
private $formattedComments = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param ApiQuery $query
|
* @param ApiQuery $query
|
||||||
* @param string $moduleName
|
* @param string $moduleName
|
||||||
* @param CommentStore $commentStore
|
* @param CommentStore $commentStore
|
||||||
|
* @param RowCommentFormatter $commentFormatter
|
||||||
* @param NameTableStore $changeTagDefStore
|
* @param NameTableStore $changeTagDefStore
|
||||||
* @param NameTableStore $slotRoleStore
|
* @param NameTableStore $slotRoleStore
|
||||||
* @param SlotRoleRegistry $slotRoleRegistry
|
* @param SlotRoleRegistry $slotRoleRegistry
|
||||||
|
|
@ -58,12 +65,14 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
|
||||||
ApiQuery $query,
|
ApiQuery $query,
|
||||||
$moduleName,
|
$moduleName,
|
||||||
CommentStore $commentStore,
|
CommentStore $commentStore,
|
||||||
|
RowCommentFormatter $commentFormatter,
|
||||||
NameTableStore $changeTagDefStore,
|
NameTableStore $changeTagDefStore,
|
||||||
NameTableStore $slotRoleStore,
|
NameTableStore $slotRoleStore,
|
||||||
SlotRoleRegistry $slotRoleRegistry
|
SlotRoleRegistry $slotRoleRegistry
|
||||||
) {
|
) {
|
||||||
parent::__construct( $query, $moduleName, 'rc' );
|
parent::__construct( $query, $moduleName, 'rc' );
|
||||||
$this->commentStore = $commentStore;
|
$this->commentStore = $commentStore;
|
||||||
|
$this->commentFormatter = $commentFormatter;
|
||||||
$this->changeTagDefStore = $changeTagDefStore;
|
$this->changeTagDefStore = $changeTagDefStore;
|
||||||
$this->slotRoleStore = $slotRoleStore;
|
$this->slotRoleStore = $slotRoleStore;
|
||||||
$this->slotRoleRegistry = $slotRoleRegistry;
|
$this->slotRoleRegistry = $slotRoleRegistry;
|
||||||
|
|
@ -413,9 +422,19 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
|
||||||
/* Perform the actual query. */
|
/* Perform the actual query. */
|
||||||
$res = $this->select( __METHOD__, [], $hookData );
|
$res = $this->select( __METHOD__, [], $hookData );
|
||||||
|
|
||||||
|
// Do batch queries
|
||||||
if ( $this->fld_title && $resultPageSet === null ) {
|
if ( $this->fld_title && $resultPageSet === null ) {
|
||||||
$this->executeGenderCacheFromResultWrapper( $res, __METHOD__, 'rc' );
|
$this->executeGenderCacheFromResultWrapper( $res, __METHOD__, 'rc' );
|
||||||
}
|
}
|
||||||
|
if ( $this->fld_parsedcomment ) {
|
||||||
|
$this->formattedComments = $this->commentFormatter->formatItems(
|
||||||
|
$this->commentFormatter->rows( $res )
|
||||||
|
->indexField( 'rc_id' )
|
||||||
|
->commentKey( 'rc_comment' )
|
||||||
|
->namespaceField( 'rc_namespace' )
|
||||||
|
->titleField( 'rc_title' )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
$revids = [];
|
$revids = [];
|
||||||
$titles = [];
|
$titles = [];
|
||||||
|
|
@ -560,13 +579,12 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
|
||||||
if ( RevisionRecord::userCanBitfield(
|
if ( RevisionRecord::userCanBitfield(
|
||||||
$row->rc_deleted, RevisionRecord::DELETED_COMMENT, $user
|
$row->rc_deleted, RevisionRecord::DELETED_COMMENT, $user
|
||||||
) ) {
|
) ) {
|
||||||
$comment = $this->commentStore->getComment( 'rc_comment', $row )->text;
|
|
||||||
if ( $this->fld_comment ) {
|
if ( $this->fld_comment ) {
|
||||||
$vals['comment'] = $comment;
|
$vals['comment'] = $this->commentStore->getComment( 'rc_comment', $row )->text;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( $this->fld_parsedcomment ) {
|
if ( $this->fld_parsedcomment ) {
|
||||||
$vals['parsedcomment'] = Linker::formatComment( $comment, $title );
|
$vals['parsedcomment'] = $this->formattedComments[$row->rc_id];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -93,7 +93,15 @@ class ChangesFeed {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$nsInfo = MediaWikiServices::getInstance()->getNamespaceInfo();
|
$services = MediaWikiServices::getInstance();
|
||||||
|
$commentFormatter = $services->getRowCommentFormatter();
|
||||||
|
$formattedComments = $commentFormatter->formatItems(
|
||||||
|
$commentFormatter->rows( $rows )
|
||||||
|
->commentKey( 'rc_comment' )
|
||||||
|
->indexField( 'rc_id' )
|
||||||
|
);
|
||||||
|
|
||||||
|
$nsInfo = $services->getNamespaceInfo();
|
||||||
foreach ( $sorted as $obj ) {
|
foreach ( $sorted as $obj ) {
|
||||||
$title = Title::makeTitle( $obj->rc_namespace, $obj->rc_title );
|
$title = Title::makeTitle( $obj->rc_namespace, $obj->rc_title );
|
||||||
$talkpage = $nsInfo->hasTalkNamespace( $obj->rc_namespace ) && $title->canExist()
|
$talkpage = $nsInfo->hasTalkNamespace( $obj->rc_namespace ) && $title->canExist()
|
||||||
|
|
@ -117,7 +125,7 @@ class ChangesFeed {
|
||||||
|
|
||||||
$items[] = new FeedItem(
|
$items[] = new FeedItem(
|
||||||
$title->getPrefixedText(),
|
$title->getPrefixedText(),
|
||||||
FeedUtils::formatDiff( $obj ),
|
FeedUtils::formatDiff( $obj, $formattedComments[$obj->rc_id] ),
|
||||||
$url,
|
$url,
|
||||||
$obj->rc_timestamp,
|
$obj->rc_timestamp,
|
||||||
( $obj->rc_deleted & RevisionRecord::DELETED_USER )
|
( $obj->rc_deleted & RevisionRecord::DELETED_USER )
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@
|
||||||
* @file
|
* @file
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use MediaWiki\CommentFormatter\RowCommentFormatter;
|
||||||
use MediaWiki\HookContainer\ProtectedHookAccessorTrait;
|
use MediaWiki\HookContainer\ProtectedHookAccessorTrait;
|
||||||
use MediaWiki\Linker\LinkRenderer;
|
use MediaWiki\Linker\LinkRenderer;
|
||||||
use MediaWiki\MediaWikiServices;
|
use MediaWiki\MediaWikiServices;
|
||||||
|
|
@ -56,6 +57,16 @@ class ChangesList extends ContextSource {
|
||||||
*/
|
*/
|
||||||
protected $linkRenderer;
|
protected $linkRenderer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var RowCommentFormatter
|
||||||
|
*/
|
||||||
|
protected $commentFormatter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string[] Comments indexed by rc_id
|
||||||
|
*/
|
||||||
|
protected $formattedComments;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var ChangesListFilterGroup[]
|
* @var ChangesListFilterGroup[]
|
||||||
*/
|
*/
|
||||||
|
|
@ -69,8 +80,11 @@ class ChangesList extends ContextSource {
|
||||||
$this->setContext( $context );
|
$this->setContext( $context );
|
||||||
$this->preCacheMessages();
|
$this->preCacheMessages();
|
||||||
$this->watchMsgCache = new MapCacheLRU( 50 );
|
$this->watchMsgCache = new MapCacheLRU( 50 );
|
||||||
$this->linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
|
|
||||||
$this->filterGroups = $filterGroups;
|
$this->filterGroups = $filterGroups;
|
||||||
|
|
||||||
|
$services = MediaWikiServices::getInstance();
|
||||||
|
$this->linkRenderer = $services->getLinkRenderer();
|
||||||
|
$this->commentFormatter = $services->getRowCommentFormatter();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -308,6 +322,16 @@ class ChangesList extends ContextSource {
|
||||||
*/
|
*/
|
||||||
public function initChangesListRows( $rows ) {
|
public function initChangesListRows( $rows ) {
|
||||||
$this->getHookRunner()->onChangesListInitRows( $this, $rows );
|
$this->getHookRunner()->onChangesListInitRows( $this, $rows );
|
||||||
|
$this->formattedComments = $this->commentFormatter->createBatch()
|
||||||
|
->comments(
|
||||||
|
$this->commentFormatter->rows( $rows )
|
||||||
|
->commentKey( 'rc_comment' )
|
||||||
|
->namespaceField( 'rc_namespace' )
|
||||||
|
->titleField( 'rc_title' )
|
||||||
|
->indexField( 'rc_id' )
|
||||||
|
)
|
||||||
|
->useBlock()
|
||||||
|
->execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -696,14 +720,21 @@ class ChangesList extends ContextSource {
|
||||||
}
|
}
|
||||||
return ' <span class="' . $deletedClass . ' comment">' .
|
return ' <span class="' . $deletedClass . ' comment">' .
|
||||||
$this->msg( 'rev-deleted-comment' )->escaped() . '</span>';
|
$this->msg( 'rev-deleted-comment' )->escaped() . '</span>';
|
||||||
|
} elseif ( isset( $rc->mAttribs['rc_id'] )
|
||||||
|
&& isset( $this->formattedComments[$rc->mAttribs['rc_id']] )
|
||||||
|
) {
|
||||||
|
return $this->formattedComments[$rc->mAttribs['rc_id']];
|
||||||
} else {
|
} else {
|
||||||
return Linker::commentBlock( $rc->mAttribs['rc_comment'], $rc->getTitle(),
|
return $this->commentFormatter->formatBlock(
|
||||||
|
$rc->mAttribs['rc_comment'],
|
||||||
|
$rc->getTitle(),
|
||||||
// Whether section links should refer to local page (using default false)
|
// Whether section links should refer to local page (using default false)
|
||||||
false,
|
false,
|
||||||
// wikid to generate links for (using default null) */
|
// wikid to generate links for (using default null) */
|
||||||
null,
|
null,
|
||||||
// whether parentheses should be rendered as part of the message
|
// whether parentheses should be rendered as part of the message
|
||||||
false );
|
false
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -111,11 +111,13 @@ class ImageHistoryList extends ContextSource {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @internal
|
||||||
* @param bool $iscur
|
* @param bool $iscur
|
||||||
* @param File $file
|
* @param File $file
|
||||||
|
* @param string $formattedComment
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function imageHistoryLine( $iscur, $file ) {
|
public function imageHistoryLine( $iscur, $file, $formattedComment ) {
|
||||||
$user = $this->getUser();
|
$user = $this->getUser();
|
||||||
$lang = $this->getLanguage();
|
$lang = $this->getLanguage();
|
||||||
$linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
|
$linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
|
||||||
|
|
@ -274,7 +276,7 @@ class ImageHistoryList extends ContextSource {
|
||||||
$row .= Html::rawElement(
|
$row .= Html::rawElement(
|
||||||
'td',
|
'td',
|
||||||
[ 'dir' => $contLang->getDir() ],
|
[ 'dir' => $contLang->getDir() ],
|
||||||
Linker::formatComment( $description, $this->title )
|
$formattedComment
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -112,10 +112,12 @@ class ImageHistoryPseudoPager extends ReverseChronologicalPager {
|
||||||
public function getBody() {
|
public function getBody() {
|
||||||
$s = '';
|
$s = '';
|
||||||
$this->doQuery();
|
$this->doQuery();
|
||||||
|
$formattedComments = [];
|
||||||
if ( count( $this->mHist ) ) {
|
if ( count( $this->mHist ) ) {
|
||||||
if ( $this->mImg->isLocal() ) {
|
if ( $this->mImg->isLocal() ) {
|
||||||
// Do a batch existence check for user pages and talkpages
|
// Do a batch existence check for user pages and talkpages. Format comments.
|
||||||
$linkBatch = $this->linkBatchFactory->newLinkBatch();
|
$linkBatch = $this->linkBatchFactory->newLinkBatch();
|
||||||
|
$comments = [];
|
||||||
for ( $i = $this->mRange[0]; $i <= $this->mRange[1]; $i++ ) {
|
for ( $i = $this->mRange[0]; $i <= $this->mRange[1]; $i++ ) {
|
||||||
$file = $this->mHist[$i];
|
$file = $this->mHist[$i];
|
||||||
$uploader = $file->getUploader( File::FOR_THIS_USER, $this->getAuthority() );
|
$uploader = $file->getUploader( File::FOR_THIS_USER, $this->getAuthority() );
|
||||||
|
|
@ -123,8 +125,11 @@ class ImageHistoryPseudoPager extends ReverseChronologicalPager {
|
||||||
$linkBatch->add( NS_USER, $uploader->getName() );
|
$linkBatch->add( NS_USER, $uploader->getName() );
|
||||||
$linkBatch->add( NS_USER_TALK, $uploader->getName() );
|
$linkBatch->add( NS_USER_TALK, $uploader->getName() );
|
||||||
}
|
}
|
||||||
|
$comments[$i] = $file->getDescription( File::FOR_THIS_USER, $this->getUser() );
|
||||||
}
|
}
|
||||||
$linkBatch->execute();
|
$linkBatch->execute();
|
||||||
|
$formattedComments = MediaWikiServices::getInstance()->getCommentFormatter()
|
||||||
|
->formatStrings( $comments, $this->getTitle() );
|
||||||
}
|
}
|
||||||
|
|
||||||
$list = new ImageHistoryList( $this->mImagePage );
|
$list = new ImageHistoryList( $this->mImagePage );
|
||||||
|
|
@ -134,7 +139,7 @@ class ImageHistoryPseudoPager extends ReverseChronologicalPager {
|
||||||
// Skip rows there just for paging links
|
// Skip rows there just for paging links
|
||||||
for ( $i = $this->mRange[0]; $i <= $this->mRange[1]; $i++ ) {
|
for ( $i = $this->mRange[0]; $i <= $this->mRange[1]; $i++ ) {
|
||||||
$file = $this->mHist[$i];
|
$file = $this->mHist[$i];
|
||||||
$s .= $list->imageHistoryLine( !$file->isOld(), $file );
|
$s .= $list->imageHistoryLine( !$file->isOld(), $file, $formattedComments[$i] );
|
||||||
}
|
}
|
||||||
$s .= $list->endImageHistoryList( $navLink );
|
$s .= $list->endImageHistoryList( $navLink );
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -306,6 +306,13 @@ abstract class IndexPager extends ContextSource implements Pager {
|
||||||
return $this->mResult;
|
return $this->mResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return int The current offset into the result. Valid during formatRow().
|
||||||
|
*/
|
||||||
|
public function getResultOffset() {
|
||||||
|
return $this->mResult->key();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the offset from an other source than the request
|
* Set the offset from an other source than the request
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -144,6 +144,7 @@ class SpecialPageFactory {
|
||||||
'DBLoadBalancer',
|
'DBLoadBalancer',
|
||||||
'CommentStore',
|
'CommentStore',
|
||||||
'UserCache',
|
'UserCache',
|
||||||
|
'RowCommentFormatter',
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
'Protectedtitles' => [
|
'Protectedtitles' => [
|
||||||
|
|
@ -380,6 +381,7 @@ class SpecialPageFactory {
|
||||||
'CommentStore',
|
'CommentStore',
|
||||||
'BlockUtils',
|
'BlockUtils',
|
||||||
'BlockActionInfo',
|
'BlockActionInfo',
|
||||||
|
'RowCommentFormatter',
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
'AutoblockList' => [
|
'AutoblockList' => [
|
||||||
|
|
@ -391,6 +393,7 @@ class SpecialPageFactory {
|
||||||
'CommentStore',
|
'CommentStore',
|
||||||
'BlockUtils',
|
'BlockUtils',
|
||||||
'BlockActionInfo',
|
'BlockActionInfo',
|
||||||
|
'RowCommentFormatter',
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
'ChangePassword' => [
|
'ChangePassword' => [
|
||||||
|
|
@ -445,6 +448,7 @@ class SpecialPageFactory {
|
||||||
'UserNameUtils',
|
'UserNameUtils',
|
||||||
'UserNamePrefixSearch',
|
'UserNamePrefixSearch',
|
||||||
'UserOptionsLookup',
|
'UserOptionsLookup',
|
||||||
|
'CommentFormatter',
|
||||||
'UserFactory',
|
'UserFactory',
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ use MediaWiki\Block\BlockActionInfo;
|
||||||
use MediaWiki\Block\BlockRestrictionStore;
|
use MediaWiki\Block\BlockRestrictionStore;
|
||||||
use MediaWiki\Block\BlockUtils;
|
use MediaWiki\Block\BlockUtils;
|
||||||
use MediaWiki\Cache\LinkBatchFactory;
|
use MediaWiki\Cache\LinkBatchFactory;
|
||||||
|
use MediaWiki\CommentFormatter\RowCommentFormatter;
|
||||||
use Wikimedia\Rdbms\ILoadBalancer;
|
use Wikimedia\Rdbms\ILoadBalancer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -53,6 +54,9 @@ class SpecialAutoblockList extends SpecialPage {
|
||||||
/** @var BlockActionInfo */
|
/** @var BlockActionInfo */
|
||||||
private $blockActionInfo;
|
private $blockActionInfo;
|
||||||
|
|
||||||
|
/** @var RowCommentFormatter */
|
||||||
|
private $rowCommentFormatter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param LinkBatchFactory $linkBatchFactory
|
* @param LinkBatchFactory $linkBatchFactory
|
||||||
* @param BlockRestrictionStore $blockRestrictionStore
|
* @param BlockRestrictionStore $blockRestrictionStore
|
||||||
|
|
@ -60,6 +64,7 @@ class SpecialAutoblockList extends SpecialPage {
|
||||||
* @param CommentStore $commentStore
|
* @param CommentStore $commentStore
|
||||||
* @param BlockUtils $blockUtils
|
* @param BlockUtils $blockUtils
|
||||||
* @param BlockActionInfo $blockActionInfo
|
* @param BlockActionInfo $blockActionInfo
|
||||||
|
* @param RowCommentFormatter $rowCommentFormatter
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
LinkBatchFactory $linkBatchFactory,
|
LinkBatchFactory $linkBatchFactory,
|
||||||
|
|
@ -67,7 +72,8 @@ class SpecialAutoblockList extends SpecialPage {
|
||||||
ILoadBalancer $loadBalancer,
|
ILoadBalancer $loadBalancer,
|
||||||
CommentStore $commentStore,
|
CommentStore $commentStore,
|
||||||
BlockUtils $blockUtils,
|
BlockUtils $blockUtils,
|
||||||
BlockActionInfo $blockActionInfo
|
BlockActionInfo $blockActionInfo,
|
||||||
|
RowCommentFormatter $rowCommentFormatter
|
||||||
) {
|
) {
|
||||||
parent::__construct( 'AutoblockList' );
|
parent::__construct( 'AutoblockList' );
|
||||||
|
|
||||||
|
|
@ -77,6 +83,7 @@ class SpecialAutoblockList extends SpecialPage {
|
||||||
$this->commentStore = $commentStore;
|
$this->commentStore = $commentStore;
|
||||||
$this->blockUtils = $blockUtils;
|
$this->blockUtils = $blockUtils;
|
||||||
$this->blockActionInfo = $blockActionInfo;
|
$this->blockActionInfo = $blockActionInfo;
|
||||||
|
$this->rowCommentFormatter = $rowCommentFormatter;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -140,7 +147,8 @@ class SpecialAutoblockList extends SpecialPage {
|
||||||
$this->getSpecialPageFactory(),
|
$this->getSpecialPageFactory(),
|
||||||
$this->commentStore,
|
$this->commentStore,
|
||||||
$this->blockUtils,
|
$this->blockUtils,
|
||||||
$this->blockActionInfo
|
$this->blockActionInfo,
|
||||||
|
$this->rowCommentFormatter
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ use MediaWiki\Block\BlockRestrictionStore;
|
||||||
use MediaWiki\Block\BlockUtils;
|
use MediaWiki\Block\BlockUtils;
|
||||||
use MediaWiki\Block\DatabaseBlock;
|
use MediaWiki\Block\DatabaseBlock;
|
||||||
use MediaWiki\Cache\LinkBatchFactory;
|
use MediaWiki\Cache\LinkBatchFactory;
|
||||||
|
use MediaWiki\CommentFormatter\RowCommentFormatter;
|
||||||
use Wikimedia\IPUtils;
|
use Wikimedia\IPUtils;
|
||||||
use Wikimedia\Rdbms\IDatabase;
|
use Wikimedia\Rdbms\IDatabase;
|
||||||
use Wikimedia\Rdbms\ILoadBalancer;
|
use Wikimedia\Rdbms\ILoadBalancer;
|
||||||
|
|
@ -60,13 +61,17 @@ class SpecialBlockList extends SpecialPage {
|
||||||
/** @var BlockActionInfo */
|
/** @var BlockActionInfo */
|
||||||
private $blockActionInfo;
|
private $blockActionInfo;
|
||||||
|
|
||||||
|
/** @var RowCommentFormatter */
|
||||||
|
private $rowCommentFormatter;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
LinkBatchFactory $linkBatchFactory,
|
LinkBatchFactory $linkBatchFactory,
|
||||||
BlockRestrictionStore $blockRestrictionStore,
|
BlockRestrictionStore $blockRestrictionStore,
|
||||||
ILoadBalancer $loadBalancer,
|
ILoadBalancer $loadBalancer,
|
||||||
CommentStore $commentStore,
|
CommentStore $commentStore,
|
||||||
BlockUtils $blockUtils,
|
BlockUtils $blockUtils,
|
||||||
BlockActionInfo $blockActionInfo
|
BlockActionInfo $blockActionInfo,
|
||||||
|
RowCommentFormatter $rowCommentFormatter
|
||||||
) {
|
) {
|
||||||
parent::__construct( 'BlockList' );
|
parent::__construct( 'BlockList' );
|
||||||
|
|
||||||
|
|
@ -76,6 +81,7 @@ class SpecialBlockList extends SpecialPage {
|
||||||
$this->commentStore = $commentStore;
|
$this->commentStore = $commentStore;
|
||||||
$this->blockUtils = $blockUtils;
|
$this->blockUtils = $blockUtils;
|
||||||
$this->blockActionInfo = $blockActionInfo;
|
$this->blockActionInfo = $blockActionInfo;
|
||||||
|
$this->rowCommentFormatter = $rowCommentFormatter;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -249,7 +255,8 @@ class SpecialBlockList extends SpecialPage {
|
||||||
$this->getSpecialPageFactory(),
|
$this->getSpecialPageFactory(),
|
||||||
$this->commentStore,
|
$this->commentStore,
|
||||||
$this->blockUtils,
|
$this->blockUtils,
|
||||||
$this->blockActionInfo
|
$this->blockActionInfo,
|
||||||
|
$this->rowCommentFormatter
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@
|
||||||
|
|
||||||
use MediaWiki\Block\DatabaseBlock;
|
use MediaWiki\Block\DatabaseBlock;
|
||||||
use MediaWiki\Cache\LinkBatchFactory;
|
use MediaWiki\Cache\LinkBatchFactory;
|
||||||
|
use MediaWiki\CommentFormatter\CommentFormatter;
|
||||||
use MediaWiki\HookContainer\HookRunner;
|
use MediaWiki\HookContainer\HookRunner;
|
||||||
use MediaWiki\MediaWikiServices;
|
use MediaWiki\MediaWikiServices;
|
||||||
use MediaWiki\Permissions\PermissionManager;
|
use MediaWiki\Permissions\PermissionManager;
|
||||||
|
|
@ -69,6 +70,9 @@ class SpecialContributions extends IncludableSpecialPage {
|
||||||
/** @var UserOptionsLookup */
|
/** @var UserOptionsLookup */
|
||||||
private $userOptionsLookup;
|
private $userOptionsLookup;
|
||||||
|
|
||||||
|
/** @var CommentFormatter */
|
||||||
|
private $commentFormatter;
|
||||||
|
|
||||||
/** @var UserFactory */
|
/** @var UserFactory */
|
||||||
private $userFactory;
|
private $userFactory;
|
||||||
|
|
||||||
|
|
@ -85,6 +89,7 @@ class SpecialContributions extends IncludableSpecialPage {
|
||||||
* @param UserNameUtils|null $userNameUtils
|
* @param UserNameUtils|null $userNameUtils
|
||||||
* @param UserNamePrefixSearch|null $userNamePrefixSearch
|
* @param UserNamePrefixSearch|null $userNamePrefixSearch
|
||||||
* @param UserOptionsLookup|null $userOptionsLookup
|
* @param UserOptionsLookup|null $userOptionsLookup
|
||||||
|
* @param CommentFormatter|null $commentFormatter
|
||||||
* @param UserFactory|null $userFactory
|
* @param UserFactory|null $userFactory
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
|
|
@ -97,6 +102,7 @@ class SpecialContributions extends IncludableSpecialPage {
|
||||||
UserNameUtils $userNameUtils = null,
|
UserNameUtils $userNameUtils = null,
|
||||||
UserNamePrefixSearch $userNamePrefixSearch = null,
|
UserNamePrefixSearch $userNamePrefixSearch = null,
|
||||||
UserOptionsLookup $userOptionsLookup = null,
|
UserOptionsLookup $userOptionsLookup = null,
|
||||||
|
CommentFormatter $commentFormatter = null,
|
||||||
UserFactory $userFactory = null
|
UserFactory $userFactory = null
|
||||||
) {
|
) {
|
||||||
parent::__construct( 'Contributions' );
|
parent::__construct( 'Contributions' );
|
||||||
|
|
@ -111,6 +117,7 @@ class SpecialContributions extends IncludableSpecialPage {
|
||||||
$this->userNameUtils = $userNameUtils ?? $services->getUserNameUtils();
|
$this->userNameUtils = $userNameUtils ?? $services->getUserNameUtils();
|
||||||
$this->userNamePrefixSearch = $userNamePrefixSearch ?? $services->getUserNamePrefixSearch();
|
$this->userNamePrefixSearch = $userNamePrefixSearch ?? $services->getUserNamePrefixSearch();
|
||||||
$this->userOptionsLookup = $userOptionsLookup ?? $services->getUserOptionsLookup();
|
$this->userOptionsLookup = $userOptionsLookup ?? $services->getUserOptionsLookup();
|
||||||
|
$this->commentFormatter = $commentFormatter ?? $services->getCommentFormatter();
|
||||||
$this->userFactory = $userFactory ?? $services->getUserFactory();
|
$this->userFactory = $userFactory ?? $services->getUserFactory();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -858,7 +865,8 @@ class SpecialContributions extends IncludableSpecialPage {
|
||||||
$this->actorMigration,
|
$this->actorMigration,
|
||||||
$this->revisionStore,
|
$this->revisionStore,
|
||||||
$this->namespaceInfo,
|
$this->namespaceInfo,
|
||||||
$targetUser
|
$targetUser,
|
||||||
|
$this->commentFormatter
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use MediaWiki\Cache\LinkBatchFactory;
|
use MediaWiki\Cache\LinkBatchFactory;
|
||||||
|
use MediaWiki\CommentFormatter\RowCommentFormatter;
|
||||||
use Wikimedia\Rdbms\ILoadBalancer;
|
use Wikimedia\Rdbms\ILoadBalancer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -45,23 +46,29 @@ class SpecialProtectedpages extends SpecialPage {
|
||||||
/** @var UserCache */
|
/** @var UserCache */
|
||||||
private $userCache;
|
private $userCache;
|
||||||
|
|
||||||
|
/** @var RowCommentFormatter */
|
||||||
|
private $rowCommentFormatter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param LinkBatchFactory $linkBatchFactory
|
* @param LinkBatchFactory $linkBatchFactory
|
||||||
* @param ILoadBalancer $loadBalancer
|
* @param ILoadBalancer $loadBalancer
|
||||||
* @param CommentStore $commentStore
|
* @param CommentStore $commentStore
|
||||||
* @param UserCache $userCache
|
* @param UserCache $userCache
|
||||||
|
* @param RowCommentFormatter $rowCommentFormatter
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
LinkBatchFactory $linkBatchFactory,
|
LinkBatchFactory $linkBatchFactory,
|
||||||
ILoadBalancer $loadBalancer,
|
ILoadBalancer $loadBalancer,
|
||||||
CommentStore $commentStore,
|
CommentStore $commentStore,
|
||||||
UserCache $userCache
|
UserCache $userCache,
|
||||||
|
RowCommentFormatter $rowCommentFormatter
|
||||||
) {
|
) {
|
||||||
parent::__construct( 'Protectedpages' );
|
parent::__construct( 'Protectedpages' );
|
||||||
$this->linkBatchFactory = $linkBatchFactory;
|
$this->linkBatchFactory = $linkBatchFactory;
|
||||||
$this->loadBalancer = $loadBalancer;
|
$this->loadBalancer = $loadBalancer;
|
||||||
$this->commentStore = $commentStore;
|
$this->commentStore = $commentStore;
|
||||||
$this->userCache = $userCache;
|
$this->userCache = $userCache;
|
||||||
|
$this->rowCommentFormatter = $rowCommentFormatter;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function execute( $par ) {
|
public function execute( $par ) {
|
||||||
|
|
@ -97,7 +104,8 @@ class SpecialProtectedpages extends SpecialPage {
|
||||||
$this->linkBatchFactory,
|
$this->linkBatchFactory,
|
||||||
$this->loadBalancer,
|
$this->loadBalancer,
|
||||||
$this->commentStore,
|
$this->commentStore,
|
||||||
$this->userCache
|
$this->userCache,
|
||||||
|
$this->rowCommentFormatter
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->getOutput()->addHTML( $this->showOptions(
|
$this->getOutput()->addHTML( $this->showOptions(
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ use MediaWiki\Block\Restriction\NamespaceRestriction;
|
||||||
use MediaWiki\Block\Restriction\PageRestriction;
|
use MediaWiki\Block\Restriction\PageRestriction;
|
||||||
use MediaWiki\Block\Restriction\Restriction;
|
use MediaWiki\Block\Restriction\Restriction;
|
||||||
use MediaWiki\Cache\LinkBatchFactory;
|
use MediaWiki\Cache\LinkBatchFactory;
|
||||||
|
use MediaWiki\CommentFormatter\RowCommentFormatter;
|
||||||
use MediaWiki\SpecialPage\SpecialPageFactory;
|
use MediaWiki\SpecialPage\SpecialPageFactory;
|
||||||
use MediaWiki\User\UserIdentity;
|
use MediaWiki\User\UserIdentity;
|
||||||
use Wikimedia\IPUtils;
|
use Wikimedia\IPUtils;
|
||||||
|
|
@ -65,6 +66,12 @@ class BlockListPager extends TablePager {
|
||||||
/** @var BlockActionInfo */
|
/** @var BlockActionInfo */
|
||||||
private $blockActionInfo;
|
private $blockActionInfo;
|
||||||
|
|
||||||
|
/** @var RowCommentFormatter */
|
||||||
|
private $rowCommentFormatter;
|
||||||
|
|
||||||
|
/** @var string[] */
|
||||||
|
private $formattedComments = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param SpecialPage $page
|
* @param SpecialPage $page
|
||||||
* @param array $conds
|
* @param array $conds
|
||||||
|
|
@ -75,6 +82,7 @@ class BlockListPager extends TablePager {
|
||||||
* @param CommentStore $commentStore
|
* @param CommentStore $commentStore
|
||||||
* @param BlockUtils $blockUtils
|
* @param BlockUtils $blockUtils
|
||||||
* @param BlockActionInfo $blockActionInfo
|
* @param BlockActionInfo $blockActionInfo
|
||||||
|
* @param RowCommentFormatter $rowCommentFormatter
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
$page,
|
$page,
|
||||||
|
|
@ -85,7 +93,8 @@ class BlockListPager extends TablePager {
|
||||||
SpecialPageFactory $specialPageFactory,
|
SpecialPageFactory $specialPageFactory,
|
||||||
CommentStore $commentStore,
|
CommentStore $commentStore,
|
||||||
BlockUtils $blockUtils,
|
BlockUtils $blockUtils,
|
||||||
BlockActionInfo $blockActionInfo
|
BlockActionInfo $blockActionInfo,
|
||||||
|
RowCommentFormatter $rowCommentFormatter
|
||||||
) {
|
) {
|
||||||
$this->mDb = $loadBalancer->getConnectionRef( ILoadBalancer::DB_REPLICA );
|
$this->mDb = $loadBalancer->getConnectionRef( ILoadBalancer::DB_REPLICA );
|
||||||
parent::__construct( $page->getContext(), $page->getLinkRenderer() );
|
parent::__construct( $page->getContext(), $page->getLinkRenderer() );
|
||||||
|
|
@ -97,6 +106,7 @@ class BlockListPager extends TablePager {
|
||||||
$this->commentStore = $commentStore;
|
$this->commentStore = $commentStore;
|
||||||
$this->blockUtils = $blockUtils;
|
$this->blockUtils = $blockUtils;
|
||||||
$this->blockActionInfo = $blockActionInfo;
|
$this->blockActionInfo = $blockActionInfo;
|
||||||
|
$this->rowCommentFormatter = $rowCommentFormatter;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getFieldNames() {
|
protected function getFieldNames() {
|
||||||
|
|
@ -243,8 +253,7 @@ class BlockListPager extends TablePager {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'ipb_reason':
|
case 'ipb_reason':
|
||||||
$value = $this->commentStore->getComment( 'ipb_reason', $row )->text;
|
$formatted = $this->formattedComments[$this->getResultOffset()];
|
||||||
$formatted = Linker::formatComment( $value );
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'ipb_params':
|
case 'ipb_params':
|
||||||
|
|
@ -469,7 +478,7 @@ class BlockListPager extends TablePager {
|
||||||
* @param IResultWrapper $result
|
* @param IResultWrapper $result
|
||||||
*/
|
*/
|
||||||
public function preprocessResults( $result ) {
|
public function preprocessResults( $result ) {
|
||||||
# Do a link batch query
|
// Do a link batch query
|
||||||
$lb = $this->linkBatchFactory->newLinkBatch();
|
$lb = $this->linkBatchFactory->newLinkBatch();
|
||||||
$lb->setCaller( __METHOD__ );
|
$lb->setCaller( __METHOD__ );
|
||||||
|
|
||||||
|
|
@ -505,6 +514,10 @@ class BlockListPager extends TablePager {
|
||||||
}
|
}
|
||||||
|
|
||||||
$lb->execute();
|
$lb->execute();
|
||||||
|
|
||||||
|
// Format comments
|
||||||
|
// The keys of formattedComments will be the corresponding offset into $result
|
||||||
|
$this->formattedComments = $this->rowCommentFormatter->formatRows( $result, 'ipb_reason' );
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use MediaWiki\Cache\LinkBatchFactory;
|
use MediaWiki\Cache\LinkBatchFactory;
|
||||||
|
use MediaWiki\CommentFormatter\CommentFormatter;
|
||||||
use MediaWiki\HookContainer\HookContainer;
|
use MediaWiki\HookContainer\HookContainer;
|
||||||
use MediaWiki\HookContainer\HookRunner;
|
use MediaWiki\HookContainer\HookRunner;
|
||||||
use MediaWiki\Linker\LinkRenderer;
|
use MediaWiki\Linker\LinkRenderer;
|
||||||
|
|
@ -127,6 +128,15 @@ class ContribsPager extends RangeChronologicalPager {
|
||||||
/** @var NamespaceInfo */
|
/** @var NamespaceInfo */
|
||||||
private $namespaceInfo;
|
private $namespaceInfo;
|
||||||
|
|
||||||
|
/** @var CommentFormatter */
|
||||||
|
private $commentFormatter;
|
||||||
|
|
||||||
|
/** @var string[] */
|
||||||
|
private $formattedComments = [];
|
||||||
|
|
||||||
|
/** @var RevisionRecord[] Cached revisions by ID */
|
||||||
|
private $revisions = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param IContextSource $context
|
* @param IContextSource $context
|
||||||
* @param array $options
|
* @param array $options
|
||||||
|
|
@ -138,6 +148,7 @@ class ContribsPager extends RangeChronologicalPager {
|
||||||
* @param RevisionStore|null $revisionStore
|
* @param RevisionStore|null $revisionStore
|
||||||
* @param NamespaceInfo|null $namespaceInfo
|
* @param NamespaceInfo|null $namespaceInfo
|
||||||
* @param UserIdentity|null $targetUser
|
* @param UserIdentity|null $targetUser
|
||||||
|
* @param CommentFormatter|null $commentFormatter
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
IContextSource $context,
|
IContextSource $context,
|
||||||
|
|
@ -149,7 +160,8 @@ class ContribsPager extends RangeChronologicalPager {
|
||||||
ActorMigration $actorMigration = null,
|
ActorMigration $actorMigration = null,
|
||||||
RevisionStore $revisionStore = null,
|
RevisionStore $revisionStore = null,
|
||||||
NamespaceInfo $namespaceInfo = null,
|
NamespaceInfo $namespaceInfo = null,
|
||||||
UserIdentity $targetUser = null
|
UserIdentity $targetUser = null,
|
||||||
|
CommentFormatter $commentFormatter = null
|
||||||
) {
|
) {
|
||||||
// Class is used directly in extensions - T266484
|
// Class is used directly in extensions - T266484
|
||||||
$services = MediaWikiServices::getInstance();
|
$services = MediaWikiServices::getInstance();
|
||||||
|
|
@ -225,6 +237,7 @@ class ContribsPager extends RangeChronologicalPager {
|
||||||
$this->hookRunner = new HookRunner( $hookContainer ?? $services->getHookContainer() );
|
$this->hookRunner = new HookRunner( $hookContainer ?? $services->getHookContainer() );
|
||||||
$this->revisionStore = $revisionStore ?? $services->getRevisionStore();
|
$this->revisionStore = $revisionStore ?? $services->getRevisionStore();
|
||||||
$this->namespaceInfo = $namespaceInfo ?? $services->getNamespaceInfo();
|
$this->namespaceInfo = $namespaceInfo ?? $services->getNamespaceInfo();
|
||||||
|
$this->commentFormatter = $commentFormatter ?? $services->getCommentFormatter();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getDefaultQuery() {
|
public function getDefaultQuery() {
|
||||||
|
|
@ -567,28 +580,41 @@ class ContribsPager extends RangeChronologicalPager {
|
||||||
$this->mResult->seek( 0 );
|
$this->mResult->seek( 0 );
|
||||||
$parentRevIds = [];
|
$parentRevIds = [];
|
||||||
$this->mParentLens = [];
|
$this->mParentLens = [];
|
||||||
$batch = $this->linkBatchFactory->newLinkBatch();
|
$revisions = [];
|
||||||
|
$linkBatch = $this->linkBatchFactory->newLinkBatch();
|
||||||
$isIpRange = $this->isQueryableRange( $this->target );
|
$isIpRange = $this->isQueryableRange( $this->target );
|
||||||
# Give some pointers to make (last) links
|
# Give some pointers to make (last) links
|
||||||
foreach ( $this->mResult as $row ) {
|
foreach ( $this->mResult as $row ) {
|
||||||
if ( isset( $row->rev_parent_id ) && $row->rev_parent_id ) {
|
if ( isset( $row->rev_parent_id ) && $row->rev_parent_id ) {
|
||||||
$parentRevIds[] = $row->rev_parent_id;
|
$parentRevIds[] = $row->rev_parent_id;
|
||||||
}
|
}
|
||||||
if ( isset( $row->rev_id ) ) {
|
if ( $this->revisionStore->isRevisionRow( $row ) ) {
|
||||||
$this->mParentLens[$row->rev_id] = $row->rev_len;
|
$this->mParentLens[$row->rev_id] = $row->rev_len;
|
||||||
if ( $isIpRange ) {
|
if ( $isIpRange ) {
|
||||||
// If this is an IP range, batch the IP's talk page
|
// If this is an IP range, batch the IP's talk page
|
||||||
$batch->add( NS_USER_TALK, $row->rev_user_text );
|
$linkBatch->add( NS_USER_TALK, $row->rev_user_text );
|
||||||
}
|
}
|
||||||
$batch->add( $row->page_namespace, $row->page_title );
|
$linkBatch->add( $row->page_namespace, $row->page_title );
|
||||||
|
$revisions[$row->rev_id] = $this->revisionStore->newRevisionFromRow( $row );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
# Fetch rev_len for revisions not already scanned above
|
# Fetch rev_len for revisions not already scanned above
|
||||||
$this->mParentLens += $this->revisionStore->getRevisionSizes(
|
$this->mParentLens += $this->revisionStore->getRevisionSizes(
|
||||||
array_diff( $parentRevIds, array_keys( $this->mParentLens ) )
|
array_diff( $parentRevIds, array_keys( $this->mParentLens ) )
|
||||||
);
|
);
|
||||||
$batch->execute();
|
$linkBatch->execute();
|
||||||
$this->mResult->seek( 0 );
|
|
||||||
|
$this->formattedComments = $this->commentFormatter->createRevisionBatch()
|
||||||
|
->authority( $this->getAuthority() )
|
||||||
|
->revisions( $revisions )
|
||||||
|
->hideIfDeleted()
|
||||||
|
->execute();
|
||||||
|
|
||||||
|
# For performance, save the revision objects for later.
|
||||||
|
# The array is indexed by rev_id. doBatchLookups() may be called
|
||||||
|
# multiple times with different results, so merge the revisions array,
|
||||||
|
# ignoring any duplicates.
|
||||||
|
$this->revisions += $revisions;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -606,24 +632,25 @@ class ContribsPager extends RangeChronologicalPager {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check whether the revision associated is valid for formatting. If has no
|
* If the object looks like a revision row, or corresponds to a previously
|
||||||
* associated revision ID then null is returned.
|
* cached revision, return the RevisionRecord. Otherwise, return null.
|
||||||
*
|
|
||||||
* This was previously used by formatRow() but now exists only for the
|
|
||||||
* convenience of extensions.
|
|
||||||
*
|
*
|
||||||
* @since 1.35
|
* @since 1.35
|
||||||
* @deprecated since 1.37 use RevisionStore::isRevisionRow()
|
|
||||||
*
|
*
|
||||||
* @param stdClass $row
|
* @param mixed $row
|
||||||
* @param Title|null $title
|
* @param Title|null $title
|
||||||
* @return RevisionRecord|null
|
* @return RevisionRecord|null
|
||||||
*/
|
*/
|
||||||
public function tryCreatingRevisionRecord( $row, $title = null ) {
|
public function tryCreatingRevisionRecord( $row, $title = null ) {
|
||||||
if ( !$this->revisionStore->isRevisionRow( $row ) ) {
|
if ( $row instanceof stdClass && isset( $row->rev_id )
|
||||||
|
&& isset( $this->revisions[$row->rev_id] )
|
||||||
|
) {
|
||||||
|
return $this->revisions[$row->rev_id];
|
||||||
|
} elseif ( $this->revisionStore->isRevisionRow( $row ) ) {
|
||||||
|
return $this->revisionStore->newRevisionFromRow( $row, 0, $title );
|
||||||
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return $this->revisionStore->newRevisionFromRow( $row, 0, $title );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -656,7 +683,8 @@ class ContribsPager extends RangeChronologicalPager {
|
||||||
// skip formatting so that the row can be formatted by the
|
// skip formatting so that the row can be formatted by the
|
||||||
// ContributionsLineEnding hook below.
|
// ContributionsLineEnding hook below.
|
||||||
// FIXME: have some better way for extensions to provide formatted rows.
|
// FIXME: have some better way for extensions to provide formatted rows.
|
||||||
if ( $this->revisionStore->isRevisionRow( $row ) ) {
|
$revRecord = $this->tryCreatingRevisionRecord( $row, $page );
|
||||||
|
if ( $revRecord ) {
|
||||||
$revRecord = $this->revisionStore->newRevisionFromRow( $row, 0, $page );
|
$revRecord = $this->revisionStore->newRevisionFromRow( $row, 0, $page );
|
||||||
$attribs['data-mw-revid'] = $revRecord->getId();
|
$attribs['data-mw-revid'] = $revRecord->getId();
|
||||||
|
|
||||||
|
|
@ -732,7 +760,12 @@ class ContribsPager extends RangeChronologicalPager {
|
||||||
}
|
}
|
||||||
|
|
||||||
$lang = $this->getLanguage();
|
$lang = $this->getLanguage();
|
||||||
$comment = $lang->getDirMark() . Linker::revComment( $revRecord, false, true, false );
|
|
||||||
|
$comment = $lang->getDirMark() . (
|
||||||
|
$this->formattedComments[$row->rev_id] ??
|
||||||
|
$this->commentFormatter->formatRevision(
|
||||||
|
$revRecord, $user, false, true, false
|
||||||
|
) );
|
||||||
$d = ChangesList::revDateLink( $revRecord, $user, $lang, $page );
|
$d = ChangesList::revDateLink( $revRecord, $user, $lang, $page );
|
||||||
|
|
||||||
# When querying for an IP range, we want to always show user and user talk links.
|
# When querying for an IP range, we want to always show user and user talk links.
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use MediaWiki\Cache\LinkBatchFactory;
|
use MediaWiki\Cache\LinkBatchFactory;
|
||||||
|
use MediaWiki\CommentFormatter\RowCommentFormatter;
|
||||||
use MediaWiki\Linker\LinkRenderer;
|
use MediaWiki\Linker\LinkRenderer;
|
||||||
use Wikimedia\Rdbms\ILoadBalancer;
|
use Wikimedia\Rdbms\ILoadBalancer;
|
||||||
|
|
||||||
|
|
@ -37,6 +38,12 @@ class ProtectedPagesPager extends TablePager {
|
||||||
/** @var UserCache */
|
/** @var UserCache */
|
||||||
private $userCache;
|
private $userCache;
|
||||||
|
|
||||||
|
/** @var RowCommentFormatter */
|
||||||
|
private $rowCommentFormatter;
|
||||||
|
|
||||||
|
/** @var string[] */
|
||||||
|
private $formattedComments = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param SpecialPage $form
|
* @param SpecialPage $form
|
||||||
* @param array $conds
|
* @param array $conds
|
||||||
|
|
@ -53,6 +60,7 @@ class ProtectedPagesPager extends TablePager {
|
||||||
* @param ILoadBalancer $loadBalancer
|
* @param ILoadBalancer $loadBalancer
|
||||||
* @param CommentStore $commentStore
|
* @param CommentStore $commentStore
|
||||||
* @param UserCache $userCache
|
* @param UserCache $userCache
|
||||||
|
* @param RowCommentFormatter $rowCommentFormatter
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
$form,
|
$form,
|
||||||
|
|
@ -69,7 +77,8 @@ class ProtectedPagesPager extends TablePager {
|
||||||
LinkBatchFactory $linkBatchFactory,
|
LinkBatchFactory $linkBatchFactory,
|
||||||
ILoadBalancer $loadBalancer,
|
ILoadBalancer $loadBalancer,
|
||||||
CommentStore $commentStore,
|
CommentStore $commentStore,
|
||||||
UserCache $userCache
|
UserCache $userCache,
|
||||||
|
RowCommentFormatter $rowCommentFormatter
|
||||||
) {
|
) {
|
||||||
// Set database before parent constructor to avoid setting it there with wfGetDB
|
// Set database before parent constructor to avoid setting it there with wfGetDB
|
||||||
$this->mDb = $loadBalancer->getConnectionRef( ILoadBalancer::DB_REPLICA );
|
$this->mDb = $loadBalancer->getConnectionRef( ILoadBalancer::DB_REPLICA );
|
||||||
|
|
@ -86,6 +95,7 @@ class ProtectedPagesPager extends TablePager {
|
||||||
$this->linkBatchFactory = $linkBatchFactory;
|
$this->linkBatchFactory = $linkBatchFactory;
|
||||||
$this->commentStore = $commentStore;
|
$this->commentStore = $commentStore;
|
||||||
$this->userCache = $userCache;
|
$this->userCache = $userCache;
|
||||||
|
$this->rowCommentFormatter = $rowCommentFormatter;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function preprocessResults( $result ) {
|
public function preprocessResults( $result ) {
|
||||||
|
|
@ -113,6 +123,9 @@ class ProtectedPagesPager extends TablePager {
|
||||||
}
|
}
|
||||||
|
|
||||||
$lb->execute();
|
$lb->execute();
|
||||||
|
|
||||||
|
// Format the comments
|
||||||
|
$this->formattedComments = $this->rowCommentFormatter->formatRows( $result, 'log_comment' );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getFieldNames() {
|
protected function getFieldNames() {
|
||||||
|
|
@ -255,8 +268,7 @@ class ProtectedPagesPager extends TablePager {
|
||||||
LogPage::DELETED_COMMENT,
|
LogPage::DELETED_COMMENT,
|
||||||
$this->getUser()
|
$this->getUser()
|
||||||
) ) {
|
) ) {
|
||||||
$value = $this->commentStore->getComment( 'log_comment', $row )->text;
|
$formatted = $this->formattedComments[$this->getResultOffset()];
|
||||||
$formatted = Linker::formatComment( $value ?? '' );
|
|
||||||
} else {
|
} else {
|
||||||
$formatted = $this->msg( 'rev-deleted-comment' )->escaped();
|
$formatted = $this->msg( 'rev-deleted-comment' )->escaped();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use MediaWiki\CommentFormatter\CommentItem;
|
||||||
|
use MediaWiki\MediaWikiServices;
|
||||||
|
|
||||||
require_once __DIR__ . '/../includes/Benchmarker.php';
|
require_once __DIR__ . '/../includes/Benchmarker.php';
|
||||||
|
|
||||||
class BenchmarkCommentFormatter extends Benchmarker {
|
class BenchmarkCommentFormatter extends Benchmarker {
|
||||||
|
|
@ -23,11 +26,13 @@ class BenchmarkCommentFormatter extends Benchmarker {
|
||||||
}
|
}
|
||||||
$entries = $result['query']['recentchanges'];
|
$entries = $result['query']['recentchanges'];
|
||||||
$inputs = [];
|
$inputs = [];
|
||||||
|
$comments = [];
|
||||||
foreach ( $entries as $entry ) {
|
foreach ( $entries as $entry ) {
|
||||||
$inputs[] = [
|
$inputs[] = [
|
||||||
'comment' => $entry['comment'],
|
'comment' => $entry['comment'],
|
||||||
'title' => Title::newFromText( $entry['title'] )
|
'title' => Title::newFromText( $entry['title'] )
|
||||||
];
|
];
|
||||||
|
$comments[] = $entry['comment'];
|
||||||
}
|
}
|
||||||
$this->bench( [
|
$this->bench( [
|
||||||
'Linker::formatComment' => [
|
'Linker::formatComment' => [
|
||||||
|
|
@ -41,6 +46,30 @@ class BenchmarkCommentFormatter extends Benchmarker {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
||||||
|
'CommentFormatter::createBatch' => [
|
||||||
|
'function' => static function () use ( $inputs ) {
|
||||||
|
Title::clearCaches();
|
||||||
|
$formatter = MediaWikiServices::getInstance()->getCommentFormatter();
|
||||||
|
$comments = [];
|
||||||
|
foreach ( $inputs as $input ) {
|
||||||
|
$comments[] = ( new CommentItem( $input['comment'] ) )
|
||||||
|
->selfLinkTarget( $input['title'] );
|
||||||
|
}
|
||||||
|
$formatter->createBatch()
|
||||||
|
->comments( $comments )
|
||||||
|
->execute();
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
'CommentFormatter::formatStrings' => [
|
||||||
|
'function' => static function () use ( $comments ) {
|
||||||
|
Title::clearCaches();
|
||||||
|
$formatter = MediaWikiServices::getInstance()->getCommentFormatter();
|
||||||
|
$formatter->formatStrings( $comments );
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
] );
|
] );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -228,6 +228,9 @@ $wgAutoloadClasses += [
|
||||||
# tests/phpunit/unit/includes/auth
|
# tests/phpunit/unit/includes/auth
|
||||||
'MediaWiki\Tests\Unit\Auth\AuthenticationProviderTestTrait' => "$testDir/phpunit/unit/includes/auth/AuthenticationProviderTestTrait.php",
|
'MediaWiki\Tests\Unit\Auth\AuthenticationProviderTestTrait' => "$testDir/phpunit/unit/includes/auth/AuthenticationProviderTestTrait.php",
|
||||||
|
|
||||||
|
# tests/phpunit/unit/includes/CommentFormatter
|
||||||
|
'MediaWiki\Tests\Unit\CommentFormatter\CommentFormatterTestUtils' => "$testDir/phpunit/unit/includes/CommentFormatter/CommentFormatterTestUtils.php",
|
||||||
|
|
||||||
# tests/phpunit/unit/includes/editpage/Constraint and tests/phpunit/integration/includes/editpage/Constraint
|
# tests/phpunit/unit/includes/editpage/Constraint and tests/phpunit/integration/includes/editpage/Constraint
|
||||||
'EditConstraintTestTrait' => "$testDir/phpunit/unit/includes/editpage/Constraint/EditConstraintTestTrait.php",
|
'EditConstraintTestTrait' => "$testDir/phpunit/unit/includes/editpage/Constraint/EditConstraintTestTrait.php",
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -267,8 +267,9 @@ class LinkerTest extends MediaWikiLangTestCase {
|
||||||
/**
|
/**
|
||||||
* @dataProvider provideCasesForFormatComment
|
* @dataProvider provideCasesForFormatComment
|
||||||
* @covers Linker::formatComment
|
* @covers Linker::formatComment
|
||||||
* @covers Linker::formatAutocomments
|
|
||||||
* @covers Linker::formatLinksInComment
|
* @covers Linker::formatLinksInComment
|
||||||
|
* @covers \MediaWiki\CommentFormatter\CommentParser
|
||||||
|
* @covers \MediaWiki\CommentFormatter\CommentFormatter
|
||||||
*/
|
*/
|
||||||
public function testFormatComment(
|
public function testFormatComment(
|
||||||
$expected, $comment, $title = false, $local = false, $wikiId = null
|
$expected, $comment, $title = false, $local = false, $wikiId = null
|
||||||
|
|
@ -490,6 +491,8 @@ class LinkerTest extends MediaWikiLangTestCase {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @covers Linker::formatLinksInComment
|
* @covers Linker::formatLinksInComment
|
||||||
|
* @covers \MediaWiki\CommentFormatter\CommentParser
|
||||||
|
* @covers \MediaWiki\CommentFormatter\CommentFormatter
|
||||||
* @dataProvider provideCasesForFormatLinksInComment
|
* @dataProvider provideCasesForFormatLinksInComment
|
||||||
*/
|
*/
|
||||||
public function testFormatLinksInComment( $expected, $input, $wiki ) {
|
public function testFormatLinksInComment( $expected, $input, $wiki ) {
|
||||||
|
|
@ -666,53 +669,6 @@ class LinkerTest extends MediaWikiLangTestCase {
|
||||||
$this->assertEquals( $expected, $result );
|
$this->assertEquals( $expected, $result );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @covers Linker::makeCommentLink
|
|
||||||
* @dataProvider provideMakeCommentLink
|
|
||||||
*/
|
|
||||||
public function testMakeCommentLink( $expected, $linkTarget, $text, $wikiId = null, $options = [] ) {
|
|
||||||
$conf = new SiteConfiguration();
|
|
||||||
$conf->settings = [
|
|
||||||
'wgServer' => [
|
|
||||||
'enwiki' => '//en.example.org'
|
|
||||||
],
|
|
||||||
'wgArticlePath' => [
|
|
||||||
'enwiki' => '/w/$1',
|
|
||||||
],
|
|
||||||
];
|
|
||||||
$conf->suffixes = [ 'wiki' ];
|
|
||||||
$this->setMwGlobals( [
|
|
||||||
'wgScript' => '/wiki/index.php',
|
|
||||||
'wgArticlePath' => '/wiki/$1',
|
|
||||||
'wgCapitalLinks' => true,
|
|
||||||
'wgConf' => $conf,
|
|
||||||
] );
|
|
||||||
|
|
||||||
$this->assertEquals( $expected, Linker::makeCommentLink( $linkTarget, $text, $wikiId, $options ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function provideMakeCommentLink() {
|
|
||||||
return [
|
|
||||||
[
|
|
||||||
'<a href="/wiki/Special:BlankPage" title="Special:BlankPage">Test</a>',
|
|
||||||
new TitleValue( NS_SPECIAL, 'BlankPage' ),
|
|
||||||
'Test'
|
|
||||||
],
|
|
||||||
'External comment link' => [
|
|
||||||
'<a class="external" rel="nofollow" href="//en.example.org/w/BlankPage">Test</a>',
|
|
||||||
new TitleValue( NS_MAIN, 'BlankPage' ),
|
|
||||||
'Test',
|
|
||||||
'enwiki'
|
|
||||||
],
|
|
||||||
'External special page comment link' => [
|
|
||||||
'<a class="external" rel="nofollow" href="//en.example.org/w/Special:BlankPage">Test</a>',
|
|
||||||
new TitleValue( NS_SPECIAL, 'BlankPage' ),
|
|
||||||
'Test',
|
|
||||||
'enwiki'
|
|
||||||
],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @covers Linker::commentBlock
|
* @covers Linker::commentBlock
|
||||||
* @dataProvider provideCommentBlock
|
* @dataProvider provideCommentBlock
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,8 @@ class RollbackActionTest extends MediaWikiIntegrationTestCase {
|
||||||
$mwServices->getContentHandlerFactory(),
|
$mwServices->getContentHandlerFactory(),
|
||||||
$mwServices->getRollbackPageFactory(),
|
$mwServices->getRollbackPageFactory(),
|
||||||
$mwServices->getUserOptionsLookup(),
|
$mwServices->getUserOptionsLookup(),
|
||||||
$mwServices->getWatchlistManager()
|
$mwServices->getWatchlistManager(),
|
||||||
|
$mwServices->getCommentFormatter()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,9 @@ class ContribsPagerTest extends MediaWikiIntegrationTestCase {
|
||||||
/** @var NamespaceInfo */
|
/** @var NamespaceInfo */
|
||||||
private $namespaceInfo;
|
private $namespaceInfo;
|
||||||
|
|
||||||
|
/** @var CommentFormatter */
|
||||||
|
private $commentFormatter;
|
||||||
|
|
||||||
protected function setUp(): void {
|
protected function setUp(): void {
|
||||||
parent::setUp();
|
parent::setUp();
|
||||||
|
|
||||||
|
|
@ -44,6 +47,7 @@ class ContribsPagerTest extends MediaWikiIntegrationTestCase {
|
||||||
$this->loadBalancer = $services->getDBLoadBalancer();
|
$this->loadBalancer = $services->getDBLoadBalancer();
|
||||||
$this->actorMigration = $services->getActorMigration();
|
$this->actorMigration = $services->getActorMigration();
|
||||||
$this->namespaceInfo = $services->getNamespaceInfo();
|
$this->namespaceInfo = $services->getNamespaceInfo();
|
||||||
|
$this->commentFormatter = $services->getCommentFormatter();
|
||||||
$this->pager = $this->getContribsPager( [
|
$this->pager = $this->getContribsPager( [
|
||||||
'start' => '2017-01-01',
|
'start' => '2017-01-01',
|
||||||
'end' => '2017-02-02',
|
'end' => '2017-02-02',
|
||||||
|
|
@ -61,7 +65,8 @@ class ContribsPagerTest extends MediaWikiIntegrationTestCase {
|
||||||
$this->actorMigration,
|
$this->actorMigration,
|
||||||
$this->revisionStore,
|
$this->revisionStore,
|
||||||
$this->namespaceInfo,
|
$this->namespaceInfo,
|
||||||
$targetUser
|
$targetUser,
|
||||||
|
$this->commentFormatter
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ use MediaWiki\Block\DatabaseBlock;
|
||||||
use MediaWiki\Block\Restriction\NamespaceRestriction;
|
use MediaWiki\Block\Restriction\NamespaceRestriction;
|
||||||
use MediaWiki\Block\Restriction\PageRestriction;
|
use MediaWiki\Block\Restriction\PageRestriction;
|
||||||
use MediaWiki\Cache\LinkBatchFactory;
|
use MediaWiki\Cache\LinkBatchFactory;
|
||||||
|
use MediaWiki\CommentFormatter\RowCommentFormatter;
|
||||||
use MediaWiki\MediaWikiServices;
|
use MediaWiki\MediaWikiServices;
|
||||||
use MediaWiki\SpecialPage\SpecialPageFactory;
|
use MediaWiki\SpecialPage\SpecialPageFactory;
|
||||||
use Wikimedia\Rdbms\ILoadBalancer;
|
use Wikimedia\Rdbms\ILoadBalancer;
|
||||||
|
|
@ -41,6 +42,9 @@ class BlockListPagerTest extends MediaWikiIntegrationTestCase {
|
||||||
/** @var BlockActionInfo */
|
/** @var BlockActionInfo */
|
||||||
private $blockActionInfo;
|
private $blockActionInfo;
|
||||||
|
|
||||||
|
/** @var RowCommentFormatter */
|
||||||
|
private $rowCommentFormatter;
|
||||||
|
|
||||||
protected function setUp(): void {
|
protected function setUp(): void {
|
||||||
parent::setUp();
|
parent::setUp();
|
||||||
|
|
||||||
|
|
@ -52,6 +56,7 @@ class BlockListPagerTest extends MediaWikiIntegrationTestCase {
|
||||||
$this->commentStore = $services->getCommentStore();
|
$this->commentStore = $services->getCommentStore();
|
||||||
$this->blockUtils = $services->getBlockUtils();
|
$this->blockUtils = $services->getBlockUtils();
|
||||||
$this->blockActionInfo = $services->getBlockActionInfo();
|
$this->blockActionInfo = $services->getBlockActionInfo();
|
||||||
|
$this->rowCommentFormatter = $services->getRowCommentFormatter();
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getBlockListPager() {
|
private function getBlockListPager() {
|
||||||
|
|
@ -64,7 +69,8 @@ class BlockListPagerTest extends MediaWikiIntegrationTestCase {
|
||||||
$this->specialPageFactory,
|
$this->specialPageFactory,
|
||||||
$this->commentStore,
|
$this->commentStore,
|
||||||
$this->blockUtils,
|
$this->blockUtils,
|
||||||
$this->blockActionInfo
|
$this->blockActionInfo,
|
||||||
|
$this->rowCommentFormatter
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -229,6 +235,12 @@ class BlockListPagerTest extends MediaWikiIntegrationTestCase {
|
||||||
* @covers ::preprocessResults
|
* @covers ::preprocessResults
|
||||||
*/
|
*/
|
||||||
public function testPreprocessResults() {
|
public function testPreprocessResults() {
|
||||||
|
$this->tablesUsed[] = 'ipblocks';
|
||||||
|
$this->tablesUsed[] = 'ipblocks_restrictions';
|
||||||
|
$this->tablesUsed[] = 'comment';
|
||||||
|
$this->tablesUsed[] = 'page';
|
||||||
|
$this->tablesUsed[] = 'user';
|
||||||
|
|
||||||
// Test the Link Cache.
|
// Test the Link Cache.
|
||||||
$linkCache = MediaWikiServices::getInstance()->getLinkCache();
|
$linkCache = MediaWikiServices::getInstance()->getLinkCache();
|
||||||
$wrappedlinkCache = TestingAccessWrapper::newFromObject( $linkCache );
|
$wrappedlinkCache = TestingAccessWrapper::newFromObject( $linkCache );
|
||||||
|
|
@ -238,7 +250,8 @@ class BlockListPagerTest extends MediaWikiIntegrationTestCase {
|
||||||
'User:127.0.0.1',
|
'User:127.0.0.1',
|
||||||
'User_talk:127.0.0.1',
|
'User_talk:127.0.0.1',
|
||||||
$admin->getUserPage()->getPrefixedDBkey(),
|
$admin->getUserPage()->getPrefixedDBkey(),
|
||||||
$admin->getTalkPage()->getPrefixedDBkey()
|
$admin->getTalkPage()->getPrefixedDBkey(),
|
||||||
|
'Comment_link'
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach ( $links as $link ) {
|
foreach ( $links as $link ) {
|
||||||
|
|
@ -251,9 +264,11 @@ class BlockListPagerTest extends MediaWikiIntegrationTestCase {
|
||||||
'ipb_by_text' => $admin->getName(),
|
'ipb_by_text' => $admin->getName(),
|
||||||
'ipb_sitewide' => 1,
|
'ipb_sitewide' => 1,
|
||||||
'ipb_timestamp' => $this->db->timestamp( wfTimestamp( TS_MW ) ),
|
'ipb_timestamp' => $this->db->timestamp( wfTimestamp( TS_MW ) ),
|
||||||
|
'ipb_reason_text' => '[[Comment link]]',
|
||||||
|
'ipb_reason_data' => null,
|
||||||
];
|
];
|
||||||
$pager = $this->getBlockListPager();
|
$pager = $this->getBlockListPager();
|
||||||
$pager->preprocessResults( [ $row ] );
|
$pager->preprocessResults( new FakeResultWrapper( [ $row ] ) );
|
||||||
|
|
||||||
foreach ( $links as $link ) {
|
foreach ( $links as $link ) {
|
||||||
$this->assertSame( 1, $wrappedlinkCache->badLinks->get( $link ), "Bad link [[$link]]" );
|
$this->assertSame( 1, $wrappedlinkCache->badLinks->get( $link ), "Bad link [[$link]]" );
|
||||||
|
|
@ -265,9 +280,11 @@ class BlockListPagerTest extends MediaWikiIntegrationTestCase {
|
||||||
'ipb_by' => $admin->getId(),
|
'ipb_by' => $admin->getId(),
|
||||||
'ipb_by_text' => $admin->getName(),
|
'ipb_by_text' => $admin->getName(),
|
||||||
'ipb_sitewide' => 1,
|
'ipb_sitewide' => 1,
|
||||||
|
'ipb_reason_text' => '',
|
||||||
|
'ipb_reason_data' => null,
|
||||||
];
|
];
|
||||||
$pager = $this->getBlockListPager();
|
$pager = $this->getBlockListPager();
|
||||||
$pager->preprocessResults( [ $row ] );
|
$pager->preprocessResults( new FakeResultWrapper( [ $row ] ) );
|
||||||
|
|
||||||
$this->assertObjectNotHasAttribute( 'ipb_restrictions', $row );
|
$this->assertObjectNotHasAttribute( 'ipb_restrictions', $row );
|
||||||
|
|
||||||
|
|
@ -291,7 +308,11 @@ class BlockListPagerTest extends MediaWikiIntegrationTestCase {
|
||||||
$blockStore = MediaWikiServices::getInstance()->getDatabaseBlockStore();
|
$blockStore = MediaWikiServices::getInstance()->getDatabaseBlockStore();
|
||||||
$blockStore->insertBlock( $block );
|
$blockStore->insertBlock( $block );
|
||||||
|
|
||||||
$result = $this->db->select( 'ipblocks', [ '*' ], [ 'ipb_id' => $block->getId() ] );
|
$result = $this->db->newSelectQueryBuilder()
|
||||||
|
->queryInfo( DatabaseBlock::getQueryInfo() )
|
||||||
|
->where( [ 'ipb_id' => $block->getId() ] )
|
||||||
|
->caller( __METHOD__ )
|
||||||
|
->fetchResultSet();
|
||||||
|
|
||||||
$pager = $this->getBlockListPager();
|
$pager = $this->getBlockListPager();
|
||||||
$pager->preprocessResults( $result );
|
$pager->preprocessResults( $result );
|
||||||
|
|
@ -306,8 +327,5 @@ class BlockListPagerTest extends MediaWikiIntegrationTestCase {
|
||||||
$this->assertEquals( $page->getId(), $restriction->getTitle()->getArticleID() );
|
$this->assertEquals( $page->getId(), $restriction->getTitle()->getArticleID() );
|
||||||
$this->assertEquals( $title->getDBkey(), $restriction->getTitle()->getDBkey() );
|
$this->assertEquals( $title->getDBkey(), $restriction->getTitle()->getDBkey() );
|
||||||
$this->assertEquals( $title->getNamespace(), $restriction->getTitle()->getNamespace() );
|
$this->assertEquals( $title->getNamespace(), $restriction->getTitle()->getNamespace() );
|
||||||
|
|
||||||
// Delete the block and the restrictions.
|
|
||||||
$blockStore->deleteBlock( $block );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,380 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MediaWiki\Tests\Integration\CommentFormatter;
|
||||||
|
|
||||||
|
use MediaWiki\CommentFormatter\CommentFormatter;
|
||||||
|
use MediaWiki\CommentFormatter\CommentItem;
|
||||||
|
use MediaWiki\CommentFormatter\CommentParser;
|
||||||
|
use MediaWiki\CommentFormatter\CommentParserFactory;
|
||||||
|
use MediaWiki\Linker\LinkTarget;
|
||||||
|
use MediaWiki\Page\PageIdentityValue;
|
||||||
|
use MediaWiki\Permissions\SimpleAuthority;
|
||||||
|
use MediaWiki\Revision\MutableRevisionRecord;
|
||||||
|
use MediaWiki\Revision\RevisionRecord;
|
||||||
|
use MediaWiki\Tests\Unit\CommentFormatter\CommentFormatterTestUtils;
|
||||||
|
use MediaWiki\User\UserIdentityValue;
|
||||||
|
use MediaWikiIntegrationTestCase;
|
||||||
|
use TitleValue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trivial comment formatting with a mocked parser. Can't be a unit test because
|
||||||
|
* of the wfMessage() calls.
|
||||||
|
*
|
||||||
|
* @covers \MediaWiki\CommentFormatter\CommentFormatter
|
||||||
|
*/
|
||||||
|
class CommentFormatterTest extends MediaWikiIntegrationTestCase {
|
||||||
|
private function getParser() {
|
||||||
|
return new class extends CommentParser {
|
||||||
|
public function __construct() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public function preprocess(
|
||||||
|
string $comment, LinkTarget $selfLinkTarget = null, $samePage = false,
|
||||||
|
$wikiId = null, $enableSectionLinks = true
|
||||||
|
) {
|
||||||
|
if ( $comment === '' || $comment === '*' ) {
|
||||||
|
return $comment; // Hack to make it work more like the real parser
|
||||||
|
}
|
||||||
|
return CommentFormatterTestUtils::dumpArray( [
|
||||||
|
'comment' => $comment,
|
||||||
|
'selfLinkTarget' => $selfLinkTarget,
|
||||||
|
'samePage' => $samePage,
|
||||||
|
'wikiId' => $wikiId,
|
||||||
|
'enableSectionLinks' => $enableSectionLinks
|
||||||
|
] );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function preprocessUnsafe(
|
||||||
|
$comment, LinkTarget $selfLinkTarget = null, $samePage = false, $wikiId = null,
|
||||||
|
$enableSectionLinks = true
|
||||||
|
) {
|
||||||
|
return CommentFormatterTestUtils::dumpArray( [
|
||||||
|
'comment' => $comment,
|
||||||
|
'selfLinkTarget' => $selfLinkTarget,
|
||||||
|
'samePage' => $samePage,
|
||||||
|
'wikiId' => $wikiId,
|
||||||
|
'enableSectionLinks' => $enableSectionLinks,
|
||||||
|
'unsafe' => true
|
||||||
|
] );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function finalize( $comments ) {
|
||||||
|
return $comments;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getParserFactory() {
|
||||||
|
$parser = $this->getParser();
|
||||||
|
return new class( $parser ) extends CommentParserFactory {
|
||||||
|
private $parser;
|
||||||
|
|
||||||
|
public function __construct( $parser ) {
|
||||||
|
$this->parser = $parser;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function create() {
|
||||||
|
return $this->parser;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private function newCommentFormatter() {
|
||||||
|
return new CommentFormatter( $this->getParserFactory() );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCreateBatch() {
|
||||||
|
$formatter = $this->newCommentFormatter();
|
||||||
|
$result = $formatter->createBatch()
|
||||||
|
->strings( [ 'key' => 'c' ] )
|
||||||
|
->useBlock()
|
||||||
|
->useParentheses()
|
||||||
|
->samePage()
|
||||||
|
->execute();
|
||||||
|
$this->assertSame(
|
||||||
|
[
|
||||||
|
'key' =>
|
||||||
|
// parentheses have to come after something so I guess it
|
||||||
|
// makes sense that there is a space here
|
||||||
|
' ' .
|
||||||
|
'<span class="comment">(comment=c, samePage, !wikiId, enableSectionLinks)</span>'
|
||||||
|
],
|
||||||
|
$result
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFormatItems() {
|
||||||
|
$formatter = $this->newCommentFormatter();
|
||||||
|
$result = $formatter->formatItems( [
|
||||||
|
'key' => new CommentItem( 'c' )
|
||||||
|
] );
|
||||||
|
$this->assertSame(
|
||||||
|
[ 'key' => 'comment=c, !samePage, !wikiId, enableSectionLinks' ],
|
||||||
|
$result
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFormat() {
|
||||||
|
$formatter = $this->newCommentFormatter();
|
||||||
|
$result = $formatter->format(
|
||||||
|
'c',
|
||||||
|
new TitleValue( 0, 'Page' ),
|
||||||
|
true,
|
||||||
|
'enwiki'
|
||||||
|
);
|
||||||
|
$this->assertSame(
|
||||||
|
'comment=c, selfLinkTarget=0:Page, samePage, wikiId=enwiki, enableSectionLinks',
|
||||||
|
$result
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFormatBlock() {
|
||||||
|
$formatter = $this->newCommentFormatter();
|
||||||
|
$result = $formatter->formatBlock(
|
||||||
|
'c',
|
||||||
|
new TitleValue( 0, 'Page' ),
|
||||||
|
true,
|
||||||
|
'enwiki',
|
||||||
|
true
|
||||||
|
);
|
||||||
|
$this->assertSame(
|
||||||
|
' <span class="comment">' .
|
||||||
|
'(comment=c, selfLinkTarget=0:Page, samePage, wikiId=enwiki, enableSectionLinks)' .
|
||||||
|
'</span>',
|
||||||
|
$result
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFormatLinksUnsafe() {
|
||||||
|
$formatter = $this->newCommentFormatter();
|
||||||
|
$result = $formatter->formatLinksUnsafe(
|
||||||
|
'c',
|
||||||
|
new TitleValue( 0, 'Page' ),
|
||||||
|
true,
|
||||||
|
'enwiki'
|
||||||
|
);
|
||||||
|
$this->assertSame(
|
||||||
|
'comment=c, selfLinkTarget=0:Page, samePage, wikiId=enwiki, !enableSectionLinks, unsafe',
|
||||||
|
$result
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFormatLinks() {
|
||||||
|
$formatter = $this->newCommentFormatter();
|
||||||
|
$result = $formatter->formatLinks(
|
||||||
|
'c',
|
||||||
|
new TitleValue( 0, 'Page' ),
|
||||||
|
true,
|
||||||
|
'enwiki'
|
||||||
|
);
|
||||||
|
$this->assertSame(
|
||||||
|
'comment=c, selfLinkTarget=0:Page, samePage, wikiId=enwiki, !enableSectionLinks',
|
||||||
|
$result
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFormatStrings() {
|
||||||
|
$formatter = $this->newCommentFormatter();
|
||||||
|
$result = $formatter->formatStrings(
|
||||||
|
[
|
||||||
|
'a' => 'A',
|
||||||
|
'b' => 'B'
|
||||||
|
],
|
||||||
|
new TitleValue( 0, 'Page' ),
|
||||||
|
true,
|
||||||
|
'enwiki'
|
||||||
|
);
|
||||||
|
$this->assertSame(
|
||||||
|
[
|
||||||
|
'a' => 'comment=A, selfLinkTarget=0:Page, samePage, wikiId=enwiki, enableSectionLinks',
|
||||||
|
'b' => 'comment=B, selfLinkTarget=0:Page, samePage, wikiId=enwiki, enableSectionLinks'
|
||||||
|
],
|
||||||
|
$result
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFormatStringsAsBlock() {
|
||||||
|
$formatter = $this->newCommentFormatter();
|
||||||
|
$result = $formatter->formatStringsAsBlock(
|
||||||
|
[
|
||||||
|
'a' => 'A',
|
||||||
|
'b' => 'B'
|
||||||
|
],
|
||||||
|
new TitleValue( 0, 'Page' ),
|
||||||
|
true,
|
||||||
|
'enwiki',
|
||||||
|
true
|
||||||
|
);
|
||||||
|
$this->assertSame(
|
||||||
|
[
|
||||||
|
'a' => ' <span class="comment">(' .
|
||||||
|
'comment=A, selfLinkTarget=0:Page, samePage, wikiId=enwiki, enableSectionLinks' .
|
||||||
|
')</span>',
|
||||||
|
'b' => ' <span class="comment">(' .
|
||||||
|
'comment=B, selfLinkTarget=0:Page, samePage, wikiId=enwiki, enableSectionLinks' .
|
||||||
|
')</span>'
|
||||||
|
],
|
||||||
|
$result
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function provideFormatRevision() {
|
||||||
|
$normal = ' <span class="comment">(' .
|
||||||
|
'comment=hello, selfLinkTarget=Page, !samePage, enableSectionLinks' .
|
||||||
|
')</span>';
|
||||||
|
$deleted = ' <span class="history-deleted comment"> ' .
|
||||||
|
'<span class="comment">(edit summary removed)</span></span>';
|
||||||
|
$deletedAllowed = ' <span class="history-deleted comment"> ' .
|
||||||
|
'<span class="comment">(' .
|
||||||
|
'comment=hello, selfLinkTarget=Page, !samePage, enableSectionLinks' .
|
||||||
|
')</span></span>';
|
||||||
|
|
||||||
|
return [
|
||||||
|
'not deleted' => [
|
||||||
|
'hello', false, false, false, true,
|
||||||
|
$normal,
|
||||||
|
],
|
||||||
|
'deleted, for public, not allowed' => [
|
||||||
|
'hello', true, true, false, true,
|
||||||
|
$deleted
|
||||||
|
],
|
||||||
|
'deleted, for public, allowed' => [
|
||||||
|
'hello', true, true, true, true,
|
||||||
|
$deleted
|
||||||
|
],
|
||||||
|
'deleted, for private, not allowed' => [
|
||||||
|
'hello', false, true, false, true,
|
||||||
|
$deleted
|
||||||
|
],
|
||||||
|
'deleted, for private, allowed' => [
|
||||||
|
'hello', false, true, true, true,
|
||||||
|
$deletedAllowed,
|
||||||
|
],
|
||||||
|
'empty' => [
|
||||||
|
'', false, false, false, true,
|
||||||
|
''
|
||||||
|
],
|
||||||
|
'asterisk' => [
|
||||||
|
'*', false, false, false, true,
|
||||||
|
''
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $text
|
||||||
|
* @param bool $isDeleted
|
||||||
|
* @param bool $isAllowed
|
||||||
|
* @return array{RevisionRecord,Authority}
|
||||||
|
* @throws \MWException
|
||||||
|
*/
|
||||||
|
private function makeRevisionAndAuthority( $text, $isDeleted, $isAllowed ) {
|
||||||
|
$page = new PageIdentityValue( 1, 0, 'Page', false );
|
||||||
|
$rev = new MutableRevisionRecord( $page );
|
||||||
|
$comment = new \CommentStoreComment( 1, $text );
|
||||||
|
$rev->setId( 100 );
|
||||||
|
$rev->setComment( $comment );
|
||||||
|
$rev->setVisibility( $isDeleted ? RevisionRecord::DELETED_COMMENT : 0 );
|
||||||
|
$user = new UserIdentityValue( 1, 'Sysop' );
|
||||||
|
$rights = $isAllowed ? [ 'deletedhistory' ] : [];
|
||||||
|
$authority = new SimpleAuthority( $user, $rights );
|
||||||
|
return [ $rev, $authority ];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @dataProvider provideFormatRevision */
|
||||||
|
public function testFormatRevision( $comment, $isPublic, $isDeleted, $isAllowed, $useParentheses,
|
||||||
|
$expected
|
||||||
|
) {
|
||||||
|
list( $rev, $authority ) = $this->makeRevisionAndAuthority(
|
||||||
|
$comment, $isDeleted, $isAllowed );
|
||||||
|
$formatter = $this->newCommentFormatter();
|
||||||
|
$result = $formatter->formatRevision(
|
||||||
|
$rev,
|
||||||
|
$authority,
|
||||||
|
false,
|
||||||
|
$isPublic
|
||||||
|
);
|
||||||
|
$this->assertSame(
|
||||||
|
$expected,
|
||||||
|
$result
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @dataProvider provideFormatRevision */
|
||||||
|
public function testFormatRevisions( $comment, $isPublic, $isDeleted, $isAllowed, $useParentheses,
|
||||||
|
$expected
|
||||||
|
) {
|
||||||
|
list( $rev, $authority ) = $this->makeRevisionAndAuthority(
|
||||||
|
$comment, $isDeleted, $isAllowed );
|
||||||
|
$formatter = $this->newCommentFormatter();
|
||||||
|
$result = $formatter->formatRevisions(
|
||||||
|
[ 'key' => $rev ],
|
||||||
|
$authority,
|
||||||
|
false,
|
||||||
|
$isPublic
|
||||||
|
);
|
||||||
|
$this->assertSame(
|
||||||
|
[ 'key' => $expected ],
|
||||||
|
$result
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFormatRevisionsById() {
|
||||||
|
list( $rev, $authority ) = $this->makeRevisionAndAuthority(
|
||||||
|
'hello', false, false );
|
||||||
|
$formatter = $this->newCommentFormatter();
|
||||||
|
$result = $formatter->formatRevisions(
|
||||||
|
[ 'key' => $rev ],
|
||||||
|
$authority,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
$this->assertSame(
|
||||||
|
[ 100 => ' <span class="comment">(' .
|
||||||
|
'comment=hello, selfLinkTarget=Page, !samePage, enableSectionLinks' .
|
||||||
|
')</span>'
|
||||||
|
],
|
||||||
|
$result
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @dataProvider provideFormatRevision */
|
||||||
|
public function testCreateRevisionBatch( $comment, $isPublic, $isDeleted, $isAllowed, $useParentheses,
|
||||||
|
$expected
|
||||||
|
) {
|
||||||
|
list( $rev, $authority ) = $this->makeRevisionAndAuthority(
|
||||||
|
$comment, $isDeleted, $isAllowed );
|
||||||
|
$formatter = $this->newCommentFormatter();
|
||||||
|
$result = $formatter->createRevisionBatch()
|
||||||
|
->authority( $authority )
|
||||||
|
->hideIfDeleted( $isPublic )
|
||||||
|
->useParentheses()
|
||||||
|
->revisions( [ 'key' => $rev ] )
|
||||||
|
->execute();
|
||||||
|
$this->assertSame(
|
||||||
|
[ 'key' => $expected ],
|
||||||
|
$result
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCreateRevisionBatchById() {
|
||||||
|
list( $rev, $authority ) = $this->makeRevisionAndAuthority(
|
||||||
|
'hello', false, false );
|
||||||
|
$formatter = $this->newCommentFormatter();
|
||||||
|
$result = $formatter->createRevisionBatch()
|
||||||
|
->authority( $authority )
|
||||||
|
->useParentheses()
|
||||||
|
->indexById()
|
||||||
|
->revisions( [ 'key' => $rev ] )
|
||||||
|
->execute();
|
||||||
|
$this->assertSame(
|
||||||
|
[ 100 => ' <span class="comment">(' .
|
||||||
|
'comment=hello, selfLinkTarget=Page, !samePage, enableSectionLinks' .
|
||||||
|
')</span>'
|
||||||
|
],
|
||||||
|
$result
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,396 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MediaWiki\Tests\Integration\CommentFormatter;
|
||||||
|
|
||||||
|
use MediaWiki\Cache\LinkBatchFactory;
|
||||||
|
use MediaWiki\CommentFormatter\CommentParser;
|
||||||
|
use SiteConfiguration;
|
||||||
|
use Title;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \MediaWiki\CommentFormatter\CommentParser
|
||||||
|
*/
|
||||||
|
class CommentParserTest extends \MediaWikiIntegrationTestCase {
|
||||||
|
|
||||||
|
private function getParser() {
|
||||||
|
$services = $this->getServiceContainer();
|
||||||
|
return new CommentParser(
|
||||||
|
$services->getLinkRenderer(),
|
||||||
|
$services->getLinkBatchFactory(),
|
||||||
|
$services->getLinkCache(),
|
||||||
|
$services->getRepoGroup(),
|
||||||
|
$services->getContentLanguage(),
|
||||||
|
$services->getContentLanguage(),
|
||||||
|
$services->getTitleParser(),
|
||||||
|
$services->getNamespaceInfo(),
|
||||||
|
$services->getHookContainer()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copied from LinkerTest so that LinkerTest can be deleted once deprecation
|
||||||
|
* and removal of Linker::formatComment() is complete.
|
||||||
|
*
|
||||||
|
* @return array[]
|
||||||
|
*/
|
||||||
|
public function provideFormatComment() {
|
||||||
|
$wikiId = 'enwiki'; // $wgConf has a fake entry for this
|
||||||
|
|
||||||
|
// phpcs:disable Generic.Files.LineLength
|
||||||
|
return [
|
||||||
|
// Linker::formatComment
|
||||||
|
[
|
||||||
|
'a<script>b',
|
||||||
|
'a<script>b',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'a—b',
|
||||||
|
'a—b',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"'''not bolded'''",
|
||||||
|
"'''not bolded'''",
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"try <script>evil</scipt> things",
|
||||||
|
"try <script>evil</scipt> things",
|
||||||
|
],
|
||||||
|
// Linker::formatAutocomments
|
||||||
|
[
|
||||||
|
'<span dir="auto"><span class="autocomment"><a href="/wiki/Special:BlankPage#autocomment" title="Special:BlankPage">→autocomment</a></span></span>',
|
||||||
|
"/* autocomment */",
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'<span dir="auto"><span class="autocomment"><a href="/wiki/Special:BlankPage#linkie.3F" title="Special:BlankPage">→[[linkie?]]</a></span></span>',
|
||||||
|
"/* [[linkie?]] */",
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'<span dir="auto"><span class="autocomment">: </span> // Edit via via</span>',
|
||||||
|
// Regression test for T222857
|
||||||
|
"/* */ // Edit via via",
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'<span dir="auto"><span class="autocomment">: </span> foobar</span>',
|
||||||
|
// Regression test for T222857
|
||||||
|
"/**/ foobar",
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'<span dir="auto"><span class="autocomment"><a href="/wiki/Special:BlankPage#autocomment" title="Special:BlankPage">→autocomment</a>: </span> post</span>',
|
||||||
|
"/* autocomment */ post",
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'pre <span dir="auto"><span class="autocomment"><a href="/wiki/Special:BlankPage#autocomment" title="Special:BlankPage">→autocomment</a></span></span>',
|
||||||
|
"pre /* autocomment */",
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'pre <span dir="auto"><span class="autocomment"><a href="/wiki/Special:BlankPage#autocomment" title="Special:BlankPage">→autocomment</a>: </span> post</span>',
|
||||||
|
"pre /* autocomment */ post",
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'<span dir="auto"><span class="autocomment"><a href="/wiki/Special:BlankPage#autocomment" title="Special:BlankPage">→autocomment</a>: </span> multiple? <span dir="auto"><span class="autocomment"><a href="/wiki/Special:BlankPage#autocomment2" title="Special:BlankPage">→autocomment2</a></span></span></span>',
|
||||||
|
"/* autocomment */ multiple? /* autocomment2 */",
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'<span dir="auto"><span class="autocomment"><a href="/wiki/Special:BlankPage#autocomment_containing_.2F.2A" title="Special:BlankPage">→autocomment containing /*</a>: </span> T70361</span>',
|
||||||
|
"/* autocomment containing /* */ T70361"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'<span dir="auto"><span class="autocomment"><a href="/wiki/Special:BlankPage#autocomment_containing_.22quotes.22" title="Special:BlankPage">→autocomment containing "quotes"</a></span></span>',
|
||||||
|
"/* autocomment containing \"quotes\" */"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'<span dir="auto"><span class="autocomment"><a href="/wiki/Special:BlankPage#autocomment_containing_.3Cscript.3Etags.3C.2Fscript.3E" title="Special:BlankPage">→autocomment containing <script>tags</script></a></span></span>',
|
||||||
|
"/* autocomment containing <script>tags</script> */"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'<span dir="auto"><span class="autocomment"><a href="#autocomment">→autocomment</a></span></span>',
|
||||||
|
"/* autocomment */",
|
||||||
|
false, true
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'<span dir="auto"><span class="autocomment">autocomment</span></span>',
|
||||||
|
"/* autocomment */",
|
||||||
|
null
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'',
|
||||||
|
"/* */",
|
||||||
|
false, true
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'',
|
||||||
|
"/* */",
|
||||||
|
null
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'<span dir="auto"><span class="autocomment">[[</span></span>',
|
||||||
|
"/* [[ */",
|
||||||
|
false, true
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'<span dir="auto"><span class="autocomment">[[</span></span>',
|
||||||
|
"/* [[ */",
|
||||||
|
null
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"foo <span dir=\"auto\"><span class=\"autocomment\"><a href=\"#.23\">→[[#_\t_]]</a></span></span>",
|
||||||
|
"foo /* [[#_\t_]] */",
|
||||||
|
false, true
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"foo <span dir=\"auto\"><span class=\"autocomment\"><a href=\"#_.09\">#_\t_</a></span></span>",
|
||||||
|
"foo /* [[#_\t_]] */",
|
||||||
|
null
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'<span dir="auto"><span class="autocomment"><a href="/wiki/Special:BlankPage#autocomment" title="Special:BlankPage">→autocomment</a></span></span>',
|
||||||
|
"/* autocomment */",
|
||||||
|
false, false
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'<span dir="auto"><span class="autocomment"><a class="external" rel="nofollow" href="//en.example.org/w/Special:BlankPage#autocomment">→autocomment</a></span></span>',
|
||||||
|
"/* autocomment */",
|
||||||
|
false, false, $wikiId
|
||||||
|
],
|
||||||
|
// Linker::formatLinksInComment
|
||||||
|
[
|
||||||
|
'abc <a href="/wiki/index.php?title=Link&action=edit&redlink=1" class="new" title="Link (page does not exist)">link</a> def',
|
||||||
|
"abc [[link]] def",
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'abc <a href="/wiki/index.php?title=Link&action=edit&redlink=1" class="new" title="Link (page does not exist)">text</a> def',
|
||||||
|
"abc [[link|text]] def",
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'abc <a href="/wiki/Special:BlankPage" title="Special:BlankPage">Special:BlankPage</a> def',
|
||||||
|
"abc [[Special:BlankPage|]] def",
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'abc <a href="/wiki/index.php?title=%C4%84%C5%9B%C5%BC&action=edit&redlink=1" class="new" title="Ąśż (page does not exist)">ąśż</a> def',
|
||||||
|
"abc [[%C4%85%C5%9B%C5%BC]] def",
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'abc <a href="/wiki/Special:BlankPage#section" title="Special:BlankPage">#section</a> def',
|
||||||
|
"abc [[#section]] def",
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'abc <a href="/wiki/index.php?title=/subpage&action=edit&redlink=1" class="new" title="/subpage (page does not exist)">/subpage</a> def',
|
||||||
|
"abc [[/subpage]] def",
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'abc <a href="/wiki/index.php?title=%22evil!%22&action=edit&redlink=1" class="new" title=""evil!" (page does not exist)">"evil!"</a> def',
|
||||||
|
"abc [[\"evil!\"]] def",
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'abc [[<script>very evil</script>]] def',
|
||||||
|
"abc [[<script>very evil</script>]] def",
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'abc [[|]] def',
|
||||||
|
"abc [[|]] def",
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'abc <a href="/wiki/index.php?title=Link&action=edit&redlink=1" class="new" title="Link (page does not exist)">link</a> def',
|
||||||
|
"abc [[link]] def",
|
||||||
|
false, false
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'abc <a class="external" rel="nofollow" href="//en.example.org/w/Link">link</a> def',
|
||||||
|
"abc [[link]] def",
|
||||||
|
false, false, $wikiId
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'<a href="/wiki/index.php?title=Special:Upload&wpDestFile=LinkerTest.jpg" class="new" title="LinkerTest.jpg">Media:LinkerTest.jpg</a>',
|
||||||
|
'[[Media:LinkerTest.jpg]]'
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'<a href="/wiki/Special:BlankPage" title="Special:BlankPage">Special:BlankPage</a>',
|
||||||
|
'[[:Special:BlankPage]]'
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'<a href="/wiki/index.php?title=Link&action=edit&redlink=1" class="new" title="Link (page does not exist)">linktrail</a>...',
|
||||||
|
'[[link]]trail...'
|
||||||
|
]
|
||||||
|
];
|
||||||
|
// phpcs:enable
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adapted from LinkerTest
|
||||||
|
*
|
||||||
|
* @dataProvider provideFormatComment
|
||||||
|
*/
|
||||||
|
public function testFormatComment(
|
||||||
|
$expected, $comment, $title = false, $local = false, $wikiId = null
|
||||||
|
) {
|
||||||
|
$conf = new SiteConfiguration();
|
||||||
|
$conf->settings = [
|
||||||
|
'wgServer' => [
|
||||||
|
'enwiki' => '//en.example.org',
|
||||||
|
'dewiki' => '//de.example.org',
|
||||||
|
],
|
||||||
|
'wgArticlePath' => [
|
||||||
|
'enwiki' => '/w/$1',
|
||||||
|
'dewiki' => '/w/$1',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
$conf->suffixes = [ 'wiki' ];
|
||||||
|
|
||||||
|
$this->setMwGlobals( [
|
||||||
|
'wgScript' => '/wiki/index.php',
|
||||||
|
'wgArticlePath' => '/wiki/$1',
|
||||||
|
'wgCapitalLinks' => true,
|
||||||
|
'wgConf' => $conf,
|
||||||
|
// TODO: update tests when the default changes
|
||||||
|
'wgFragmentMode' => [ 'legacy' ],
|
||||||
|
] );
|
||||||
|
|
||||||
|
if ( $title === false ) {
|
||||||
|
// We need a page title that exists
|
||||||
|
$title = Title::newFromText( 'Special:BlankPage' );
|
||||||
|
}
|
||||||
|
|
||||||
|
$parser = $this->getParser();
|
||||||
|
$result = $parser->finalize(
|
||||||
|
$parser->preprocess(
|
||||||
|
$comment,
|
||||||
|
$title,
|
||||||
|
$local,
|
||||||
|
$wikiId
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals( $expected, $result );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adapted from LinkerTest
|
||||||
|
*/
|
||||||
|
public static function provideFormatLinksInComment() {
|
||||||
|
// phpcs:disable Generic.Files.LineLength
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
'foo bar <a href="/wiki/Special:BlankPage" title="Special:BlankPage">Special:BlankPage</a>',
|
||||||
|
'foo bar [[Special:BlankPage]]',
|
||||||
|
null,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'<a href="/wiki/Special:BlankPage" title="Special:BlankPage">Special:BlankPage</a>',
|
||||||
|
'[[ :Special:BlankPage]]',
|
||||||
|
null,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'[[Foo<a href="/wiki/Special:BlankPage" title="Special:BlankPage">Special:BlankPage</a>',
|
||||||
|
'[[Foo[[Special:BlankPage]]',
|
||||||
|
null,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'<a class="external" rel="nofollow" href="//en.example.org/w/Foo%27bar">Foo'bar</a>',
|
||||||
|
"[[Foo'bar]]",
|
||||||
|
'enwiki',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'foo bar <a class="external" rel="nofollow" href="//en.example.org/w/Special:BlankPage">Special:BlankPage</a>',
|
||||||
|
'foo bar [[Special:BlankPage]]',
|
||||||
|
'enwiki',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'foo bar <a class="external" rel="nofollow" href="//en.example.org/w/File:Example">Image:Example</a>',
|
||||||
|
'foo bar [[Image:Example]]',
|
||||||
|
'enwiki',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
// phpcs:enable
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adapted from LinkerTest. Note that we test the new HTML escaping variant.
|
||||||
|
*
|
||||||
|
* @dataProvider provideFormatLinksInComment
|
||||||
|
*/
|
||||||
|
public function testFormatLinksInComment( $expected, $input, $wiki ) {
|
||||||
|
$conf = new SiteConfiguration();
|
||||||
|
$conf->settings = [
|
||||||
|
'wgServer' => [
|
||||||
|
'enwiki' => '//en.example.org'
|
||||||
|
],
|
||||||
|
'wgArticlePath' => [
|
||||||
|
'enwiki' => '/w/$1',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
$conf->suffixes = [ 'wiki' ];
|
||||||
|
$this->setMwGlobals( [
|
||||||
|
'wgScript' => '/wiki/index.php',
|
||||||
|
'wgArticlePath' => '/wiki/$1',
|
||||||
|
'wgCapitalLinks' => true,
|
||||||
|
'wgConf' => $conf,
|
||||||
|
] );
|
||||||
|
|
||||||
|
$parser = $this->getParser();
|
||||||
|
$title = Title::newFromText( 'Special:BlankPage' );
|
||||||
|
$result = $parser->finalize(
|
||||||
|
$parser->preprocess(
|
||||||
|
$input, $title, false, $wiki, false
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals( $expected, $result );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testLinkCacheInteraction() {
|
||||||
|
$this->tablesUsed[] = 'page';
|
||||||
|
$services = $this->getServiceContainer();
|
||||||
|
$present = Title::newFromText( 'Present' );
|
||||||
|
$absent = Title::newFromText( 'Absent' );
|
||||||
|
$this->editPage(
|
||||||
|
$present,
|
||||||
|
'content'
|
||||||
|
);
|
||||||
|
$parser = $this->getParser();
|
||||||
|
$linkCache = $services->getLinkCache();
|
||||||
|
$result = $parser->finalize( [
|
||||||
|
$parser->preprocess( "[[$present]]" ),
|
||||||
|
$parser->preprocess( "[[$absent]]" )
|
||||||
|
] );
|
||||||
|
$expected = [
|
||||||
|
'<a href="/index.php/Present" title="Present">Present</a>',
|
||||||
|
// phpcs:ignore Generic.Files.LineLength
|
||||||
|
'<a href="/index.php?title=Absent&action=edit&redlink=1" class="new" title="Absent (page does not exist)">Absent</a>'
|
||||||
|
];
|
||||||
|
$this->assertSame( $expected, $result );
|
||||||
|
$this->assertGreaterThan( 0, $linkCache->getGoodLinkID( $present ) );
|
||||||
|
$this->assertTrue( $linkCache->isBadLink( $absent ) );
|
||||||
|
|
||||||
|
// Run the comment batch again and confirm that LinkBatch does not need
|
||||||
|
// to execute a query. This is a CommentParser responsibility since
|
||||||
|
// LinkBatch does not provide a transparent read-through cache.
|
||||||
|
// TODO: Generic $this->assertQueryCount() would do the job.
|
||||||
|
$lbf = $services->getDBLoadBalancerFactory();
|
||||||
|
$fakeLB = $lbf->newMainLB();
|
||||||
|
$fakeLB->disable( __METHOD__ );
|
||||||
|
$linkBatchFactory = new LinkBatchFactory(
|
||||||
|
$services->getLinkCache(),
|
||||||
|
$services->getTitleFormatter(),
|
||||||
|
$services->getContentLanguage(),
|
||||||
|
$services->getGenderCache(),
|
||||||
|
$fakeLB
|
||||||
|
);
|
||||||
|
$parser = new CommentParser(
|
||||||
|
$services->getLinkRenderer(),
|
||||||
|
$linkBatchFactory,
|
||||||
|
$linkCache,
|
||||||
|
$services->getRepoGroup(),
|
||||||
|
$services->getContentLanguage(),
|
||||||
|
$services->getContentLanguage(),
|
||||||
|
$services->getTitleParser(),
|
||||||
|
$services->getNamespaceInfo(),
|
||||||
|
$services->getHookContainer()
|
||||||
|
);
|
||||||
|
$result = $parser->finalize( [
|
||||||
|
$parser->preprocess( "[[$present]]" ),
|
||||||
|
$parser->preprocess( "[[$absent]]" )
|
||||||
|
] );
|
||||||
|
$this->assertSame( $expected, $result );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,141 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MediaWiki\Tests\Integration\CommentFormatter;
|
||||||
|
|
||||||
|
use MediaWiki\CommentFormatter\CommentParser;
|
||||||
|
use MediaWiki\CommentFormatter\CommentParserFactory;
|
||||||
|
use MediaWiki\CommentFormatter\RowCommentFormatter;
|
||||||
|
use MediaWiki\Linker\LinkTarget;
|
||||||
|
use MediaWiki\Tests\Unit\CommentFormatter\CommentFormatterTestUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \MediaWiki\CommentFormatter\RowCommentFormatter
|
||||||
|
* @covers \MediaWiki\CommentFormatter\RowCommentIterator
|
||||||
|
*/
|
||||||
|
class RowCommentFormatterTest extends \MediaWikiIntegrationTestCase {
|
||||||
|
private function getParser() {
|
||||||
|
return new class extends CommentParser {
|
||||||
|
public function __construct() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public function preprocess(
|
||||||
|
string $comment, LinkTarget $selfLinkTarget = null, $samePage = false,
|
||||||
|
$wikiId = null, $enableSectionLinks = true
|
||||||
|
) {
|
||||||
|
if ( $comment === '' || $comment === '*' ) {
|
||||||
|
return $comment; // Hack to make it work more like the real parser
|
||||||
|
}
|
||||||
|
return CommentFormatterTestUtils::dumpArray( [
|
||||||
|
'comment' => $comment,
|
||||||
|
'selfLinkTarget' => $selfLinkTarget,
|
||||||
|
'samePage' => $samePage,
|
||||||
|
'wikiId' => $wikiId,
|
||||||
|
'enableSectionLinks' => $enableSectionLinks
|
||||||
|
] );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getParserFactory() {
|
||||||
|
$parser = $this->getParser();
|
||||||
|
return new class( $parser ) extends CommentParserFactory {
|
||||||
|
private $parser;
|
||||||
|
|
||||||
|
public function __construct( $parser ) {
|
||||||
|
$this->parser = $parser;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function create() {
|
||||||
|
return $this->parser;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private function newCommentFormatter() {
|
||||||
|
return new RowCommentFormatter(
|
||||||
|
$this->getParserFactory(),
|
||||||
|
$this->getServiceContainer()->getCommentStore()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFormatRows() {
|
||||||
|
$rows = [
|
||||||
|
(object)[
|
||||||
|
'comment_text' => 'hello',
|
||||||
|
'comment_data' => null,
|
||||||
|
'namespace' => '0',
|
||||||
|
'title' => 'Page',
|
||||||
|
'id' => 1
|
||||||
|
]
|
||||||
|
];
|
||||||
|
$commentFormatter = $this->newCommentFormatter();
|
||||||
|
$result = $commentFormatter->formatRows(
|
||||||
|
$rows,
|
||||||
|
'comment',
|
||||||
|
'namespace',
|
||||||
|
'title',
|
||||||
|
'id'
|
||||||
|
);
|
||||||
|
$this->assertSame(
|
||||||
|
[
|
||||||
|
1 => 'comment=hello, selfLinkTarget=0:Page, !samePage, !wikiId, enableSectionLinks'
|
||||||
|
],
|
||||||
|
$result
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRowsWithFormatItems() {
|
||||||
|
$rows = [
|
||||||
|
(object)[
|
||||||
|
'comment_text' => 'hello',
|
||||||
|
'comment_data' => null,
|
||||||
|
'namespace' => '0',
|
||||||
|
'title' => 'Page',
|
||||||
|
'id' => 1
|
||||||
|
]
|
||||||
|
];
|
||||||
|
$commentFormatter = $this->newCommentFormatter();
|
||||||
|
$result = $commentFormatter->formatItems(
|
||||||
|
$commentFormatter->rows( $rows )
|
||||||
|
->commentKey( 'comment' )
|
||||||
|
->namespaceField( 'namespace' )
|
||||||
|
->titleField( 'title' )
|
||||||
|
->indexField( 'id' )
|
||||||
|
);
|
||||||
|
$this->assertSame(
|
||||||
|
[
|
||||||
|
1 => 'comment=hello, selfLinkTarget=0:Page, !samePage, !wikiId, enableSectionLinks'
|
||||||
|
],
|
||||||
|
$result
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRowsWithCreateBatch() {
|
||||||
|
$rows = [
|
||||||
|
(object)[
|
||||||
|
'comment_text' => 'hello',
|
||||||
|
'comment_data' => null,
|
||||||
|
'namespace' => '0',
|
||||||
|
'title' => 'Page',
|
||||||
|
'id' => 1
|
||||||
|
]
|
||||||
|
];
|
||||||
|
$commentFormatter = $this->newCommentFormatter();
|
||||||
|
$result = $commentFormatter->createBatch()
|
||||||
|
->comments(
|
||||||
|
$commentFormatter->rows( $rows )
|
||||||
|
->commentKey( 'comment' )
|
||||||
|
->namespaceField( 'namespace' )
|
||||||
|
->titleField( 'title' )
|
||||||
|
->indexField( 'id' )
|
||||||
|
)
|
||||||
|
->samePage( true )
|
||||||
|
->execute();
|
||||||
|
$this->assertSame(
|
||||||
|
[
|
||||||
|
1 => 'comment=hello, selfLinkTarget=0:Page, samePage, !wikiId, enableSectionLinks'
|
||||||
|
],
|
||||||
|
$result
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,321 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MediaWiki\Tests\Unit\CommentFormatter;
|
||||||
|
|
||||||
|
use MediaWiki\CommentFormatter\CommentBatch;
|
||||||
|
use MediaWiki\CommentFormatter\CommentFormatter;
|
||||||
|
use MediaWiki\CommentFormatter\CommentItem;
|
||||||
|
use MediaWikiUnitTestCase;
|
||||||
|
use TitleValue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trivial unit test with the universe mocked.
|
||||||
|
*
|
||||||
|
* @covers \MediaWiki\CommentFormatter\CommentBatch
|
||||||
|
* @covers \MediaWiki\CommentFormatter\CommentItem
|
||||||
|
* @covers \MediaWiki\CommentFormatter\StringCommentIterator
|
||||||
|
*/
|
||||||
|
class CommentBatchTest extends MediaWikiUnitTestCase {
|
||||||
|
private $calls;
|
||||||
|
|
||||||
|
private function getFormatter() {
|
||||||
|
return new class( $this->calls ) extends CommentFormatter {
|
||||||
|
private $calls;
|
||||||
|
|
||||||
|
public function __construct( &$calls ) {
|
||||||
|
$this->calls =& $calls;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function formatItemsInternal(
|
||||||
|
$items, $selfLinkTarget = null, $samePage = null, $wikiId = null,
|
||||||
|
$enableSectionLinks = null, $useBlock = null, $useParentheses = null
|
||||||
|
) {
|
||||||
|
$paramDump = CommentFormatterTestUtils::dumpArray( [
|
||||||
|
'selfLinkTarget' => $selfLinkTarget,
|
||||||
|
'samePage' => $samePage,
|
||||||
|
'wikiId' => $wikiId,
|
||||||
|
'enableSectionLinks' => $enableSectionLinks,
|
||||||
|
'useBlock' => $useBlock,
|
||||||
|
'useParentheses' => $useParentheses,
|
||||||
|
] );
|
||||||
|
if ( $paramDump !== '' ) {
|
||||||
|
$lines = [ $paramDump ];
|
||||||
|
}
|
||||||
|
/** @var CommentItem $item */
|
||||||
|
foreach ( $items as $i => $item ) {
|
||||||
|
$lines[] = "$i. " . CommentFormatterTestUtils::dumpArray( [
|
||||||
|
'comment' => $item->comment,
|
||||||
|
'selfLinkTarget' => $item->selfLinkTarget,
|
||||||
|
'samePage' => $item->samePage,
|
||||||
|
'wikiId' => $item->wikiId,
|
||||||
|
] );
|
||||||
|
}
|
||||||
|
$this->calls[] = $lines;
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private function newBatch() {
|
||||||
|
return new CommentBatch( $this->getFormatter() );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testComments() {
|
||||||
|
$this->newBatch()
|
||||||
|
->comments( [ new CommentItem( 'c' ) ] )
|
||||||
|
->execute();
|
||||||
|
$this->assertSame(
|
||||||
|
[
|
||||||
|
[ '0. comment=c' ],
|
||||||
|
],
|
||||||
|
$this->calls
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testStrings() {
|
||||||
|
$this->newBatch()
|
||||||
|
->strings( [
|
||||||
|
1 => 'one',
|
||||||
|
3 => 'three',
|
||||||
|
5 => 'five',
|
||||||
|
] )
|
||||||
|
->execute();
|
||||||
|
$this->assertSame(
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'1. comment=one',
|
||||||
|
'3. comment=three',
|
||||||
|
'5. comment=five'
|
||||||
|
],
|
||||||
|
],
|
||||||
|
$this->calls
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function provideUseBlock() {
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
null,
|
||||||
|
[ '0. comment=c' ]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
false,
|
||||||
|
[ '!useBlock', '0. comment=c' ]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
true,
|
||||||
|
[ 'useBlock', '0. comment=c' ]
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @dataProvider provideUseBlock */
|
||||||
|
public function testUseBlock( $useBlock, $expected ) {
|
||||||
|
$batch = $this->newBatch()
|
||||||
|
->strings( [ 'c' ] );
|
||||||
|
if ( $useBlock !== null ) {
|
||||||
|
$batch->useBlock( $useBlock );
|
||||||
|
}
|
||||||
|
$batch->execute();
|
||||||
|
$this->assertSame( $this->calls, [ $expected ] );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function provideUseParentheses() {
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
null,
|
||||||
|
[ '0. comment=c' ]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
false,
|
||||||
|
[ '!useParentheses', '0. comment=c' ]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
true,
|
||||||
|
[ 'useParentheses', '0. comment=c' ]
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @dataProvider provideUseParentheses */
|
||||||
|
public function testUseParentheses( $useParentheses, $expected ) {
|
||||||
|
$batch = $this->newBatch()
|
||||||
|
->strings( [ 'c' ] );
|
||||||
|
if ( $useParentheses !== null ) {
|
||||||
|
$batch->useParentheses( $useParentheses );
|
||||||
|
}
|
||||||
|
$batch->execute();
|
||||||
|
$this->assertSame( $this->calls, [ $expected ] );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function provideSelfLinkTarget() {
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
null,
|
||||||
|
[ '0. comment=c' ]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[ 0, 'Page' ],
|
||||||
|
[ 'selfLinkTarget=0:Page', '0. comment=c' ]
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @dataProvider provideSelfLinkTarget */
|
||||||
|
public function testSelfLinkTarget( $selfLinkTarget, $expected ) {
|
||||||
|
$batch = $this->newBatch()
|
||||||
|
->strings( [ 'c' ] );
|
||||||
|
if ( $selfLinkTarget !== null ) {
|
||||||
|
$batch->selfLinkTarget( new TitleValue( $selfLinkTarget[0], $selfLinkTarget[1] ) );
|
||||||
|
}
|
||||||
|
$batch->execute();
|
||||||
|
$this->assertSame( $this->calls, [ $expected ] );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function provideEnableSectionLinks() {
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
null,
|
||||||
|
[ '0. comment=c' ]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
false,
|
||||||
|
[ '!enableSectionLinks', '0. comment=c' ]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
true,
|
||||||
|
[ 'enableSectionLinks', '0. comment=c' ]
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @dataProvider provideEnableSectionLinks */
|
||||||
|
public function testEnableSectionLinks( $enableSectionLinks, $expected ) {
|
||||||
|
$batch = $this->newBatch()
|
||||||
|
->strings( [ 'c' ] );
|
||||||
|
if ( $enableSectionLinks !== null ) {
|
||||||
|
$batch->enableSectionLinks( $enableSectionLinks );
|
||||||
|
}
|
||||||
|
$batch->execute();
|
||||||
|
$this->assertSame( $this->calls, [ $expected ] );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function provideDisableSectionLinks() {
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
null,
|
||||||
|
[ '0. comment=c' ]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
true,
|
||||||
|
[ '!enableSectionLinks', '0. comment=c' ]
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @dataProvider provideDisableSectionLinks */
|
||||||
|
public function testDisableSectionLinks( $disableSectionLinks, $expected ) {
|
||||||
|
$batch = $this->newBatch()
|
||||||
|
->strings( [ 'c' ] );
|
||||||
|
if ( $disableSectionLinks !== null ) {
|
||||||
|
$batch->disableSectionLinks();
|
||||||
|
}
|
||||||
|
$batch->execute();
|
||||||
|
$this->assertSame( $this->calls, [ $expected ] );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function provideSamePage() {
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
null,
|
||||||
|
[ '0. comment=c' ]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
false,
|
||||||
|
[ '!samePage', '0. comment=c' ]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
true,
|
||||||
|
[ 'samePage', '0. comment=c' ]
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @dataProvider provideSamePage */
|
||||||
|
public function testSamePage( $samePage, $expected ) {
|
||||||
|
$batch = $this->newBatch()
|
||||||
|
->strings( [ 'c' ] );
|
||||||
|
if ( $samePage !== null ) {
|
||||||
|
$batch->samePage( $samePage );
|
||||||
|
}
|
||||||
|
$batch->execute();
|
||||||
|
$this->assertSame( $this->calls, [ $expected ] );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function provideWikiId() {
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
null,
|
||||||
|
[ '0. comment=c' ]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'enwiki',
|
||||||
|
[ 'wikiId=enwiki', '0. comment=c' ]
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @dataProvider provideWikiId */
|
||||||
|
public function testWikiId( $wikiId, $expected ) {
|
||||||
|
$batch = $this->newBatch()
|
||||||
|
->strings( [ 'c' ] );
|
||||||
|
if ( $wikiId !== null ) {
|
||||||
|
$batch->wikiId( $wikiId );
|
||||||
|
}
|
||||||
|
$batch->execute();
|
||||||
|
$this->assertSame( $this->calls, [ $expected ] );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testItemSelfLinkTarget() {
|
||||||
|
$item = ( new CommentItem( 'c' ) )
|
||||||
|
->selfLinkTarget( new TitleValue( 0, 'Page' ) );
|
||||||
|
$this->newBatch()
|
||||||
|
->comments( [ $item ] )
|
||||||
|
->execute();
|
||||||
|
$this->assertSame(
|
||||||
|
[
|
||||||
|
[ '0. comment=c, selfLinkTarget=0:Page' ],
|
||||||
|
],
|
||||||
|
$this->calls
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testItemSamePage() {
|
||||||
|
$item = ( new CommentItem( 'c' ) )
|
||||||
|
->samePage();
|
||||||
|
$this->newBatch()
|
||||||
|
->comments( [ $item ] )
|
||||||
|
->execute();
|
||||||
|
$this->assertSame(
|
||||||
|
[
|
||||||
|
[ '0. comment=c, samePage' ],
|
||||||
|
],
|
||||||
|
$this->calls
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testItemWikiId() {
|
||||||
|
$item = ( new CommentItem( 'c' ) )
|
||||||
|
->wikiId( 'enwiki' );
|
||||||
|
$this->newBatch()
|
||||||
|
->comments( [ $item ] )
|
||||||
|
->execute();
|
||||||
|
$this->assertSame(
|
||||||
|
[
|
||||||
|
[ '0. comment=c, wikiId=enwiki' ],
|
||||||
|
],
|
||||||
|
$this->calls
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MediaWiki\Tests\Unit\CommentFormatter;
|
||||||
|
|
||||||
|
class CommentFormatterTestUtils {
|
||||||
|
public static function dumpArray( $a ) {
|
||||||
|
$s = '';
|
||||||
|
foreach ( $a as $k => $v ) {
|
||||||
|
if ( $v === null ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ( $s !== '' ) {
|
||||||
|
$s .= ', ';
|
||||||
|
}
|
||||||
|
if ( $v === true ) {
|
||||||
|
$s .= $k;
|
||||||
|
} elseif ( $v === false ) {
|
||||||
|
$s .= "!$k";
|
||||||
|
} else {
|
||||||
|
$s .= "$k=$v";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,135 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MediaWiki\Tests\Unit\CommentFormatter;
|
||||||
|
|
||||||
|
use ArrayIterator;
|
||||||
|
use MediaWiki\CommentFormatter\CommentFormatter;
|
||||||
|
use MediaWiki\CommentFormatter\RevisionCommentBatch;
|
||||||
|
use MediaWiki\Permissions\Authority;
|
||||||
|
use MediaWiki\Permissions\SimpleAuthority;
|
||||||
|
use MediaWiki\User\UserIdentityValue;
|
||||||
|
use MediaWikiUnitTestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trivial unit test with the universe mocked.
|
||||||
|
*
|
||||||
|
* @covers \MediaWiki\CommentFormatter\RevisionCommentBatch
|
||||||
|
*/
|
||||||
|
class RevisionCommentBatchTest extends MediaWikiUnitTestCase {
|
||||||
|
private function getFormatter( $callback ) {
|
||||||
|
return new class( $callback ) extends CommentFormatter {
|
||||||
|
private $callback;
|
||||||
|
|
||||||
|
public function __construct( $callback ) {
|
||||||
|
$this->callback = $callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function formatRevisions(
|
||||||
|
$revisions, Authority $authority, $samePage = false, $isPublic = false,
|
||||||
|
$useParentheses = true, $indexById = false
|
||||||
|
) {
|
||||||
|
( $this->callback )( get_defined_vars() );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private function newBatch( $callback ) {
|
||||||
|
return new RevisionCommentBatch(
|
||||||
|
$this->getFormatter( $callback )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getAuthority() {
|
||||||
|
return new SimpleAuthority(
|
||||||
|
new UserIdentityValue( 1, 'Sysop' ),
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAuthority() {
|
||||||
|
$authority = $this->getAuthority();
|
||||||
|
$batch = $this->newBatch(
|
||||||
|
function ( $params ) use ( $authority ) {
|
||||||
|
$this->assertSame( $authority, $params['authority'] );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
$batch->authority( $authority )->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testNoAuthority() {
|
||||||
|
$this->expectException( \TypeError::class );
|
||||||
|
$batch = $this->newBatch(
|
||||||
|
static function ( $params ) {
|
||||||
|
}
|
||||||
|
);
|
||||||
|
$batch
|
||||||
|
->revisions( [] )
|
||||||
|
->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRevisions() {
|
||||||
|
$revisions = new ArrayIterator( [] );
|
||||||
|
$batch = $this->newBatch(
|
||||||
|
function ( $params ) use ( $revisions ) {
|
||||||
|
$this->assertSame( $revisions, $params['revisions'] );
|
||||||
|
// Check default booleans while we have them
|
||||||
|
$this->assertFalse( $params['samePage'] );
|
||||||
|
$this->assertFalse( $params['isPublic'] );
|
||||||
|
$this->assertFalse( $params['useParentheses'] );
|
||||||
|
$this->assertFalse( $params['indexById'] );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
$batch
|
||||||
|
->authority( $this->getAuthority() )
|
||||||
|
->revisions( $revisions )
|
||||||
|
->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSamePage() {
|
||||||
|
$batch = $this->newBatch(
|
||||||
|
function ( $params ) {
|
||||||
|
$this->assertTrue( $params['samePage'] );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
$batch
|
||||||
|
->authority( $this->getAuthority() )
|
||||||
|
->samePage()
|
||||||
|
->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUseParentheses() {
|
||||||
|
$batch = $this->newBatch(
|
||||||
|
function ( $params ) {
|
||||||
|
$this->assertTrue( $params['useParentheses'] );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
$batch
|
||||||
|
->authority( $this->getAuthority() )
|
||||||
|
->useParentheses()
|
||||||
|
->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hideIfPrivate() {
|
||||||
|
$batch = $this->newBatch(
|
||||||
|
function ( $params ) {
|
||||||
|
$this->assertTrue( $params['isPublic'] );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
$batch
|
||||||
|
->authority( $this->getAuthority() )
|
||||||
|
->hideIfDeleted()
|
||||||
|
->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function indexById() {
|
||||||
|
$batch = $this->newBatch(
|
||||||
|
function ( $params ) {
|
||||||
|
$this->assertTrue( $params['indexById'] );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
$batch
|
||||||
|
->authority( $this->getAuthority() )
|
||||||
|
->indexById()
|
||||||
|
->execute();
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue