2018-08-16 15:45:10 +00:00
|
|
|
<?php
|
2019-05-28 14:04:23 +00:00
|
|
|
|
2023-12-11 14:59:55 +00:00
|
|
|
use MediaWiki\CommentStore\CommentStoreComment;
|
2024-08-06 13:40:20 +00:00
|
|
|
use MediaWiki\Content\WikitextContent;
|
2024-02-08 14:56:54 +00:00
|
|
|
use MediaWiki\Context\DerivativeContext;
|
|
|
|
|
use MediaWiki\Context\RequestContext;
|
2022-08-01 19:14:41 +00:00
|
|
|
use MediaWiki\MainConfigNames;
|
2023-09-05 17:31:53 +00:00
|
|
|
use MediaWiki\Output\OutputPage;
|
2024-06-13 21:11:26 +00:00
|
|
|
use MediaWiki\Parser\ParserOutput;
|
2022-10-28 10:04:25 +00:00
|
|
|
use MediaWiki\Request\FauxRequest;
|
2018-09-20 17:29:04 +00:00
|
|
|
use MediaWiki\Revision\MutableRevisionRecord;
|
|
|
|
|
use MediaWiki\Revision\RevisionRecord;
|
|
|
|
|
use MediaWiki\Revision\SlotRecord;
|
2023-08-25 12:29:41 +00:00
|
|
|
use MediaWiki\Status\Status;
|
2023-03-01 20:33:26 +00:00
|
|
|
use MediaWiki\Title\Title;
|
2023-09-19 12:13:45 +00:00
|
|
|
use MediaWiki\User\User;
|
2023-08-19 03:35:06 +00:00
|
|
|
use MediaWiki\Utils\MWTimestamp;
|
2018-08-16 15:45:10 +00:00
|
|
|
use PHPUnit\Framework\MockObject\MockObject;
|
2020-11-05 19:31:26 +00:00
|
|
|
use Wikimedia\TestingAccessWrapper;
|
2018-08-16 15:45:10 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @covers \Article::view()
|
2022-05-27 16:38:32 +00:00
|
|
|
* @group Database
|
2018-08-16 15:45:10 +00:00
|
|
|
*/
|
2020-06-30 15:09:24 +00:00
|
|
|
class ArticleViewTest extends MediaWikiIntegrationTestCase {
|
2018-08-16 15:45:10 +00:00
|
|
|
|
2021-07-22 03:11:47 +00:00
|
|
|
protected function setUp(): void {
|
2018-08-16 15:45:10 +00:00
|
|
|
parent::setUp();
|
|
|
|
|
|
|
|
|
|
$this->setUserLang( 'qqx' );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function getHtml( OutputPage $output ) {
|
|
|
|
|
return preg_replace( '/<!--.*?-->/s', '', $output->getHTML() );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param string|Title $title
|
|
|
|
|
* @param Content[]|string[] $revisionContents Content of the revisions to create
|
|
|
|
|
* (as Content or string).
|
|
|
|
|
* @param RevisionRecord[] &$revisions will be filled with the RevisionRecord for $content.
|
|
|
|
|
*
|
|
|
|
|
* @return WikiPage
|
|
|
|
|
*/
|
|
|
|
|
private function getPage( $title, array $revisionContents = [], array &$revisions = [] ) {
|
|
|
|
|
if ( is_string( $title ) ) {
|
|
|
|
|
$title = Title::makeTitle( $this->getDefaultWikitextNS(), $title );
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-20 15:51:22 +00:00
|
|
|
$page = $this->getServiceContainer()->getWikiPageFactory()->newFromTitle( $title );
|
2018-08-16 15:45:10 +00:00
|
|
|
|
|
|
|
|
$user = $this->getTestUser()->getUser();
|
|
|
|
|
|
2021-05-12 13:28:39 +00:00
|
|
|
// Make sure all revision have different timestamps all the time,
|
|
|
|
|
// to make timestamp asserts below deterministic.
|
|
|
|
|
$time = time() - 86400;
|
|
|
|
|
MWTimestamp::setFakeTime( $time );
|
|
|
|
|
|
2018-08-16 15:45:10 +00:00
|
|
|
foreach ( $revisionContents as $key => $cont ) {
|
|
|
|
|
if ( is_string( $cont ) ) {
|
|
|
|
|
$cont = new WikitextContent( $cont );
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-04 17:10:37 +00:00
|
|
|
$rev = $page->newPageUpdater( $user )
|
|
|
|
|
->setContent( SlotRecord::MAIN, $cont )
|
|
|
|
|
->saveRevision( CommentStoreComment::newUnsavedComment( 'Rev ' . $key ) );
|
2018-08-16 15:45:10 +00:00
|
|
|
|
|
|
|
|
$revisions[ $key ] = $rev;
|
2021-05-12 13:28:39 +00:00
|
|
|
MWTimestamp::setFakeTime( ++$time );
|
2018-08-16 15:45:10 +00:00
|
|
|
}
|
2021-05-12 13:28:39 +00:00
|
|
|
MWTimestamp::setFakeTime( false );
|
2018-08-16 15:45:10 +00:00
|
|
|
|
2021-01-04 13:35:38 +00:00
|
|
|
// Clear content model cache to support tests that mock the revision
|
|
|
|
|
$this->getServiceContainer()->getMainWANObjectCache()->clearProcessCache();
|
|
|
|
|
|
2018-08-16 15:45:10 +00:00
|
|
|
return $page;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2024-02-16 18:04:47 +00:00
|
|
|
* @covers \Article::getOldId()
|
|
|
|
|
* @covers \Article::getRevIdFetched()
|
2018-08-16 15:45:10 +00:00
|
|
|
*/
|
|
|
|
|
public function testGetOldId() {
|
|
|
|
|
$revisions = [];
|
|
|
|
|
$page = $this->getPage( __METHOD__, [ 1 => 'Test A', 2 => 'Test B' ], $revisions );
|
|
|
|
|
|
|
|
|
|
$idA = $revisions[1]->getId();
|
|
|
|
|
$idB = $revisions[2]->getId();
|
|
|
|
|
|
|
|
|
|
// oldid in constructor
|
|
|
|
|
$article = new Article( $page->getTitle(), $idA );
|
|
|
|
|
$this->assertSame( $idA, $article->getOldID() );
|
2021-04-21 04:35:50 +00:00
|
|
|
$article->fetchRevisionRecord();
|
2018-08-16 15:45:10 +00:00
|
|
|
$this->assertSame( $idA, $article->getRevIdFetched() );
|
|
|
|
|
|
|
|
|
|
// oldid 0 in constructor
|
|
|
|
|
$article = new Article( $page->getTitle(), 0 );
|
|
|
|
|
$this->assertSame( 0, $article->getOldID() );
|
2021-04-21 04:35:50 +00:00
|
|
|
$article->fetchRevisionRecord();
|
2018-08-16 15:45:10 +00:00
|
|
|
$this->assertSame( $idB, $article->getRevIdFetched() );
|
|
|
|
|
|
|
|
|
|
// oldid in request
|
|
|
|
|
$article = new Article( $page->getTitle() );
|
|
|
|
|
$context = new RequestContext();
|
|
|
|
|
$context->setRequest( new FauxRequest( [ 'oldid' => $idA ] ) );
|
|
|
|
|
$article->setContext( $context );
|
|
|
|
|
$this->assertSame( $idA, $article->getOldID() );
|
2021-04-21 04:35:50 +00:00
|
|
|
$article->fetchRevisionRecord();
|
2018-08-16 15:45:10 +00:00
|
|
|
$this->assertSame( $idA, $article->getRevIdFetched() );
|
|
|
|
|
|
|
|
|
|
// no oldid
|
|
|
|
|
$article = new Article( $page->getTitle() );
|
|
|
|
|
$context = new RequestContext();
|
|
|
|
|
$context->setRequest( new FauxRequest( [] ) );
|
|
|
|
|
$article->setContext( $context );
|
|
|
|
|
$this->assertSame( 0, $article->getOldID() );
|
2021-04-21 04:35:50 +00:00
|
|
|
$article->fetchRevisionRecord();
|
2018-08-16 15:45:10 +00:00
|
|
|
$this->assertSame( $idB, $article->getRevIdFetched() );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testView() {
|
|
|
|
|
$page = $this->getPage( __METHOD__, [ 1 => 'Test A', 2 => 'Test B' ] );
|
|
|
|
|
|
|
|
|
|
$article = new Article( $page->getTitle(), 0 );
|
|
|
|
|
$article->getContext()->getOutput()->setTitle( $page->getTitle() );
|
|
|
|
|
$article->view();
|
|
|
|
|
|
|
|
|
|
$output = $article->getContext()->getOutput();
|
2019-12-14 10:27:56 +00:00
|
|
|
$this->assertStringContainsString( 'Test B', $this->getHtml( $output ) );
|
|
|
|
|
$this->assertStringNotContainsString( 'id="mw-revision-info"', $this->getHtml( $output ) );
|
|
|
|
|
$this->assertStringNotContainsString( 'id="mw-revision-nav"', $this->getHtml( $output ) );
|
2018-08-16 15:45:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testViewCached() {
|
|
|
|
|
$page = $this->getPage( __METHOD__, [ 1 => 'Test A', 2 => 'Test B' ] );
|
|
|
|
|
|
|
|
|
|
$po = new ParserOutput( 'Cached Text' );
|
|
|
|
|
|
|
|
|
|
$article = new Article( $page->getTitle(), 0 );
|
|
|
|
|
$article->getContext()->getOutput()->setTitle( $page->getTitle() );
|
|
|
|
|
|
2022-01-12 20:13:39 +00:00
|
|
|
$cache = $this->getServiceContainer()->getParserCache();
|
2018-08-16 15:45:10 +00:00
|
|
|
$cache->save( $po, $page, $article->getParserOptions() );
|
|
|
|
|
|
|
|
|
|
$article->view();
|
|
|
|
|
|
|
|
|
|
$output = $article->getContext()->getOutput();
|
2019-12-14 10:27:56 +00:00
|
|
|
$this->assertStringContainsString( 'Cached Text', $this->getHtml( $output ) );
|
|
|
|
|
$this->assertStringNotContainsString( 'Test A', $this->getHtml( $output ) );
|
|
|
|
|
$this->assertStringNotContainsString( 'Test B', $this->getHtml( $output ) );
|
2018-08-16 15:45:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2024-02-16 18:04:47 +00:00
|
|
|
* @covers \Article::getPage
|
|
|
|
|
* @covers \WikiPage::getRedirectTarget
|
2023-05-05 20:16:04 +00:00
|
|
|
* @covers \MediaWiki\Page\RedirectLookup::getRedirectTarget
|
2018-08-16 15:45:10 +00:00
|
|
|
*/
|
|
|
|
|
public function testViewRedirect() {
|
|
|
|
|
$target = Title::makeTitle( $this->getDefaultWikitextNS(), 'Test_Target' );
|
|
|
|
|
$redirectText = '#REDIRECT [[' . $target->getPrefixedText() . ']]';
|
|
|
|
|
|
|
|
|
|
$page = $this->getPage( __METHOD__, [ $redirectText ] );
|
|
|
|
|
|
|
|
|
|
$article = new Article( $page->getTitle(), 0 );
|
|
|
|
|
$article->getContext()->getOutput()->setTitle( $page->getTitle() );
|
|
|
|
|
$article->view();
|
|
|
|
|
|
2021-10-20 15:51:22 +00:00
|
|
|
$redirectStore = $this->getServiceContainer()->getRedirectStore();
|
2024-05-05 10:46:56 +00:00
|
|
|
$titleFormatter = $this->getServiceContainer()->getTitleFormatter();
|
2021-10-20 15:51:22 +00:00
|
|
|
|
2018-08-16 15:45:10 +00:00
|
|
|
$this->assertNotNull(
|
2024-05-05 10:46:56 +00:00
|
|
|
$redirectStore->getRedirectTarget( $article->getPage() )
|
2018-08-16 15:45:10 +00:00
|
|
|
);
|
|
|
|
|
$this->assertSame(
|
|
|
|
|
$target->getPrefixedDBkey(),
|
2024-05-05 10:46:56 +00:00
|
|
|
$titleFormatter->getPrefixedDBkey( $redirectStore->getRedirectTarget( $article->getPage() ) )
|
2018-08-16 15:45:10 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$output = $article->getContext()->getOutput();
|
2019-12-14 10:27:56 +00:00
|
|
|
$this->assertStringContainsString( 'class="redirectText"', $this->getHtml( $output ) );
|
|
|
|
|
$this->assertStringContainsString(
|
2018-08-16 15:45:10 +00:00
|
|
|
'>' . htmlspecialchars( $target->getPrefixedText() ) . '<',
|
|
|
|
|
$this->getHtml( $output )
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testViewNonText() {
|
|
|
|
|
$dummy = $this->getPage( __METHOD__, [ 'Dummy' ] );
|
2020-04-18 02:39:58 +00:00
|
|
|
$dummyRev = $dummy->getRevisionRecord();
|
2018-08-16 15:45:10 +00:00
|
|
|
$title = $dummy->getTitle();
|
|
|
|
|
|
|
|
|
|
/** @var MockObject|ContentHandler $mockHandler */
|
|
|
|
|
$mockHandler = $this->getMockBuilder( ContentHandler::class )
|
2021-03-20 15:18:58 +00:00
|
|
|
->onlyMethods(
|
2018-08-16 15:45:10 +00:00
|
|
|
[
|
|
|
|
|
'isParserCacheSupported',
|
|
|
|
|
'serializeContent',
|
|
|
|
|
'unserializeContent',
|
|
|
|
|
'makeEmptyContent',
|
2021-09-29 14:02:36 +00:00
|
|
|
'getParserOutput',
|
2018-08-16 15:45:10 +00:00
|
|
|
]
|
|
|
|
|
)
|
|
|
|
|
->setConstructorArgs( [ 'NotText', [ 'application/frobnitz' ] ] )
|
|
|
|
|
->getMock();
|
|
|
|
|
|
|
|
|
|
$mockHandler->method( 'isParserCacheSupported' )
|
|
|
|
|
->willReturn( false );
|
2021-09-29 14:02:36 +00:00
|
|
|
$mockHandler->method( 'getParserOutput' )
|
|
|
|
|
->willReturn( new ParserOutput( 'Structured Output' ) );
|
2018-08-16 15:45:10 +00:00
|
|
|
|
|
|
|
|
$this->setTemporaryHook(
|
|
|
|
|
'ContentHandlerForModelID',
|
2021-02-07 13:10:36 +00:00
|
|
|
static function ( $id, &$handler ) use ( $mockHandler ) {
|
2018-08-16 15:45:10 +00:00
|
|
|
$handler = $mockHandler;
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
/** @var MockObject|Content $content */
|
2019-10-05 05:21:11 +00:00
|
|
|
$content = $this->createMock( Content::class );
|
2018-08-16 15:45:10 +00:00
|
|
|
$content->method( 'getModel' )
|
|
|
|
|
->willReturn( 'NotText' );
|
2019-02-07 00:14:23 +00:00
|
|
|
$content->expects( $this->never() )->method( 'getNativeData' );
|
2024-04-10 12:13:05 +00:00
|
|
|
$content->method( 'copy' )->willReturnSelf();
|
2018-08-16 15:45:10 +00:00
|
|
|
|
|
|
|
|
$rev = new MutableRevisionRecord( $title );
|
|
|
|
|
$rev->setId( $dummyRev->getId() );
|
|
|
|
|
$rev->setPageId( $title->getArticleID() );
|
|
|
|
|
$rev->setUser( $dummyRev->getUser() );
|
|
|
|
|
$rev->setComment( $dummyRev->getComment() );
|
|
|
|
|
$rev->setTimestamp( $dummyRev->getTimestamp() );
|
|
|
|
|
|
2018-09-24 21:10:08 +00:00
|
|
|
$rev->setContent( SlotRecord::MAIN, $content );
|
2018-08-16 15:45:10 +00:00
|
|
|
|
|
|
|
|
/** @var MockObject|WikiPage $page */
|
|
|
|
|
$page = $this->getMockBuilder( WikiPage::class )
|
2022-05-27 16:38:32 +00:00
|
|
|
->onlyMethods( [ 'getRevisionRecord', 'getLatest', 'getContentHandler' ] )
|
2018-08-16 15:45:10 +00:00
|
|
|
->setConstructorArgs( [ $title ] )
|
|
|
|
|
->getMock();
|
|
|
|
|
|
2020-04-18 02:39:58 +00:00
|
|
|
$page->method( 'getRevisionRecord' )
|
2018-08-16 15:45:10 +00:00
|
|
|
->willReturn( $rev );
|
|
|
|
|
$page->method( 'getLatest' )
|
|
|
|
|
->willReturn( $rev->getId() );
|
2022-05-27 16:38:32 +00:00
|
|
|
$page->method( 'getContentHandler' )
|
|
|
|
|
->willReturn( $mockHandler );
|
2018-08-16 15:45:10 +00:00
|
|
|
|
|
|
|
|
$article = Article::newFromWikiPage( $page, RequestContext::getMain() );
|
|
|
|
|
$article->getContext()->getOutput()->setTitle( $page->getTitle() );
|
|
|
|
|
$article->view();
|
|
|
|
|
|
|
|
|
|
$output = $article->getContext()->getOutput();
|
2019-12-14 10:27:56 +00:00
|
|
|
$this->assertStringContainsString( 'Structured Output', $this->getHtml( $output ) );
|
|
|
|
|
$this->assertStringNotContainsString( 'Dummy', $this->getHtml( $output ) );
|
2018-08-16 15:45:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testViewOfOldRevision() {
|
|
|
|
|
$revisions = [];
|
|
|
|
|
$page = $this->getPage( __METHOD__, [ 1 => 'Test A', 2 => 'Test B' ], $revisions );
|
|
|
|
|
$idA = $revisions[1]->getId();
|
|
|
|
|
|
|
|
|
|
$article = new Article( $page->getTitle(), $idA );
|
|
|
|
|
$article->getContext()->getOutput()->setTitle( $page->getTitle() );
|
|
|
|
|
$article->view();
|
|
|
|
|
|
|
|
|
|
$output = $article->getContext()->getOutput();
|
2019-12-14 10:27:56 +00:00
|
|
|
$this->assertStringContainsString( 'Test A', $this->getHtml( $output ) );
|
|
|
|
|
$this->assertStringContainsString( 'id="mw-revision-info"', $output->getSubtitle() );
|
|
|
|
|
$this->assertStringContainsString( 'id="mw-revision-nav"', $output->getSubtitle() );
|
2018-08-16 15:45:10 +00:00
|
|
|
|
2019-12-14 10:27:56 +00:00
|
|
|
$this->assertStringNotContainsString( 'id="revision-info-current"', $output->getSubtitle() );
|
|
|
|
|
$this->assertStringNotContainsString( 'Test B', $this->getHtml( $output ) );
|
2021-05-11 13:23:03 +00:00
|
|
|
$this->assertSame( $idA, $output->getRevisionId() );
|
2021-05-12 13:14:34 +00:00
|
|
|
$this->assertSame( $revisions[1]->getTimestamp(), $output->getRevisionTimestamp() );
|
2018-08-16 15:45:10 +00:00
|
|
|
}
|
|
|
|
|
|
2020-12-09 03:09:26 +00:00
|
|
|
public function testViewOfOldRevisionFromCache() {
|
2022-08-01 19:14:41 +00:00
|
|
|
$this->overrideConfigValues( [
|
|
|
|
|
MainConfigNames::OldRevisionParserCacheExpireTime => 100500,
|
objectcache: Remove $wgMainWANCache and $wgWANObjectCaches
We always wrap the local cluster cache, and there are no subclasses
of WANObjectCache. It was never documented or recommended how these
would be used. It is a left-over from the original 2015 Multi-DC plan
in which WANObjectCache would work differently. See task for details.
Note that this requires no configuration changes, even in the
theoretical case of these variables being used, as the only
option is to use the main cache, and that's also the default.
* Update WAN overrides to override the underlying main cache
instead.
* Fix EditPageTest which was previously implicitly using a 'hash'
as main cache but also relying on wan cache to be 'none'.
The part that it actually needs is the 'none'. When WAN cache is
enabled, testUpdateNoMinor fails due to an edit conflict because
one of the edits it makes is made with a current timestamp whereas
it expects to simulate wpEdittime in the year 2012 which, when
caching is enabled, is ignored and becomes the current time instead.
I don't understand exactly why, but I'm going to conserve that
behaviour for now.
* Fix TemplateCategoriesTest, which was failing due to an unexpected
cache hit:
> [objectcache] fetchOrRegenerate(…:page:10:…): volatile hit
This could be solved in a more realistic way by splitting the test,
or by explicitly resetting services half-way the test to clear
WikiPageFactory, PageStore and WANCache process state.
For now, keep the prior behaviour of no cache in this test.
Bug: T305093
Bug: T329680
Depends-On: If890622eed0d0f8b4bd73d36ba1815a3d760ea05
Depends-On: Ie1def75208822bdf19bb2cfd7e6edf32c2000e6b
Depends-On: I35cce61dc3ee90dcee3dd6f0b36f84133be029ed
Change-Id: I53781a8c06ebb2583f6ca83dd91bbfe8a5c88b13
2023-02-14 21:43:12 +00:00
|
|
|
MainConfigNames::MainCacheType => CACHE_HASH,
|
2020-12-09 03:09:26 +00:00
|
|
|
] );
|
2021-05-11 13:23:03 +00:00
|
|
|
|
2020-12-09 03:09:26 +00:00
|
|
|
$revisions = [];
|
|
|
|
|
$page = $this->getPage( __METHOD__, [ 1 => 'Test A', 2 => 'Test B' ], $revisions );
|
|
|
|
|
$idA = $revisions[1]->getId();
|
2024-03-10 19:25:42 +00:00
|
|
|
$context = RequestContext::getMain();
|
|
|
|
|
$context->setTitle( $page->getTitle() );
|
2020-12-09 03:09:26 +00:00
|
|
|
|
2021-05-11 13:23:03 +00:00
|
|
|
// View the revision once (to get it into the cache)
|
2020-12-09 03:09:26 +00:00
|
|
|
$article = new Article( $page->getTitle(), $idA );
|
2021-05-11 13:23:03 +00:00
|
|
|
$article->view();
|
|
|
|
|
|
|
|
|
|
// Reset the output page and view the revision again (from ParserCache)
|
|
|
|
|
$article = new Article( $page->getTitle(), $idA );
|
|
|
|
|
$context->setOutput( new OutputPage( $context ) );
|
|
|
|
|
$article->setContext( $context );
|
2020-12-09 03:09:26 +00:00
|
|
|
|
2021-05-11 13:23:03 +00:00
|
|
|
$outputPageBeforeHTMLRevisionId = null;
|
|
|
|
|
$this->setTemporaryHook( 'OutputPageBeforeHTML',
|
|
|
|
|
static function ( OutputPage $out ) use ( &$outputPageBeforeHTMLRevisionId ) {
|
|
|
|
|
$outputPageBeforeHTMLRevisionId = $out->getRevisionId();
|
|
|
|
|
}
|
|
|
|
|
);
|
2020-12-09 03:09:26 +00:00
|
|
|
|
|
|
|
|
$article->view();
|
|
|
|
|
$output = $article->getContext()->getOutput();
|
|
|
|
|
$this->assertStringContainsString( 'Test A', $this->getHtml( $output ) );
|
2023-02-28 21:48:22 +00:00
|
|
|
$this->assertSame( 1, substr_count( $output->getSubtitle(), 'cdx-message--warning' ) );
|
2020-12-17 13:22:35 +00:00
|
|
|
$this->assertSame( $idA, $output->getRevisionId() );
|
2021-05-11 13:23:03 +00:00
|
|
|
$this->assertSame( $idA, $outputPageBeforeHTMLRevisionId );
|
2021-05-12 13:14:34 +00:00
|
|
|
$this->assertSame( $revisions[1]->getTimestamp(), $output->getRevisionTimestamp() );
|
2020-12-09 03:09:26 +00:00
|
|
|
}
|
|
|
|
|
|
2018-08-16 15:45:10 +00:00
|
|
|
public function testViewOfCurrentRevision() {
|
|
|
|
|
$revisions = [];
|
|
|
|
|
$page = $this->getPage( __METHOD__, [ 1 => 'Test A', 2 => 'Test B' ], $revisions );
|
|
|
|
|
$idB = $revisions[2]->getId();
|
|
|
|
|
|
|
|
|
|
$article = new Article( $page->getTitle(), $idB );
|
|
|
|
|
$article->getContext()->getOutput()->setTitle( $page->getTitle() );
|
|
|
|
|
$article->view();
|
|
|
|
|
|
|
|
|
|
$output = $article->getContext()->getOutput();
|
2019-12-14 10:27:56 +00:00
|
|
|
$this->assertStringContainsString( 'Test B', $this->getHtml( $output ) );
|
|
|
|
|
$this->assertStringContainsString( 'id="mw-revision-info-current"', $output->getSubtitle() );
|
|
|
|
|
$this->assertStringContainsString( 'id="mw-revision-nav"', $output->getSubtitle() );
|
2018-08-16 15:45:10 +00:00
|
|
|
}
|
|
|
|
|
|
2023-08-07 00:56:10 +00:00
|
|
|
public function testViewOfCurrentRevisionDirty() {
|
|
|
|
|
$this->overrideConfigValue(
|
|
|
|
|
MainConfigNames::PoolCounterConf,
|
|
|
|
|
[
|
|
|
|
|
'ArticleView' => [
|
|
|
|
|
'class' => MockPoolCounterFailing::class,
|
|
|
|
|
]
|
|
|
|
|
]
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$revisions = [];
|
|
|
|
|
$page = $this->getPage( __METHOD__, [ 1 => 'Test A' ], $revisions );
|
|
|
|
|
$idA = $revisions[1]->getId();
|
|
|
|
|
|
|
|
|
|
// Do the next edit without ParserCache produce an outdated cache entry
|
|
|
|
|
$parserCacheFactory = $this->getServiceContainer()->getParserCacheFactory();
|
|
|
|
|
$this->overrideConfigValue( MainConfigNames::ParserCacheType, CACHE_NONE );
|
|
|
|
|
$latestEditStatus = $this->editPage( $page, 'Test B' );
|
|
|
|
|
// Restore the old cache instance with the now outdated cache entry
|
|
|
|
|
$this->setService( 'ParserCacheFactory', $parserCacheFactory );
|
|
|
|
|
|
|
|
|
|
// Request the article for the latest
|
|
|
|
|
$article = new Article( $page->getTitle() );
|
|
|
|
|
$article->getContext()->getOutput()->setTitle( $page->getTitle() );
|
|
|
|
|
$article->view();
|
|
|
|
|
|
|
|
|
|
// Expected the old values to return
|
|
|
|
|
$output = $article->getContext()->getOutput();
|
|
|
|
|
$this->assertStringContainsString( 'Test A', $this->getHtml( $output ) );
|
|
|
|
|
$this->assertSame( $idA, $output->getRevisionId() );
|
|
|
|
|
$this->assertSame( $revisions[1]->getTimestamp(), $output->getRevisionTimestamp() );
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-16 15:45:10 +00:00
|
|
|
public function testViewOfMissingRevision() {
|
|
|
|
|
$revisions = [];
|
|
|
|
|
$page = $this->getPage( __METHOD__, [ 1 => 'Test A' ], $revisions );
|
|
|
|
|
$badId = $revisions[1]->getId() + 100;
|
|
|
|
|
|
|
|
|
|
$article = new Article( $page->getTitle(), $badId );
|
|
|
|
|
$article->getContext()->getOutput()->setTitle( $page->getTitle() );
|
|
|
|
|
$article->view();
|
|
|
|
|
|
|
|
|
|
$output = $article->getContext()->getOutput();
|
2019-12-14 10:27:56 +00:00
|
|
|
$this->assertStringContainsString( 'missing-revision: ' . $badId, $this->getHtml( $output ) );
|
2018-08-16 15:45:10 +00:00
|
|
|
|
2019-12-14 10:27:56 +00:00
|
|
|
$this->assertStringNotContainsString( 'Test A', $this->getHtml( $output ) );
|
2018-08-16 15:45:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testViewOfDeletedRevision() {
|
|
|
|
|
$revisions = [];
|
|
|
|
|
$page = $this->getPage( __METHOD__, [ 1 => 'Test A', 2 => 'Test B' ], $revisions );
|
|
|
|
|
$idA = $revisions[1]->getId();
|
|
|
|
|
|
2020-11-25 04:28:52 +00:00
|
|
|
$revDelList = $this->getRevDelRevisionList( $page->getTitle(), $idA );
|
2018-08-16 15:45:10 +00:00
|
|
|
$revDelList->setVisibility( [
|
|
|
|
|
'value' => [ RevisionRecord::DELETED_TEXT => 1 ],
|
|
|
|
|
'comment' => "Testing",
|
|
|
|
|
] );
|
|
|
|
|
|
|
|
|
|
$article = new Article( $page->getTitle(), $idA );
|
|
|
|
|
$article->getContext()->getOutput()->setTitle( $page->getTitle() );
|
|
|
|
|
$article->view();
|
|
|
|
|
|
|
|
|
|
$output = $article->getContext()->getOutput();
|
2019-12-04 06:49:47 +00:00
|
|
|
$this->assertStringContainsString( 'rev-deleted-text-permission', $this->getHtml( $output ) );
|
2018-08-16 15:45:10 +00:00
|
|
|
|
2019-12-14 10:27:56 +00:00
|
|
|
$this->assertStringNotContainsString( 'Test A', $this->getHtml( $output ) );
|
|
|
|
|
$this->assertStringNotContainsString( 'Test B', $this->getHtml( $output ) );
|
2018-08-16 15:45:10 +00:00
|
|
|
}
|
|
|
|
|
|
2018-10-02 01:33:28 +00:00
|
|
|
public function testUnhiddenViewOfDeletedRevision() {
|
|
|
|
|
$revisions = [];
|
|
|
|
|
$page = $this->getPage( __METHOD__, [ 1 => 'Test A', 2 => 'Test B' ], $revisions );
|
|
|
|
|
$idA = $revisions[1]->getId();
|
|
|
|
|
|
2020-11-25 04:28:52 +00:00
|
|
|
$revDelList = $this->getRevDelRevisionList( $page->getTitle(), $idA );
|
2018-10-02 01:33:28 +00:00
|
|
|
$revDelList->setVisibility( [
|
2022-12-17 16:03:41 +00:00
|
|
|
'value' => [
|
|
|
|
|
RevisionRecord::DELETED_TEXT => 1,
|
|
|
|
|
RevisionRecord::DELETED_COMMENT => 1,
|
|
|
|
|
RevisionRecord::DELETED_USER => 1,
|
|
|
|
|
],
|
2018-10-02 01:33:28 +00:00
|
|
|
'comment' => "Testing",
|
|
|
|
|
] );
|
|
|
|
|
|
2022-12-17 16:03:41 +00:00
|
|
|
$realContext = RequestContext::getMain();
|
|
|
|
|
$oldUser = $realContext->getUser();
|
|
|
|
|
$oldLanguage = $realContext->getLanguage();
|
|
|
|
|
|
2018-10-02 01:33:28 +00:00
|
|
|
$article = new Article( $page->getTitle(), $idA );
|
2022-12-17 16:03:41 +00:00
|
|
|
$context = new DerivativeContext( $realContext );
|
2018-10-02 01:33:28 +00:00
|
|
|
$article->setContext( $context );
|
|
|
|
|
$context->getOutput()->setTitle( $page->getTitle() );
|
|
|
|
|
$context->getRequest()->setVal( 'unhide', 1 );
|
|
|
|
|
$context->setUser( $this->getTestUser( [ 'sysop' ] )->getUser() );
|
2022-12-17 16:03:41 +00:00
|
|
|
|
|
|
|
|
// Need global user set to sysop, global state in Linker::revUserTools/Linker::revComment (T309479)
|
|
|
|
|
$realContext->setUser( $context->getUser() );
|
|
|
|
|
// Language is resetted in setUser
|
|
|
|
|
$this->setUserLang( $oldLanguage );
|
|
|
|
|
|
2018-10-02 01:33:28 +00:00
|
|
|
$article->view();
|
|
|
|
|
|
|
|
|
|
$output = $article->getContext()->getOutput();
|
2022-12-17 16:03:41 +00:00
|
|
|
$subtitle = $output->getSubtitle();
|
|
|
|
|
$html = $this->getHtml( $output );
|
2018-10-02 01:33:28 +00:00
|
|
|
|
2022-12-17 16:03:41 +00:00
|
|
|
// Test that oldid is select, not the current version
|
|
|
|
|
$this->assertStringNotContainsString( 'Test B', $html );
|
|
|
|
|
|
|
|
|
|
// Warning about rev-del must exists
|
|
|
|
|
$this->assertStringContainsString( 'rev-deleted-text-view', $html );
|
|
|
|
|
|
|
|
|
|
// Test for the hidden values
|
|
|
|
|
$this->assertStringContainsString( 'Test A', $html );
|
|
|
|
|
$this->assertStringContainsString( $revisions[1]->getUser()->getName(), $subtitle );
|
2022-01-11 20:50:21 +00:00
|
|
|
$this->assertStringContainsString( '(parentheses: Rev 1)', $subtitle );
|
2022-12-17 16:03:41 +00:00
|
|
|
|
|
|
|
|
// Should not contain the rev-del messages
|
|
|
|
|
$this->assertStringNotContainsString( '(rev-deleted-user)', $subtitle );
|
2022-01-11 20:50:21 +00:00
|
|
|
$this->assertStringNotContainsString( '(rev-deleted-comment)', $subtitle );
|
2022-12-17 16:03:41 +00:00
|
|
|
|
|
|
|
|
$realContext->setUser( $oldUser );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testHiddenViewOfDeletedRevision() {
|
|
|
|
|
$revisions = [];
|
|
|
|
|
$page = $this->getPage( __METHOD__, [ 1 => 'Test A', 2 => 'Test B' ], $revisions );
|
|
|
|
|
$idA = $revisions[1]->getId();
|
|
|
|
|
|
|
|
|
|
$revDelList = $this->getRevDelRevisionList( $page->getTitle(), $idA );
|
|
|
|
|
$revDelList->setVisibility( [
|
|
|
|
|
'value' => [
|
|
|
|
|
RevisionRecord::DELETED_TEXT => 1,
|
|
|
|
|
RevisionRecord::DELETED_COMMENT => 1,
|
|
|
|
|
RevisionRecord::DELETED_USER => 1,
|
|
|
|
|
],
|
|
|
|
|
'comment' => "Testing",
|
|
|
|
|
] );
|
|
|
|
|
|
|
|
|
|
$realContext = RequestContext::getMain();
|
|
|
|
|
$oldUser = $realContext->getUser();
|
|
|
|
|
$oldLanguage = $realContext->getLanguage();
|
|
|
|
|
|
|
|
|
|
$article = new Article( $page->getTitle(), $idA );
|
|
|
|
|
$context = new DerivativeContext( $realContext );
|
|
|
|
|
$article->setContext( $context );
|
|
|
|
|
$context->getOutput()->setTitle( $page->getTitle() );
|
|
|
|
|
// No unhide=1 is set in this test case
|
|
|
|
|
$context->setUser( $this->getTestUser( [ 'sysop' ] )->getUser() );
|
|
|
|
|
|
|
|
|
|
// Need global user set to sysop, global state in Linker::revUserTools/Linker::revComment (T309479)
|
|
|
|
|
$realContext->setUser( $context->getUser() );
|
|
|
|
|
// Language is resetted in setUser
|
|
|
|
|
$this->setUserLang( $oldLanguage );
|
|
|
|
|
|
|
|
|
|
$article->view();
|
|
|
|
|
|
|
|
|
|
$output = $article->getContext()->getOutput();
|
|
|
|
|
$subtitle = $output->getSubtitle();
|
|
|
|
|
$html = $this->getHtml( $output );
|
|
|
|
|
|
|
|
|
|
// Test that oldid is select, not the current version
|
|
|
|
|
$this->assertStringNotContainsString( 'Test B', $html );
|
|
|
|
|
|
|
|
|
|
// Warning about rev-del must exists
|
|
|
|
|
$this->assertStringContainsString( 'rev-deleted-text-unhide', $html );
|
|
|
|
|
|
|
|
|
|
// Test for the rev-del messages
|
|
|
|
|
$this->assertStringContainsString( '(rev-deleted-user)', $subtitle );
|
|
|
|
|
$this->assertStringContainsString( '(rev-deleted-comment)', $subtitle );
|
|
|
|
|
|
|
|
|
|
// Should not contain the hidden values
|
|
|
|
|
$this->assertStringNotContainsString( 'Test A', $html );
|
|
|
|
|
$this->assertStringNotContainsString( $revisions[1]->getUser()->getName(), $subtitle );
|
|
|
|
|
$this->assertStringNotContainsString( '(parentheses: Rev 1)', $subtitle );
|
|
|
|
|
|
|
|
|
|
$realContext->setUser( $oldUser );
|
2018-10-02 01:33:28 +00:00
|
|
|
}
|
|
|
|
|
|
2018-08-16 15:45:10 +00:00
|
|
|
public function testViewMissingPage() {
|
|
|
|
|
$page = $this->getPage( __METHOD__ );
|
|
|
|
|
|
|
|
|
|
$article = new Article( $page->getTitle() );
|
|
|
|
|
$article->getContext()->getOutput()->setTitle( $page->getTitle() );
|
|
|
|
|
$article->view();
|
|
|
|
|
|
|
|
|
|
$output = $article->getContext()->getOutput();
|
2019-12-14 10:27:56 +00:00
|
|
|
$this->assertStringContainsString( '(noarticletextanon)', $this->getHtml( $output ) );
|
2018-08-16 15:45:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testViewDeletedPage() {
|
|
|
|
|
$page = $this->getPage( __METHOD__, [ 1 => 'Test A', 2 => 'Test B' ] );
|
2021-10-30 15:45:49 +00:00
|
|
|
$this->deletePage( $page );
|
2018-08-16 15:45:10 +00:00
|
|
|
|
|
|
|
|
$article = new Article( $page->getTitle() );
|
|
|
|
|
$article->getContext()->getOutput()->setTitle( $page->getTitle() );
|
|
|
|
|
$article->view();
|
|
|
|
|
|
|
|
|
|
$output = $article->getContext()->getOutput();
|
2019-12-14 10:27:56 +00:00
|
|
|
$this->assertStringContainsString( 'moveddeleted', $this->getHtml( $output ) );
|
|
|
|
|
$this->assertStringContainsString( 'logentry-delete-delete', $this->getHtml( $output ) );
|
|
|
|
|
$this->assertStringContainsString( '(noarticletextanon)', $this->getHtml( $output ) );
|
2018-08-16 15:45:10 +00:00
|
|
|
|
2019-12-14 10:27:56 +00:00
|
|
|
$this->assertStringNotContainsString( 'Test A', $this->getHtml( $output ) );
|
|
|
|
|
$this->assertStringNotContainsString( 'Test B', $this->getHtml( $output ) );
|
2018-08-16 15:45:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testViewMessagePage() {
|
|
|
|
|
$title = Title::makeTitle( NS_MEDIAWIKI, 'Mainpage' );
|
|
|
|
|
$page = $this->getPage( $title );
|
|
|
|
|
|
|
|
|
|
$article = new Article( $page->getTitle() );
|
|
|
|
|
$article->getContext()->getOutput()->setTitle( $page->getTitle() );
|
|
|
|
|
$article->view();
|
|
|
|
|
|
|
|
|
|
$output = $article->getContext()->getOutput();
|
2019-12-14 12:22:50 +00:00
|
|
|
$this->assertStringContainsString(
|
2018-08-16 15:45:10 +00:00
|
|
|
wfMessage( 'mainpage' )->inContentLanguage()->parse(),
|
|
|
|
|
$this->getHtml( $output )
|
|
|
|
|
);
|
2019-12-14 10:27:56 +00:00
|
|
|
$this->assertStringNotContainsString( '(noarticletextanon)', $this->getHtml( $output ) );
|
2018-08-16 15:45:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testViewMissingUserPage() {
|
|
|
|
|
$user = $this->getTestUser()->getUser();
|
|
|
|
|
$user->addToDatabase();
|
|
|
|
|
|
|
|
|
|
$title = Title::makeTitle( NS_USER, $user->getName() );
|
|
|
|
|
|
|
|
|
|
$page = $this->getPage( $title );
|
|
|
|
|
|
|
|
|
|
$article = new Article( $page->getTitle() );
|
|
|
|
|
$article->getContext()->getOutput()->setTitle( $page->getTitle() );
|
|
|
|
|
$article->view();
|
|
|
|
|
|
|
|
|
|
$output = $article->getContext()->getOutput();
|
2019-12-14 10:27:56 +00:00
|
|
|
$this->assertStringContainsString( '(noarticletextanon)', $this->getHtml( $output ) );
|
|
|
|
|
$this->assertStringNotContainsString(
|
|
|
|
|
'(userpage-userdoesnotexist-view)',
|
|
|
|
|
$this->getHtml( $output )
|
|
|
|
|
);
|
2018-08-16 15:45:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testViewUserPageOfNonexistingUser() {
|
|
|
|
|
$user = User::newFromName( 'Testing ' . __METHOD__ );
|
|
|
|
|
|
|
|
|
|
$title = Title::makeTitle( NS_USER, $user->getName() );
|
|
|
|
|
|
|
|
|
|
$page = $this->getPage( $title );
|
|
|
|
|
|
|
|
|
|
$article = new Article( $page->getTitle() );
|
|
|
|
|
$article->getContext()->getOutput()->setTitle( $page->getTitle() );
|
|
|
|
|
$article->view();
|
|
|
|
|
|
|
|
|
|
$output = $article->getContext()->getOutput();
|
2019-12-14 10:27:56 +00:00
|
|
|
$this->assertStringContainsString( '(noarticletextanon)', $this->getHtml( $output ) );
|
|
|
|
|
$this->assertStringContainsString(
|
|
|
|
|
'(userpage-userdoesnotexist-view:',
|
|
|
|
|
$this->getHtml( $output )
|
|
|
|
|
);
|
2018-08-16 15:45:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testArticleViewHeaderHook() {
|
|
|
|
|
$page = $this->getPage( __METHOD__, [ 1 => 'Test A' ] );
|
|
|
|
|
|
|
|
|
|
$article = new Article( $page->getTitle(), 0 );
|
|
|
|
|
$article->getContext()->getOutput()->setTitle( $page->getTitle() );
|
|
|
|
|
|
|
|
|
|
$this->setTemporaryHook(
|
|
|
|
|
'ArticleViewHeader',
|
|
|
|
|
function ( Article $articlePage, &$outputDone, &$useParserCache ) use ( $article ) {
|
|
|
|
|
$this->assertSame( $article, $articlePage, '$articlePage' );
|
|
|
|
|
|
|
|
|
|
$outputDone = new ParserOutput( 'Hook Text' );
|
|
|
|
|
$outputDone->setTitleText( 'Hook Title' );
|
|
|
|
|
|
|
|
|
|
$articlePage->getContext()->getOutput()->addParserOutput( $outputDone );
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$article->view();
|
|
|
|
|
|
|
|
|
|
$output = $article->getContext()->getOutput();
|
2019-12-14 10:27:56 +00:00
|
|
|
$this->assertStringNotContainsString( 'Test A', $this->getHtml( $output ) );
|
|
|
|
|
$this->assertStringContainsString( 'Hook Text', $this->getHtml( $output ) );
|
2018-08-16 15:45:10 +00:00
|
|
|
$this->assertSame( 'Hook Title', $output->getPageTitle() );
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-14 16:37:30 +00:00
|
|
|
public function testArticleRevisionViewCustomHook() {
|
|
|
|
|
$page = $this->getPage( __METHOD__, [ 1 => 'Test A' ] );
|
|
|
|
|
|
|
|
|
|
$article = new Article( $page->getTitle(), 0 );
|
|
|
|
|
$article->getContext()->getOutput()->setTitle( $page->getTitle() );
|
|
|
|
|
|
|
|
|
|
// use ArticleViewHeader hook to bypass the parser cache
|
|
|
|
|
$this->setTemporaryHook(
|
|
|
|
|
'ArticleViewHeader',
|
2021-07-13 19:43:07 +00:00
|
|
|
static function ( Article $articlePage, &$outputDone, &$useParserCache ) {
|
2018-08-14 16:37:30 +00:00
|
|
|
$useParserCache = false;
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$this->setTemporaryHook(
|
|
|
|
|
'ArticleRevisionViewCustom',
|
|
|
|
|
function ( RevisionRecord $rev, Title $title, $oldid, OutputPage $output ) use ( $page ) {
|
2018-09-24 21:10:08 +00:00
|
|
|
$content = $rev->getContent( SlotRecord::MAIN );
|
2018-08-14 16:37:30 +00:00
|
|
|
$this->assertSame( $page->getTitle(), $title, '$title' );
|
2019-02-07 00:14:23 +00:00
|
|
|
$this->assertSame( 'Test A', $content->getText(), '$content' );
|
2018-08-14 16:37:30 +00:00
|
|
|
|
|
|
|
|
$output->addHTML( 'Hook Text' );
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
2018-08-16 15:45:10 +00:00
|
|
|
$article->view();
|
|
|
|
|
|
|
|
|
|
$output = $article->getContext()->getOutput();
|
2019-12-14 10:27:56 +00:00
|
|
|
$this->assertStringNotContainsString( 'Test A', $this->getHtml( $output ) );
|
|
|
|
|
$this->assertStringContainsString( 'Hook Text', $this->getHtml( $output ) );
|
2018-08-16 15:45:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testShowMissingArticleHook() {
|
|
|
|
|
$page = $this->getPage( __METHOD__ );
|
|
|
|
|
|
|
|
|
|
$article = new Article( $page->getTitle() );
|
|
|
|
|
$article->getContext()->getOutput()->setTitle( $page->getTitle() );
|
|
|
|
|
|
|
|
|
|
$this->setTemporaryHook(
|
|
|
|
|
'ShowMissingArticle',
|
|
|
|
|
function ( Article $articlePage ) use ( $article ) {
|
|
|
|
|
$this->assertSame( $article, $articlePage, '$articlePage' );
|
|
|
|
|
|
|
|
|
|
$articlePage->getContext()->getOutput()->addHTML( 'Hook Text' );
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$article->view();
|
|
|
|
|
|
|
|
|
|
$output = $article->getContext()->getOutput();
|
2019-12-14 10:27:56 +00:00
|
|
|
$this->assertStringContainsString( '(noarticletextanon)', $this->getHtml( $output ) );
|
|
|
|
|
$this->assertStringContainsString( 'Hook Text', $this->getHtml( $output ) );
|
2018-08-16 15:45:10 +00:00
|
|
|
}
|
|
|
|
|
|
2020-11-05 19:31:26 +00:00
|
|
|
/**
|
|
|
|
|
* @covers \Article::showViewError()
|
|
|
|
|
*/
|
|
|
|
|
public function testViewLatestError() {
|
|
|
|
|
$page = $this->getPage( __METHOD__, [ 1 => 'Test A' ] );
|
|
|
|
|
|
|
|
|
|
$article = new Article( $page->getTitle(), 0 );
|
|
|
|
|
$output = $article->getContext()->getOutput();
|
|
|
|
|
$output->setTitle( $page->getTitle() );
|
|
|
|
|
|
|
|
|
|
// use ArticleViewHeader hook to bypass the parser cache
|
|
|
|
|
$this->setTemporaryHook(
|
|
|
|
|
'ArticleViewHeader',
|
2021-07-13 19:43:07 +00:00
|
|
|
static function ( Article $articlePage, &$outputDone, &$useParserCache ) {
|
2020-11-05 19:31:26 +00:00
|
|
|
$useParserCache = false;
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$article = TestingAccessWrapper::newFromObject( $article );
|
|
|
|
|
$article->fetchResult = Status::newFatal(
|
|
|
|
|
'rev-deleted-text-permission',
|
|
|
|
|
$page->getTitle()->getPrefixedDBkey()
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$article->view();
|
|
|
|
|
|
|
|
|
|
$this->assertStringContainsString(
|
|
|
|
|
'rev-deleted-text-permission: ArticleViewTest::testViewLatestError',
|
|
|
|
|
$this->getHtml( $output )
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @covers \Article::showViewError()
|
|
|
|
|
*/
|
|
|
|
|
public function testViewOldError() {
|
|
|
|
|
$revisions = [];
|
|
|
|
|
$page = $this->getPage( __METHOD__, [ 1 => 'Test A', 2 => 'Test B' ], $revisions );
|
|
|
|
|
$idA = $revisions[1]->getId();
|
|
|
|
|
|
|
|
|
|
$article = new Article( $page->getTitle(), $idA );
|
|
|
|
|
$output = $article->getContext()->getOutput();
|
|
|
|
|
$output->setTitle( $page->getTitle() );
|
|
|
|
|
|
|
|
|
|
$article = TestingAccessWrapper::newFromObject( $article );
|
|
|
|
|
$article->fetchResult = Status::newFatal(
|
|
|
|
|
'rev-deleted-text-permission',
|
|
|
|
|
$page->getTitle()->getPrefixedDBkey()
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$article->view();
|
|
|
|
|
|
|
|
|
|
$this->assertStringContainsString(
|
|
|
|
|
'rev-deleted-text-permission: ArticleViewTest::testViewOldError',
|
|
|
|
|
$this->getHtml( $output )
|
|
|
|
|
);
|
|
|
|
|
}
|
2020-11-25 04:28:52 +00:00
|
|
|
|
|
|
|
|
private function getRevDelRevisionList( $title, $revisionId ) {
|
2022-01-12 20:13:39 +00:00
|
|
|
$services = $this->getServiceContainer();
|
2024-06-27 07:57:24 +00:00
|
|
|
$context = new DerivativeContext( RequestContext::getMain() );
|
|
|
|
|
$context->setUser(
|
|
|
|
|
$this->getTestUser( [ 'sysop' ] )->getUser()
|
|
|
|
|
);
|
2020-11-25 04:28:52 +00:00
|
|
|
return new RevDelRevisionList(
|
2024-06-27 07:57:24 +00:00
|
|
|
$context,
|
2020-11-25 04:28:52 +00:00
|
|
|
$title,
|
|
|
|
|
[ $revisionId ],
|
2024-01-22 21:32:48 +00:00
|
|
|
$services->getConnectionProvider(),
|
2020-11-25 04:28:52 +00:00
|
|
|
$services->getHookContainer(),
|
|
|
|
|
$services->getHtmlCacheUpdater(),
|
2022-08-05 17:55:35 +00:00
|
|
|
$services->getRevisionStore()
|
2020-11-25 04:28:52 +00:00
|
|
|
);
|
|
|
|
|
}
|
2022-05-27 16:38:32 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Test the "useParsoid" parser option and the ArticleParserOptions
|
|
|
|
|
* hook.
|
|
|
|
|
*/
|
|
|
|
|
public function testUseParsoid() {
|
|
|
|
|
// Create an appropriate test page.
|
|
|
|
|
$title = Title::makeTitle( NS_MAIN, 'UseParsoidTest' );
|
|
|
|
|
$article = new Article( $title );
|
|
|
|
|
$page = $this->getExistingTestPage( $title );
|
|
|
|
|
$page->doUserEditContent(
|
|
|
|
|
ContentHandler::makeContent(
|
|
|
|
|
'[[Foo]]',
|
|
|
|
|
$title,
|
|
|
|
|
// Force this page to be wikitext
|
|
|
|
|
CONTENT_MODEL_WIKITEXT
|
|
|
|
|
),
|
2023-08-09 01:01:19 +00:00
|
|
|
$this->getTestSysop()->getUser(),
|
|
|
|
|
'TestUseParsoid Summary',
|
2022-05-27 16:38:32 +00:00
|
|
|
EDIT_SUPPRESS_RC
|
|
|
|
|
);
|
|
|
|
|
$article->view();
|
|
|
|
|
$html = $this->getHtml( $article->getContext()->getOutput() );
|
|
|
|
|
// Confirm that this is NOT parsoid-generated HTML
|
|
|
|
|
$this->assertStringNotContainsString(
|
|
|
|
|
'rel="mw:WikiLink"',
|
|
|
|
|
$html
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Now enable Parsoid via the ArticleParserOptions hook
|
|
|
|
|
$article = new Article( $title );
|
|
|
|
|
$this->setTemporaryHook( 'ArticleParserOptions', static function ( $article, $popts ) {
|
|
|
|
|
$popts->setUseParsoid();
|
|
|
|
|
} );
|
|
|
|
|
$article->view();
|
|
|
|
|
$html = $this->getHtml( $article->getContext()->getOutput() );
|
|
|
|
|
// Look for a marker that this is Parsoid-generated HTML
|
|
|
|
|
$this->assertStringContainsString(
|
|
|
|
|
'rel="mw:WikiLink"',
|
|
|
|
|
$html
|
|
|
|
|
);
|
|
|
|
|
}
|
2018-08-16 15:45:10 +00:00
|
|
|
}
|