2011-12-06 23:35:42 +00:00
|
|
|
<?php
|
|
|
|
|
|
2023-02-13 19:56:51 +00:00
|
|
|
use MediaWiki\EditPage\EditPage;
|
2022-07-15 00:07:38 +00:00
|
|
|
use MediaWiki\MainConfigNames;
|
|
|
|
|
use MediaWiki\MainConfigSchema;
|
2022-10-28 10:04:25 +00:00
|
|
|
use MediaWiki\Request\FauxRequest;
|
2021-04-11 20:49:08 +00:00
|
|
|
use MediaWiki\Revision\RevisionRecord;
|
|
|
|
|
use MediaWiki\Storage\EditResult;
|
2023-03-01 20:33:26 +00:00
|
|
|
use MediaWiki\Title\Title;
|
2021-04-11 20:49:08 +00:00
|
|
|
use MediaWiki\User\UserIdentity;
|
2020-11-10 23:36:02 +00:00
|
|
|
use Wikimedia\TestingAccessWrapper;
|
2018-07-29 12:24:54 +00:00
|
|
|
|
2012-03-15 15:38:11 +00:00
|
|
|
/**
|
2012-11-10 16:24:48 +00:00
|
|
|
* @group Editing
|
|
|
|
|
* @group Database
|
|
|
|
|
* @group medium
|
2012-03-15 15:38:11 +00:00
|
|
|
*/
|
2013-07-26 21:53:06 +00:00
|
|
|
class EditPageTest extends MediaWikiLangTestCase {
|
2011-12-06 23:35:42 +00:00
|
|
|
|
2021-07-22 03:11:47 +00:00
|
|
|
protected function setUp(): void {
|
2015-04-15 08:26:22 +00:00
|
|
|
parent::setUp();
|
|
|
|
|
|
2022-07-15 00:07:38 +00:00
|
|
|
$this->overrideConfigValues( [
|
|
|
|
|
MainConfigNames::ExtraNamespaces => [
|
2018-08-01 12:40:47 +00:00
|
|
|
12312 => 'Dummy',
|
|
|
|
|
12313 => 'Dummy_talk',
|
|
|
|
|
],
|
2022-07-15 00:07:38 +00:00
|
|
|
MainConfigNames::NamespaceContentModels => [ 12312 => 'testing' ],
|
|
|
|
|
MainConfigNames::ContentHandlers =>
|
|
|
|
|
[ 'testing' => 'DummyContentHandlerForTesting' ] +
|
|
|
|
|
MainConfigSchema::getDefaultValue( MainConfigNames::ContentHandlers ),
|
2016-02-17 09:09:32 +00:00
|
|
|
] );
|
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
|
|
|
|
|
|
|
|
// Disable WAN cache to avoid edit conflicts in testUpdateNoMinor
|
|
|
|
|
$this->setMainCache( CACHE_NONE );
|
2017-09-11 20:37:18 +00:00
|
|
|
}
|
|
|
|
|
|
2011-12-06 23:35:42 +00:00
|
|
|
/**
|
2012-10-08 10:56:20 +00:00
|
|
|
* @dataProvider provideExtractSectionTitle
|
2023-05-27 09:43:12 +00:00
|
|
|
* @covers MediaWiki\EditPage\EditPage::extractSectionTitle
|
2011-12-06 23:35:42 +00:00
|
|
|
*/
|
2013-10-23 22:51:31 +00:00
|
|
|
public function testExtractSectionTitle( $section, $title ) {
|
2020-11-10 23:36:02 +00:00
|
|
|
$this->assertEquals(
|
|
|
|
|
$title,
|
2021-09-03 22:52:31 +00:00
|
|
|
TestingAccessWrapper::newFromClass( EditPage::class )->extractSectionTitle( $section )
|
2020-11-10 23:36:02 +00:00
|
|
|
);
|
2011-12-06 23:35:42 +00:00
|
|
|
}
|
|
|
|
|
|
2012-10-08 10:56:20 +00:00
|
|
|
public static function provideExtractSectionTitle() {
|
2016-02-17 09:09:32 +00:00
|
|
|
return [
|
|
|
|
|
[
|
2011-12-06 23:35:42 +00:00
|
|
|
"== Test ==\n\nJust a test section.",
|
|
|
|
|
"Test"
|
2016-02-17 09:09:32 +00:00
|
|
|
],
|
|
|
|
|
[
|
2011-12-06 23:35:42 +00:00
|
|
|
"An initial section, no header.",
|
|
|
|
|
false
|
2016-02-17 09:09:32 +00:00
|
|
|
],
|
|
|
|
|
[
|
2017-02-20 23:45:58 +00:00
|
|
|
"An initial section with a fake heder (T34617)\n\n== Test == ??\nwtf",
|
2011-12-06 23:35:42 +00:00
|
|
|
false
|
2016-02-17 09:09:32 +00:00
|
|
|
],
|
|
|
|
|
[
|
2011-12-06 23:35:42 +00:00
|
|
|
"== Section ==\nfollowed by a fake == Non-section == ??\nnoooo",
|
|
|
|
|
"Section"
|
2016-02-17 09:09:32 +00:00
|
|
|
],
|
|
|
|
|
[
|
2017-02-20 23:45:58 +00:00
|
|
|
"== Section== \t\r\n followed by whitespace (T37051)",
|
2012-03-15 15:38:11 +00:00
|
|
|
'Section',
|
2016-02-17 09:09:32 +00:00
|
|
|
],
|
|
|
|
|
];
|
2011-12-06 23:35:42 +00:00
|
|
|
}
|
2012-11-10 16:24:48 +00:00
|
|
|
|
|
|
|
|
protected function forceRevisionDate( WikiPage $page, $timestamp ) {
|
2021-04-29 02:37:11 +00:00
|
|
|
$dbw = wfGetDB( DB_PRIMARY );
|
2012-11-10 16:24:48 +00:00
|
|
|
|
|
|
|
|
$dbw->update( 'revision',
|
2016-02-17 09:09:32 +00:00
|
|
|
[ 'rev_timestamp' => $dbw->timestamp( $timestamp ) ],
|
|
|
|
|
[ 'rev_id' => $page->getLatest() ] );
|
2012-11-10 16:24:48 +00:00
|
|
|
|
|
|
|
|
$page->clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* User input text is passed to rtrim() by edit page. This is a simple
|
|
|
|
|
* wrapper around assertEquals() which calls rrtrim() to normalize the
|
|
|
|
|
* expected and actual texts.
|
2014-08-25 16:50:35 +00:00
|
|
|
* @param string $expected
|
|
|
|
|
* @param string $actual
|
|
|
|
|
* @param string $msg
|
2012-11-10 16:24:48 +00:00
|
|
|
*/
|
2013-10-24 10:54:02 +00:00
|
|
|
protected function assertEditedTextEquals( $expected, $actual, $msg = '' ) {
|
2016-01-27 16:22:32 +00:00
|
|
|
$this->assertEquals( rtrim( $expected ), rtrim( $actual ), $msg );
|
2012-11-10 16:24:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Performs an edit and checks the result.
|
|
|
|
|
*
|
2014-04-17 18:43:42 +00:00
|
|
|
* @param string|Title $title The title of the page to edit
|
|
|
|
|
* @param string|null $baseText Some text to create the page with before attempting the edit.
|
|
|
|
|
* @param User|string|null $user The user to perform the edit as.
|
2012-11-10 16:24:48 +00:00
|
|
|
* @param array $edit An array of request parameters used to define the edit to perform.
|
|
|
|
|
* Some well known fields are:
|
|
|
|
|
* * wpTextbox1: the text to submit
|
|
|
|
|
* * wpSummary: the edit summary
|
|
|
|
|
* * wpEditToken: the edit token (will be inserted if not provided)
|
2014-04-24 09:57:41 +00:00
|
|
|
* * wpEdittime: timestamp of the edit's base revision (will be inserted
|
|
|
|
|
* if not provided)
|
2020-03-05 21:23:07 +00:00
|
|
|
* * editRevId: revision ID of the edit's base revision (optional)
|
2012-11-10 16:24:48 +00:00
|
|
|
* * wpStarttime: timestamp when the edit started (will be inserted if not provided)
|
|
|
|
|
* * wpSectionTitle: the section to edit
|
2021-07-23 13:11:09 +00:00
|
|
|
* * wpMinoredit: mark as minor edit
|
2012-11-10 16:24:48 +00:00
|
|
|
* * wpWatchthis: whether to watch the page
|
|
|
|
|
* @param int|null $expectedCode The expected result code (EditPage::AS_XXX constants).
|
2014-07-22 00:50:47 +00:00
|
|
|
* Set to null to skip the check.
|
2014-04-17 18:43:42 +00:00
|
|
|
* @param string|null $expectedText The text expected to be on the page after the edit.
|
2012-11-10 16:24:48 +00:00
|
|
|
* Set to null to skip the check.
|
2014-04-17 18:43:42 +00:00
|
|
|
* @param string|null $message An optional message to show along with any error message.
|
2012-11-10 16:24:48 +00:00
|
|
|
*
|
|
|
|
|
* @return WikiPage The page that was just edited, useful for getting the edit's rev_id, etc.
|
|
|
|
|
*/
|
2020-03-19 13:09:08 +00:00
|
|
|
protected function assertEdit( $title, $baseText, $user, array $edit,
|
2014-07-22 00:50:47 +00:00
|
|
|
$expectedCode = null, $expectedText = null, $message = null
|
2012-11-10 16:24:48 +00:00
|
|
|
) {
|
|
|
|
|
if ( is_string( $title ) ) {
|
|
|
|
|
$ns = $this->getDefaultWikitextNS();
|
|
|
|
|
$title = Title::newFromText( $title, $ns );
|
|
|
|
|
}
|
2014-07-17 22:28:46 +00:00
|
|
|
$this->assertNotNull( $title );
|
2012-11-10 16:24:48 +00:00
|
|
|
|
|
|
|
|
if ( is_string( $user ) ) {
|
|
|
|
|
$user = User::newFromName( $user );
|
|
|
|
|
|
|
|
|
|
if ( $user->getId() === 0 ) {
|
|
|
|
|
$user->addToDatabase();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-24 08:42:19 +00:00
|
|
|
if ( $user == null ) {
|
|
|
|
|
$user = $this->getTestUser()->getUser();
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-01 21:18:41 +00:00
|
|
|
$wikiPageFactory = $this->getServiceContainer()->getWikiPageFactory();
|
|
|
|
|
$page = $wikiPageFactory->newFromTitle( $title );
|
2012-11-10 16:24:48 +00:00
|
|
|
|
|
|
|
|
if ( $baseText !== null ) {
|
|
|
|
|
$content = ContentHandler::makeContent( $baseText, $title );
|
2021-06-24 08:42:19 +00:00
|
|
|
$page->doUserEditContent( $content, $user, "base text for test" );
|
2012-11-10 16:24:48 +00:00
|
|
|
$this->forceRevisionDate( $page, '20120101000000' );
|
|
|
|
|
|
|
|
|
|
$page->clear();
|
2021-06-02 07:19:41 +00:00
|
|
|
$content = $page->getContent();
|
|
|
|
|
|
|
|
|
|
$this->assertInstanceOf( TextContent::class, $content );
|
|
|
|
|
$currentText = $content->getText();
|
2012-11-10 16:24:48 +00:00
|
|
|
|
|
|
|
|
# EditPage rtrim() the user input, so we alter our expected text
|
|
|
|
|
# to reflect that.
|
|
|
|
|
$this->assertEditedTextEquals( $baseText, $currentText );
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-05 06:54:11 +00:00
|
|
|
if ( !isset( $edit['wpEditToken'] ) ) {
|
|
|
|
|
$edit['wpEditToken'] = $user->getEditToken();
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-05 21:23:07 +00:00
|
|
|
if ( !isset( $edit['wpEdittime'] ) && !isset( $edit['editRevId'] ) ) {
|
2012-11-10 16:24:48 +00:00
|
|
|
$edit['wpEdittime'] = $page->exists() ? $page->getTimestamp() : '';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( !isset( $edit['wpStarttime'] ) ) {
|
|
|
|
|
$edit['wpStarttime'] = wfTimestampNow();
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-28 21:14:44 +00:00
|
|
|
if ( !isset( $edit['wpUnicodeCheck'] ) ) {
|
|
|
|
|
$edit['wpUnicodeCheck'] = EditPage::UNICODE_CHECK;
|
|
|
|
|
}
|
|
|
|
|
|
2012-11-10 16:24:48 +00:00
|
|
|
$req = new FauxRequest( $edit, true ); // session ??
|
|
|
|
|
|
2020-10-21 01:05:00 +00:00
|
|
|
$context = new RequestContext();
|
|
|
|
|
$context->setRequest( $req );
|
|
|
|
|
$context->setTitle( $title );
|
|
|
|
|
$context->setUser( $user );
|
2014-07-17 22:28:46 +00:00
|
|
|
$article = new Article( $title );
|
2020-10-21 01:05:00 +00:00
|
|
|
$article->setContext( $context );
|
2014-07-17 22:28:46 +00:00
|
|
|
$ep = new EditPage( $article );
|
2012-11-10 16:24:48 +00:00
|
|
|
$ep->setContextTitle( $title );
|
|
|
|
|
$ep->importFormData( $req );
|
|
|
|
|
|
|
|
|
|
// this is where the edit happens!
|
|
|
|
|
// Note: don't want to use EditPage::AttemptSave, because it messes with $wgOut
|
|
|
|
|
// and throws exceptions like PermissionsError
|
2021-07-23 13:11:09 +00:00
|
|
|
$status = $ep->attemptSave( $result );
|
2012-11-10 16:24:48 +00:00
|
|
|
|
|
|
|
|
if ( $expectedCode !== null ) {
|
|
|
|
|
// check edit code
|
|
|
|
|
$this->assertEquals( $expectedCode, $status->value,
|
|
|
|
|
"Expected result code mismatch. $message" );
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-01 21:18:41 +00:00
|
|
|
$page = $wikiPageFactory->newFromTitle( $title );
|
2012-11-10 16:24:48 +00:00
|
|
|
|
|
|
|
|
if ( $expectedText !== null ) {
|
|
|
|
|
// check resulting page text
|
|
|
|
|
$content = $page->getContent();
|
2021-06-02 07:19:41 +00:00
|
|
|
$text = ( $content instanceof TextContent ) ? $content->getText() : '';
|
2012-11-10 16:24:48 +00:00
|
|
|
|
|
|
|
|
# EditPage rtrim() the user input, so we alter our expected text
|
|
|
|
|
# to reflect that.
|
|
|
|
|
$this->assertEditedTextEquals( $expectedText, $text,
|
|
|
|
|
"Expected article text mismatch. $message" );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $page;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-18 01:28:26 +00:00
|
|
|
public static function provideCreatePages() {
|
2016-02-17 09:09:32 +00:00
|
|
|
return [
|
|
|
|
|
[ 'expected article being created',
|
2014-09-04 02:30:15 +00:00
|
|
|
'EditPageTest_testCreatePage',
|
|
|
|
|
null,
|
|
|
|
|
'Hello World!',
|
|
|
|
|
EditPage::AS_SUCCESS_NEW_ARTICLE,
|
|
|
|
|
'Hello World!'
|
2016-02-17 09:09:32 +00:00
|
|
|
],
|
|
|
|
|
[ 'expected article not being created if empty',
|
2014-09-04 02:30:15 +00:00
|
|
|
'EditPageTest_testCreatePage',
|
|
|
|
|
null,
|
|
|
|
|
'',
|
|
|
|
|
EditPage::AS_BLANK_ARTICLE,
|
|
|
|
|
null
|
2016-02-17 09:09:32 +00:00
|
|
|
],
|
|
|
|
|
[ 'expected MediaWiki: page being created',
|
2014-09-04 02:30:15 +00:00
|
|
|
'MediaWiki:January',
|
|
|
|
|
'UTSysop',
|
|
|
|
|
'Not January',
|
|
|
|
|
EditPage::AS_SUCCESS_NEW_ARTICLE,
|
|
|
|
|
'Not January'
|
2016-02-17 09:09:32 +00:00
|
|
|
],
|
|
|
|
|
[ 'expected not-registered MediaWiki: page not being created if empty',
|
2014-09-04 02:30:15 +00:00
|
|
|
'MediaWiki:EditPageTest_testCreatePage',
|
|
|
|
|
'UTSysop',
|
|
|
|
|
'',
|
|
|
|
|
EditPage::AS_BLANK_ARTICLE,
|
|
|
|
|
null
|
2016-02-17 09:09:32 +00:00
|
|
|
],
|
|
|
|
|
[ 'expected registered MediaWiki: page being created even if empty',
|
2014-09-04 02:30:15 +00:00
|
|
|
'MediaWiki:January',
|
|
|
|
|
'UTSysop',
|
|
|
|
|
'',
|
|
|
|
|
EditPage::AS_SUCCESS_NEW_ARTICLE,
|
|
|
|
|
''
|
2016-02-17 09:09:32 +00:00
|
|
|
],
|
|
|
|
|
[ 'expected registered MediaWiki: page whose default content is empty'
|
2015-03-14 08:35:54 +00:00
|
|
|
. ' not being created if empty',
|
2014-09-04 02:30:15 +00:00
|
|
|
'MediaWiki:Ipb-default-expiry',
|
|
|
|
|
'UTSysop',
|
|
|
|
|
'',
|
|
|
|
|
EditPage::AS_BLANK_ARTICLE,
|
|
|
|
|
''
|
2016-02-17 09:09:32 +00:00
|
|
|
],
|
|
|
|
|
[ 'expected MediaWiki: page not being created if text equals default message',
|
2014-09-04 02:30:15 +00:00
|
|
|
'MediaWiki:January',
|
|
|
|
|
'UTSysop',
|
|
|
|
|
'January',
|
|
|
|
|
EditPage::AS_BLANK_ARTICLE,
|
|
|
|
|
null
|
2016-02-17 09:09:32 +00:00
|
|
|
],
|
|
|
|
|
[ 'expected empty article being created',
|
2014-09-04 02:30:15 +00:00
|
|
|
'EditPageTest_testCreatePage',
|
|
|
|
|
null,
|
|
|
|
|
'',
|
|
|
|
|
EditPage::AS_SUCCESS_NEW_ARTICLE,
|
|
|
|
|
'',
|
|
|
|
|
true
|
2016-02-17 09:09:32 +00:00
|
|
|
],
|
|
|
|
|
];
|
2014-09-04 02:30:15 +00:00
|
|
|
}
|
2014-07-13 01:36:37 +00:00
|
|
|
|
2014-09-04 02:30:15 +00:00
|
|
|
/**
|
|
|
|
|
* @dataProvider provideCreatePages
|
2023-05-27 09:43:12 +00:00
|
|
|
* @covers MediaWiki\EditPage\EditPage
|
2014-09-04 02:30:15 +00:00
|
|
|
*/
|
2015-03-14 08:35:54 +00:00
|
|
|
public function testCreatePage(
|
|
|
|
|
$desc, $pageTitle, $user, $editText, $expectedCode, $expectedText, $ignoreBlank = false
|
|
|
|
|
) {
|
2015-12-13 12:01:03 +00:00
|
|
|
$checkId = null;
|
|
|
|
|
|
2022-07-15 00:07:38 +00:00
|
|
|
$this->setTemporaryHook(
|
|
|
|
|
'PageSaveComplete',
|
|
|
|
|
static function (
|
2021-04-11 20:49:08 +00:00
|
|
|
WikiPage $page, UserIdentity $user, string $summary,
|
|
|
|
|
int $flags, RevisionRecord $revisionRecord, EditResult $editResult
|
2015-12-13 12:01:03 +00:00
|
|
|
) use ( &$checkId ) {
|
2021-04-11 20:49:08 +00:00
|
|
|
$checkId = $revisionRecord->getId();
|
2015-12-13 12:01:03 +00:00
|
|
|
// types/refs checked
|
2022-07-15 00:07:38 +00:00
|
|
|
}
|
|
|
|
|
);
|
2015-12-13 12:01:03 +00:00
|
|
|
|
2016-02-17 09:09:32 +00:00
|
|
|
$edit = [ 'wpTextbox1' => $editText ];
|
2014-09-04 02:30:15 +00:00
|
|
|
if ( $ignoreBlank ) {
|
|
|
|
|
$edit['wpIgnoreBlankArticle'] = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$page = $this->assertEdit( $pageTitle, null, $user, $edit, $expectedCode, $expectedText, $desc );
|
|
|
|
|
|
|
|
|
|
if ( $expectedCode != EditPage::AS_BLANK_ARTICLE ) {
|
2015-12-13 12:01:03 +00:00
|
|
|
$latest = $page->getLatest();
|
2021-10-30 15:45:49 +00:00
|
|
|
$this->deletePage( $page );
|
2015-12-13 12:01:03 +00:00
|
|
|
|
|
|
|
|
$this->assertGreaterThan( 0, $latest, "Page revision ID updated in object" );
|
|
|
|
|
$this->assertEquals( $latest, $checkId, "Revision in Status for hook" );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @dataProvider provideCreatePages
|
2023-05-27 09:43:12 +00:00
|
|
|
* @covers MediaWiki\EditPage\EditPage
|
2015-12-13 12:01:03 +00:00
|
|
|
*/
|
|
|
|
|
public function testCreatePageTrx(
|
|
|
|
|
$desc, $pageTitle, $user, $editText, $expectedCode, $expectedText, $ignoreBlank = false
|
|
|
|
|
) {
|
2016-02-17 09:09:32 +00:00
|
|
|
$checkIds = [];
|
2022-07-15 00:07:38 +00:00
|
|
|
$this->setTemporaryHook(
|
|
|
|
|
'PageSaveComplete',
|
|
|
|
|
static function (
|
2021-04-11 20:49:08 +00:00
|
|
|
WikiPage $page, UserIdentity $user, string $summary,
|
|
|
|
|
int $flags, RevisionRecord $revisionRecord, EditResult $editResult
|
2015-12-13 12:01:03 +00:00
|
|
|
) use ( &$checkIds ) {
|
2021-04-11 20:49:08 +00:00
|
|
|
$checkIds[] = $revisionRecord->getId();
|
2015-12-13 12:01:03 +00:00
|
|
|
// types/refs checked
|
2022-07-15 00:07:38 +00:00
|
|
|
}
|
|
|
|
|
);
|
2015-12-13 12:01:03 +00:00
|
|
|
|
2021-04-29 02:37:11 +00:00
|
|
|
wfGetDB( DB_PRIMARY )->begin( __METHOD__ );
|
2015-12-13 12:01:03 +00:00
|
|
|
|
2016-02-17 09:09:32 +00:00
|
|
|
$edit = [ 'wpTextbox1' => $editText ];
|
2015-12-13 12:01:03 +00:00
|
|
|
if ( $ignoreBlank ) {
|
|
|
|
|
$edit['wpIgnoreBlankArticle'] = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$page = $this->assertEdit(
|
|
|
|
|
$pageTitle, null, $user, $edit, $expectedCode, $expectedText, $desc );
|
|
|
|
|
|
|
|
|
|
$pageTitle2 = (string)$pageTitle . '/x';
|
|
|
|
|
$page2 = $this->assertEdit(
|
|
|
|
|
$pageTitle2, null, $user, $edit, $expectedCode, $expectedText, $desc );
|
|
|
|
|
|
2021-04-29 02:37:11 +00:00
|
|
|
wfGetDB( DB_PRIMARY )->commit( __METHOD__ );
|
2015-12-13 12:01:03 +00:00
|
|
|
|
2019-09-17 14:31:49 +00:00
|
|
|
$this->assertSame( 0, DeferredUpdates::pendingUpdatesCount(), 'No deferred updates' );
|
2016-08-28 16:23:52 +00:00
|
|
|
|
2015-12-13 12:01:03 +00:00
|
|
|
if ( $expectedCode != EditPage::AS_BLANK_ARTICLE ) {
|
|
|
|
|
$latest = $page->getLatest();
|
2021-10-30 15:45:49 +00:00
|
|
|
$this->deletePage( $page );
|
2015-12-13 12:01:03 +00:00
|
|
|
|
|
|
|
|
$this->assertGreaterThan( 0, $latest, "Page #1 revision ID updated in object" );
|
|
|
|
|
$this->assertEquals( $latest, $checkIds[0], "Revision #1 in Status for hook" );
|
|
|
|
|
|
|
|
|
|
$latest2 = $page2->getLatest();
|
2021-10-30 15:45:49 +00:00
|
|
|
$this->deletePage( $page2 );
|
2015-12-13 12:01:03 +00:00
|
|
|
|
|
|
|
|
$this->assertGreaterThan( 0, $latest2, "Page #2 revision ID updated in object" );
|
|
|
|
|
$this->assertEquals( $latest2, $checkIds[1], "Revision #2 in Status for hook" );
|
2014-09-04 02:30:15 +00:00
|
|
|
}
|
2012-11-10 16:24:48 +00:00
|
|
|
}
|
|
|
|
|
|
2019-02-01 21:13:32 +00:00
|
|
|
/**
|
2023-05-27 09:43:12 +00:00
|
|
|
* @covers MediaWiki\EditPage\EditPage
|
2019-02-01 21:13:32 +00:00
|
|
|
*/
|
2012-11-10 16:24:48 +00:00
|
|
|
public function testUpdatePage() {
|
2016-02-17 09:09:32 +00:00
|
|
|
$checkIds = [];
|
2022-07-15 00:07:38 +00:00
|
|
|
$this->setTemporaryHook(
|
|
|
|
|
'PageSaveComplete',
|
|
|
|
|
static function (
|
2021-04-11 20:49:08 +00:00
|
|
|
WikiPage $page, UserIdentity $user, string $summary,
|
|
|
|
|
int $flags, RevisionRecord $revisionRecord, EditResult $editResult
|
2015-12-13 12:18:56 +00:00
|
|
|
) use ( &$checkIds ) {
|
2021-04-11 20:49:08 +00:00
|
|
|
$checkIds[] = $revisionRecord->getId();
|
2015-12-13 12:18:56 +00:00
|
|
|
// types/refs checked
|
2022-07-15 00:07:38 +00:00
|
|
|
}
|
|
|
|
|
);
|
2015-12-13 12:18:56 +00:00
|
|
|
|
2012-11-10 16:24:48 +00:00
|
|
|
$text = "one";
|
2016-02-17 09:09:32 +00:00
|
|
|
$edit = [
|
2012-11-10 16:24:48 +00:00
|
|
|
'wpTextbox1' => $text,
|
|
|
|
|
'wpSummary' => 'first update',
|
2016-02-17 09:09:32 +00:00
|
|
|
];
|
2012-11-10 16:24:48 +00:00
|
|
|
|
|
|
|
|
$page = $this->assertEdit( 'EditPageTest_testUpdatePage', "zero", null, $edit,
|
|
|
|
|
EditPage::AS_SUCCESS_UPDATE, $text,
|
2018-08-14 07:56:35 +00:00
|
|
|
"expected successful update with given text" );
|
2015-12-13 12:18:56 +00:00
|
|
|
$this->assertGreaterThan( 0, $checkIds[0], "First event rev ID set" );
|
2012-11-10 16:24:48 +00:00
|
|
|
|
|
|
|
|
$this->forceRevisionDate( $page, '20120101000000' );
|
|
|
|
|
|
|
|
|
|
$text = "two";
|
2016-02-17 09:09:32 +00:00
|
|
|
$edit = [
|
2012-11-10 16:24:48 +00:00
|
|
|
'wpTextbox1' => $text,
|
|
|
|
|
'wpSummary' => 'second update',
|
2016-02-17 09:09:32 +00:00
|
|
|
];
|
2012-11-10 16:24:48 +00:00
|
|
|
|
|
|
|
|
$this->assertEdit( 'EditPageTest_testUpdatePage', null, null, $edit,
|
|
|
|
|
EditPage::AS_SUCCESS_UPDATE, $text,
|
2018-08-14 07:56:35 +00:00
|
|
|
"expected successful update with given text" );
|
2015-12-13 12:18:56 +00:00
|
|
|
$this->assertGreaterThan( 0, $checkIds[1], "Second edit hook rev ID set" );
|
|
|
|
|
$this->assertGreaterThan( $checkIds[0], $checkIds[1], "Second event rev ID is higher" );
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-01 21:13:32 +00:00
|
|
|
/**
|
2023-05-27 09:43:12 +00:00
|
|
|
* @covers MediaWiki\EditPage\EditPage
|
2021-07-23 13:11:09 +00:00
|
|
|
*/
|
|
|
|
|
public function testUpdateNoMinor() {
|
|
|
|
|
$user = $this->getTestUser()->getUser();
|
|
|
|
|
$anon = new User(); // anon
|
|
|
|
|
|
|
|
|
|
// Test that page creation can never be minor
|
|
|
|
|
$edit = [
|
|
|
|
|
'wpTextbox1' => 'testing',
|
|
|
|
|
'wpSummary' => 'first update',
|
|
|
|
|
'wpMinoredit' => 'minor'
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
$page = $this->assertEdit( 'EditPageTest_testUpdateNoMinor', null, $user, $edit,
|
|
|
|
|
EditPage::AS_SUCCESS_NEW_ARTICLE, 'testing', "expected successful update" );
|
|
|
|
|
|
|
|
|
|
$this->assertFalse(
|
|
|
|
|
$page->getRevisionRecord()->isMinor(),
|
|
|
|
|
'page creation should not be minor'
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Test that anons can't make an update minor
|
|
|
|
|
$this->forceRevisionDate( $page, '20120101000000' );
|
|
|
|
|
|
|
|
|
|
$edit = [
|
|
|
|
|
'wpTextbox1' => 'testing 2',
|
|
|
|
|
'wpSummary' => 'second update',
|
|
|
|
|
'wpMinoredit' => 'minor'
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
$page = $this->assertEdit( 'EditPageTest_testUpdateNoMinor', null, $anon, $edit,
|
|
|
|
|
EditPage::AS_SUCCESS_UPDATE, 'testing 2', "expected successful update" );
|
|
|
|
|
|
|
|
|
|
$this->assertFalse(
|
|
|
|
|
$page->getRevisionRecord()->isMinor(),
|
|
|
|
|
'anon edit should not be minor'
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Test that users can make an update minor
|
|
|
|
|
$this->forceRevisionDate( $page, '20120102000000' );
|
|
|
|
|
|
|
|
|
|
$edit = [
|
|
|
|
|
'wpTextbox1' => 'testing 3',
|
|
|
|
|
'wpSummary' => 'third update',
|
|
|
|
|
'wpMinoredit' => 'minor'
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
$page = $this->assertEdit( 'EditPageTest_testUpdateNoMinor', null, $user, $edit,
|
|
|
|
|
EditPage::AS_SUCCESS_UPDATE, 'testing 3', "expected successful update" );
|
|
|
|
|
|
|
|
|
|
$this->assertTrue(
|
|
|
|
|
$page->getRevisionRecord()->isMinor(),
|
|
|
|
|
'users can make edits minor'
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2023-05-27 09:43:12 +00:00
|
|
|
* @covers MediaWiki\EditPage\EditPage
|
2019-02-01 21:13:32 +00:00
|
|
|
*/
|
2015-12-13 12:18:56 +00:00
|
|
|
public function testUpdatePageTrx() {
|
|
|
|
|
$text = "one";
|
2016-02-17 09:09:32 +00:00
|
|
|
$edit = [
|
2015-12-13 12:18:56 +00:00
|
|
|
'wpTextbox1' => $text,
|
|
|
|
|
'wpSummary' => 'first update',
|
2016-02-17 09:09:32 +00:00
|
|
|
];
|
2015-12-13 12:18:56 +00:00
|
|
|
|
|
|
|
|
$page = $this->assertEdit( 'EditPageTest_testTrxUpdatePage', "zero", null, $edit,
|
|
|
|
|
EditPage::AS_SUCCESS_UPDATE, $text,
|
2018-08-14 07:56:35 +00:00
|
|
|
"expected successful update with given text" );
|
2015-12-13 12:18:56 +00:00
|
|
|
|
|
|
|
|
$this->forceRevisionDate( $page, '20120101000000' );
|
|
|
|
|
|
2016-02-17 09:09:32 +00:00
|
|
|
$checkIds = [];
|
2022-07-15 00:07:38 +00:00
|
|
|
$this->setTemporaryHook(
|
|
|
|
|
'PageSaveComplete',
|
|
|
|
|
static function (
|
2021-04-11 20:49:08 +00:00
|
|
|
WikiPage $page, UserIdentity $user, string $summary,
|
|
|
|
|
int $flags, RevisionRecord $revisionRecord, EditResult $editResult
|
2015-12-13 12:18:56 +00:00
|
|
|
) use ( &$checkIds ) {
|
2021-04-11 20:49:08 +00:00
|
|
|
$checkIds[] = $revisionRecord->getId();
|
2015-12-13 12:18:56 +00:00
|
|
|
// types/refs checked
|
2022-07-15 00:07:38 +00:00
|
|
|
}
|
|
|
|
|
);
|
2015-12-13 12:18:56 +00:00
|
|
|
|
2021-04-29 02:37:11 +00:00
|
|
|
wfGetDB( DB_PRIMARY )->begin( __METHOD__ );
|
2015-12-13 12:18:56 +00:00
|
|
|
|
|
|
|
|
$text = "two";
|
2016-02-17 09:09:32 +00:00
|
|
|
$edit = [
|
2015-12-13 12:18:56 +00:00
|
|
|
'wpTextbox1' => $text,
|
|
|
|
|
'wpSummary' => 'second update',
|
2016-02-17 09:09:32 +00:00
|
|
|
];
|
2015-12-13 12:18:56 +00:00
|
|
|
|
|
|
|
|
$this->assertEdit( 'EditPageTest_testTrxUpdatePage', null, null, $edit,
|
|
|
|
|
EditPage::AS_SUCCESS_UPDATE, $text,
|
2018-08-14 07:56:35 +00:00
|
|
|
"expected successful update with given text" );
|
2015-12-13 12:18:56 +00:00
|
|
|
|
|
|
|
|
$text = "three";
|
2016-02-17 09:09:32 +00:00
|
|
|
$edit = [
|
2015-12-13 12:18:56 +00:00
|
|
|
'wpTextbox1' => $text,
|
|
|
|
|
'wpSummary' => 'third update',
|
2016-02-17 09:09:32 +00:00
|
|
|
];
|
2015-12-13 12:18:56 +00:00
|
|
|
|
|
|
|
|
$this->assertEdit( 'EditPageTest_testTrxUpdatePage', null, null, $edit,
|
|
|
|
|
EditPage::AS_SUCCESS_UPDATE, $text,
|
2018-08-14 07:56:35 +00:00
|
|
|
"expected successful update with given text" );
|
2015-12-13 12:18:56 +00:00
|
|
|
|
2021-04-29 02:37:11 +00:00
|
|
|
wfGetDB( DB_PRIMARY )->commit( __METHOD__ );
|
2015-12-13 12:18:56 +00:00
|
|
|
|
|
|
|
|
$this->assertGreaterThan( 0, $checkIds[0], "First event rev ID set" );
|
|
|
|
|
$this->assertGreaterThan( 0, $checkIds[1], "Second edit hook rev ID set" );
|
|
|
|
|
$this->assertGreaterThan( $checkIds[0], $checkIds[1], "Second event rev ID is higher" );
|
2012-11-10 16:24:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static function provideSectionEdit() {
|
2020-03-30 10:39:53 +00:00
|
|
|
$title = 'EditPageTest_testSectionEdit';
|
|
|
|
|
$title2 = Title::newFromText( __FUNCTION__ );
|
|
|
|
|
$title2->setContentModel( CONTENT_MODEL_CSS );
|
2013-02-14 11:22:13 +00:00
|
|
|
$text = 'Intro
|
2012-11-10 16:24:48 +00:00
|
|
|
|
|
|
|
|
== one ==
|
|
|
|
|
first section.
|
|
|
|
|
|
|
|
|
|
== two ==
|
|
|
|
|
second section.
|
|
|
|
|
';
|
|
|
|
|
|
2013-02-14 11:22:13 +00:00
|
|
|
$sectionOne = '== one ==
|
2012-11-10 16:24:48 +00:00
|
|
|
hello
|
|
|
|
|
';
|
|
|
|
|
|
2013-02-14 11:22:13 +00:00
|
|
|
$newSection = '== new section ==
|
2012-11-10 16:24:48 +00:00
|
|
|
|
|
|
|
|
hello
|
|
|
|
|
';
|
|
|
|
|
|
2013-02-14 11:22:13 +00:00
|
|
|
$textWithNewSectionOne = preg_replace(
|
|
|
|
|
'/== one ==.*== two ==/ms',
|
|
|
|
|
"$sectionOne\n== two ==", $text
|
|
|
|
|
);
|
2012-11-10 16:24:48 +00:00
|
|
|
|
|
|
|
|
$textWithNewSectionAdded = "$text\n$newSection";
|
|
|
|
|
|
2016-02-17 09:09:32 +00:00
|
|
|
return [
|
|
|
|
|
[ # 0
|
2020-03-30 10:39:53 +00:00
|
|
|
$title,
|
2012-11-10 16:24:48 +00:00
|
|
|
$text,
|
|
|
|
|
'',
|
|
|
|
|
'hello',
|
|
|
|
|
'replace all',
|
|
|
|
|
'hello'
|
2016-02-17 09:09:32 +00:00
|
|
|
],
|
2012-11-10 16:24:48 +00:00
|
|
|
|
2016-02-17 09:09:32 +00:00
|
|
|
[ # 1
|
2020-03-30 10:39:53 +00:00
|
|
|
$title,
|
2012-11-10 16:24:48 +00:00
|
|
|
$text,
|
|
|
|
|
'1',
|
|
|
|
|
$sectionOne,
|
|
|
|
|
'replace first section',
|
|
|
|
|
$textWithNewSectionOne,
|
2016-02-17 09:09:32 +00:00
|
|
|
],
|
2012-11-10 16:24:48 +00:00
|
|
|
|
2016-02-17 09:09:32 +00:00
|
|
|
[ # 2
|
2020-03-30 10:39:53 +00:00
|
|
|
$title,
|
2012-11-10 16:24:48 +00:00
|
|
|
$text,
|
|
|
|
|
'new',
|
|
|
|
|
'hello',
|
|
|
|
|
'new section',
|
|
|
|
|
$textWithNewSectionAdded,
|
2016-02-17 09:09:32 +00:00
|
|
|
],
|
2020-03-30 10:39:53 +00:00
|
|
|
|
|
|
|
|
[ # 3 Section edit not supported
|
|
|
|
|
$title2,
|
|
|
|
|
$text,
|
|
|
|
|
'1',
|
|
|
|
|
'hello',
|
|
|
|
|
'',
|
|
|
|
|
'',
|
|
|
|
|
],
|
2016-02-17 09:09:32 +00:00
|
|
|
];
|
2012-11-10 16:24:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @dataProvider provideSectionEdit
|
2023-05-27 09:43:12 +00:00
|
|
|
* @covers MediaWiki\EditPage\EditPage
|
2012-11-10 16:24:48 +00:00
|
|
|
*/
|
2020-03-30 10:39:53 +00:00
|
|
|
public function testSectionEdit( $title, $base, $section, $text, $summary, $expected ) {
|
2016-02-17 09:09:32 +00:00
|
|
|
$edit = [
|
2012-11-10 16:24:48 +00:00
|
|
|
'wpTextbox1' => $text,
|
|
|
|
|
'wpSummary' => $summary,
|
|
|
|
|
'wpSection' => $section,
|
2016-02-17 09:09:32 +00:00
|
|
|
];
|
2012-11-10 16:24:48 +00:00
|
|
|
|
2020-03-30 10:39:53 +00:00
|
|
|
$msg = "expected successful update of section";
|
|
|
|
|
$result = EditPage::AS_SUCCESS_UPDATE;
|
|
|
|
|
|
|
|
|
|
if ( $title instanceof Title ) {
|
|
|
|
|
$result = null;
|
|
|
|
|
$this->expectException( ErrorPageError::class );
|
|
|
|
|
}
|
|
|
|
|
$this->assertEdit( $title, $base, null, $edit, $result, $expected, $msg );
|
2012-11-10 16:24:48 +00:00
|
|
|
}
|
|
|
|
|
|
2020-03-05 21:23:07 +00:00
|
|
|
public static function provideConflictDetection() {
|
|
|
|
|
yield 'no conflict detected' => [
|
|
|
|
|
'Adam',
|
|
|
|
|
[
|
|
|
|
|
'wpEdittime' => 2, // use the second edit's time
|
|
|
|
|
'editRevId' => 2, // use the second edit's revision ID
|
|
|
|
|
],
|
|
|
|
|
EditPage::AS_SUCCESS_UPDATE,
|
|
|
|
|
'successful update expected'
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
yield 'conflict detected based on wpEdittime' => [
|
|
|
|
|
'Adam',
|
|
|
|
|
[
|
|
|
|
|
'wpEdittime' => 1, // use the first edit's time
|
|
|
|
|
],
|
|
|
|
|
EditPage::AS_CONFLICT_DETECTED,
|
|
|
|
|
'conflict expected'
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
yield 'conflict detected based on editRevId' => [
|
|
|
|
|
'Adam',
|
|
|
|
|
[
|
|
|
|
|
'editRevId' => 1, // use the first edit's revision ID
|
|
|
|
|
],
|
|
|
|
|
EditPage::AS_CONFLICT_DETECTED,
|
|
|
|
|
'conflict expected'
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
yield 'conflict based on wpEdittime ignored for same user' => [
|
|
|
|
|
'Berta',
|
|
|
|
|
[
|
|
|
|
|
'wpEdittime' => 1, // use the first edit's time
|
|
|
|
|
],
|
|
|
|
|
EditPage::AS_SUCCESS_UPDATE,
|
|
|
|
|
'successful update expected'
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
yield 'conflict detected based on editRevId even for same user' => [
|
|
|
|
|
'Berta',
|
|
|
|
|
[
|
|
|
|
|
'editRevId' => 1, // use the first edit's revision ID
|
|
|
|
|
],
|
|
|
|
|
EditPage::AS_CONFLICT_DETECTED,
|
|
|
|
|
'conflict expected'
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @dataProvider provideConflictDetection
|
2023-05-27 09:43:12 +00:00
|
|
|
* @covers MediaWiki\EditPage\EditPage
|
2020-03-05 21:23:07 +00:00
|
|
|
*/
|
|
|
|
|
public function testConflictDetection( $editUser, $newEdit, $expectedCode, $message ) {
|
|
|
|
|
// create page
|
|
|
|
|
$ns = $this->getDefaultWikitextNS();
|
|
|
|
|
$title = Title::newFromText( __METHOD__, $ns );
|
2022-01-12 20:13:39 +00:00
|
|
|
$wikiPageFactory = $this->getServiceContainer()->getWikiPageFactory();
|
2021-10-30 15:45:49 +00:00
|
|
|
$page = $wikiPageFactory->newFromTitle( $title );
|
2020-03-05 21:23:07 +00:00
|
|
|
|
|
|
|
|
if ( $page->exists() ) {
|
2021-10-30 15:45:49 +00:00
|
|
|
$this->deletePage( $page, "clean slate for testing" );
|
2020-03-05 21:23:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$elmosEdit['wpTextbox1'] = 'Elmo\'s text';
|
|
|
|
|
$bertasEdit['wpTextbox1'] = 'Berta\'s text';
|
|
|
|
|
$newEdit['wpTextbox1'] = 'new text';
|
|
|
|
|
|
|
|
|
|
$elmosEdit['wpSummary'] = 'Elmo\'s edit';
|
|
|
|
|
$bertasEdit['wpSummary'] = 'Bertas\'s edit';
|
2022-10-21 02:26:49 +00:00
|
|
|
$newEdit['wpSummary'] ??= 'new edit';
|
2020-03-05 21:23:07 +00:00
|
|
|
|
|
|
|
|
// first edit: Elmo
|
|
|
|
|
$page = $this->assertEdit( __METHOD__, null, 'Elmo', $elmosEdit,
|
|
|
|
|
EditPage::AS_SUCCESS_NEW_ARTICLE, null, 'expected successful creation' );
|
|
|
|
|
|
|
|
|
|
$this->forceRevisionDate( $page, '20120101000000' );
|
|
|
|
|
$rev1 = $page->getRevisionRecord();
|
|
|
|
|
|
|
|
|
|
// second edit: Berta
|
|
|
|
|
$page = $this->assertEdit( __METHOD__, null, 'Berta', $bertasEdit,
|
|
|
|
|
EditPage::AS_SUCCESS_UPDATE, null, 'expected successful update' );
|
|
|
|
|
|
|
|
|
|
$this->forceRevisionDate( $page, '20120101111111' );
|
|
|
|
|
$rev2 = $page->getRevisionRecord();
|
|
|
|
|
|
|
|
|
|
if ( !empty( $newEdit['editRevId'] ) ) {
|
|
|
|
|
$newEdit['editRevId'] = $newEdit['editRevId'] === 1 ? $rev1->getId() : $rev2->getId();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( !empty( $newEdit['wpEdittime'] ) ) {
|
|
|
|
|
$newEdit['wpEdittime'] =
|
|
|
|
|
$newEdit['wpEdittime'] === 1 ? $rev1->getTimestamp() : $rev2->getTimestamp();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// third edit
|
|
|
|
|
$this->assertEdit( __METHOD__, null, $editUser, $newEdit,
|
|
|
|
|
$expectedCode, null, $message );
|
|
|
|
|
}
|
|
|
|
|
|
2012-11-10 16:24:48 +00:00
|
|
|
public static function provideAutoMerge() {
|
2016-02-17 09:09:32 +00:00
|
|
|
$tests = [];
|
2012-11-10 16:24:48 +00:00
|
|
|
|
2016-02-17 09:09:32 +00:00
|
|
|
$tests[] = [ # 0: plain conflict
|
2012-11-10 16:24:48 +00:00
|
|
|
"Elmo", # base edit user
|
|
|
|
|
"one\n\ntwo\n\nthree\n",
|
2016-02-17 09:09:32 +00:00
|
|
|
[ # adam's edit
|
2012-11-10 16:24:48 +00:00
|
|
|
'wpTextbox1' => "ONE\n\ntwo\n\nthree\n",
|
2016-02-17 09:09:32 +00:00
|
|
|
],
|
|
|
|
|
[ # berta's edit
|
2012-11-10 16:24:48 +00:00
|
|
|
'wpTextbox1' => "(one)\n\ntwo\n\nthree\n",
|
2016-02-17 09:09:32 +00:00
|
|
|
],
|
2012-11-10 16:24:48 +00:00
|
|
|
EditPage::AS_CONFLICT_DETECTED, # expected code
|
|
|
|
|
"ONE\n\ntwo\n\nthree\n", # expected text
|
|
|
|
|
'expected edit conflict', # message
|
2016-02-17 09:09:32 +00:00
|
|
|
];
|
2012-11-10 16:24:48 +00:00
|
|
|
|
2016-02-17 09:09:32 +00:00
|
|
|
$tests[] = [ # 1: successful merge
|
2012-11-10 16:24:48 +00:00
|
|
|
"Elmo", # base edit user
|
|
|
|
|
"one\n\ntwo\n\nthree\n",
|
2016-02-17 09:09:32 +00:00
|
|
|
[ # adam's edit
|
2012-11-10 16:24:48 +00:00
|
|
|
'wpStarttime' => 1,
|
|
|
|
|
'wpTextbox1' => "ONE\n\ntwo\n\nthree\n",
|
2016-02-17 09:09:32 +00:00
|
|
|
],
|
|
|
|
|
[ # berta's edit
|
2012-11-10 16:24:48 +00:00
|
|
|
'wpStarttime' => 2,
|
|
|
|
|
'wpTextbox1' => "one\n\ntwo\n\nTHREE\n",
|
2016-02-17 09:09:32 +00:00
|
|
|
],
|
2012-11-10 16:24:48 +00:00
|
|
|
EditPage::AS_SUCCESS_UPDATE, # expected code
|
|
|
|
|
"ONE\n\ntwo\n\nTHREE\n", # expected text
|
|
|
|
|
'expected automatic merge', # message
|
2016-02-17 09:09:32 +00:00
|
|
|
];
|
2012-11-10 16:24:48 +00:00
|
|
|
|
|
|
|
|
$text = "Intro\n\n";
|
|
|
|
|
$text .= "== first section ==\n\n";
|
|
|
|
|
$text .= "one\n\ntwo\n\nthree\n\n";
|
|
|
|
|
$text .= "== second section ==\n\n";
|
|
|
|
|
$text .= "four\n\nfive\n\nsix\n\n";
|
|
|
|
|
|
|
|
|
|
// extract the first section.
|
|
|
|
|
$section = preg_replace( '/.*(== first section ==.*)== second section ==.*/sm', '$1', $text );
|
|
|
|
|
|
|
|
|
|
// generate expected text after merge
|
|
|
|
|
$expected = str_replace( 'one', 'ONE', str_replace( 'three', 'THREE', $text ) );
|
|
|
|
|
|
2016-02-17 09:09:32 +00:00
|
|
|
$tests[] = [ # 2: merge in section
|
2012-11-10 16:24:48 +00:00
|
|
|
"Elmo", # base edit user
|
|
|
|
|
$text,
|
2016-02-17 09:09:32 +00:00
|
|
|
[ # adam's edit
|
2012-11-10 16:24:48 +00:00
|
|
|
'wpTextbox1' => str_replace( 'one', 'ONE', $section ),
|
|
|
|
|
'wpSection' => '1'
|
2016-02-17 09:09:32 +00:00
|
|
|
],
|
|
|
|
|
[ # berta's edit
|
2012-11-10 16:24:48 +00:00
|
|
|
'wpTextbox1' => str_replace( 'three', 'THREE', $section ),
|
|
|
|
|
'wpSection' => '1'
|
2016-02-17 09:09:32 +00:00
|
|
|
],
|
2012-11-10 16:24:48 +00:00
|
|
|
EditPage::AS_SUCCESS_UPDATE, # expected code
|
|
|
|
|
$expected, # expected text
|
|
|
|
|
'expected automatic section merge', # message
|
2016-02-17 09:09:32 +00:00
|
|
|
];
|
2012-11-10 16:24:48 +00:00
|
|
|
|
|
|
|
|
// see whether it makes a difference who did the base edit
|
2021-02-06 19:40:52 +00:00
|
|
|
$testsWithAdam = array_map( static function ( $test ) {
|
2012-11-10 16:24:48 +00:00
|
|
|
$test[0] = 'Adam'; // change base edit user
|
|
|
|
|
return $test;
|
|
|
|
|
}, $tests );
|
|
|
|
|
|
2021-02-06 19:40:52 +00:00
|
|
|
$testsWithBerta = array_map( static function ( $test ) {
|
2012-11-10 16:24:48 +00:00
|
|
|
$test[0] = 'Berta'; // change base edit user
|
|
|
|
|
return $test;
|
|
|
|
|
}, $tests );
|
|
|
|
|
|
|
|
|
|
return array_merge( $tests, $testsWithAdam, $testsWithBerta );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @dataProvider provideAutoMerge
|
2023-05-27 09:43:12 +00:00
|
|
|
* @covers MediaWiki\EditPage\EditPage
|
2012-11-10 16:24:48 +00:00
|
|
|
*/
|
|
|
|
|
public function testAutoMerge( $baseUser, $text, $adamsEdit, $bertasEdit,
|
2013-02-14 11:22:13 +00:00
|
|
|
$expectedCode, $expectedText, $message = null
|
2012-11-10 16:24:48 +00:00
|
|
|
) {
|
2016-01-27 18:56:09 +00:00
|
|
|
$this->markTestSkippedIfNoDiff3();
|
2012-11-10 16:24:48 +00:00
|
|
|
|
2015-09-11 13:44:59 +00:00
|
|
|
// create page
|
2012-11-10 16:24:48 +00:00
|
|
|
$ns = $this->getDefaultWikitextNS();
|
|
|
|
|
$title = Title::newFromText( 'EditPageTest_testAutoMerge', $ns );
|
2022-01-12 20:13:39 +00:00
|
|
|
$wikiPageFactory = $this->getServiceContainer()->getWikiPageFactory();
|
2021-10-30 15:45:49 +00:00
|
|
|
$page = $wikiPageFactory->newFromTitle( $title );
|
2012-11-10 16:24:48 +00:00
|
|
|
|
|
|
|
|
if ( $page->exists() ) {
|
2021-10-30 15:45:49 +00:00
|
|
|
$this->deletePage( $page, "clean slate for testing" );
|
2012-11-10 16:24:48 +00:00
|
|
|
}
|
|
|
|
|
|
2016-02-17 09:09:32 +00:00
|
|
|
$baseEdit = [
|
2012-11-10 16:24:48 +00:00
|
|
|
'wpTextbox1' => $text,
|
2016-02-17 09:09:32 +00:00
|
|
|
];
|
2012-11-10 16:24:48 +00:00
|
|
|
|
|
|
|
|
$page = $this->assertEdit( 'EditPageTest_testAutoMerge', null,
|
2013-02-14 11:22:13 +00:00
|
|
|
$baseUser, $baseEdit, null, null, __METHOD__ );
|
2012-11-10 16:24:48 +00:00
|
|
|
|
|
|
|
|
$this->forceRevisionDate( $page, '20120101000000' );
|
|
|
|
|
|
|
|
|
|
$edittime = $page->getTimestamp();
|
2020-03-05 21:23:07 +00:00
|
|
|
$revId = $page->getLatest();
|
2012-11-10 16:24:48 +00:00
|
|
|
|
|
|
|
|
$adamsEdit['wpSummary'] = 'Adam\'s edit';
|
|
|
|
|
$bertasEdit['wpSummary'] = 'Bertas\'s edit';
|
|
|
|
|
|
|
|
|
|
$adamsEdit['wpEdittime'] = $edittime;
|
|
|
|
|
$bertasEdit['wpEdittime'] = $edittime;
|
|
|
|
|
|
2020-03-05 21:23:07 +00:00
|
|
|
$adamsEdit['editRevId'] = $revId;
|
|
|
|
|
$bertasEdit['editRevId'] = $revId;
|
|
|
|
|
|
2012-11-10 16:24:48 +00:00
|
|
|
// first edit
|
|
|
|
|
$this->assertEdit( 'EditPageTest_testAutoMerge', null, 'Adam', $adamsEdit,
|
2018-08-14 07:56:35 +00:00
|
|
|
EditPage::AS_SUCCESS_UPDATE, null, "expected successful update" );
|
2012-11-10 16:24:48 +00:00
|
|
|
|
|
|
|
|
// second edit
|
|
|
|
|
$this->assertEdit( 'EditPageTest_testAutoMerge', null, 'Berta', $bertasEdit,
|
|
|
|
|
$expectedCode, $expectedText, $message );
|
|
|
|
|
}
|
2015-04-15 08:26:22 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @depends testAutoMerge
|
2023-05-27 09:43:12 +00:00
|
|
|
* @covers MediaWiki\EditPage\EditPage
|
2015-04-15 08:26:22 +00:00
|
|
|
*/
|
|
|
|
|
public function testCheckDirectEditingDisallowed_forNonTextContent() {
|
2021-08-05 06:54:11 +00:00
|
|
|
$user = $this->getTestUser()->getUser();
|
|
|
|
|
|
2016-02-17 09:09:32 +00:00
|
|
|
$edit = [
|
2015-04-15 08:26:22 +00:00
|
|
|
'wpTextbox1' => serialize( 'non-text content' ),
|
2021-08-05 06:54:11 +00:00
|
|
|
'wpEditToken' => $user->getEditToken(),
|
2015-04-15 08:26:22 +00:00
|
|
|
'wpEdittime' => '',
|
2020-03-05 21:23:07 +00:00
|
|
|
'editRevId' => 0,
|
2017-08-28 21:14:44 +00:00
|
|
|
'wpStarttime' => wfTimestampNow(),
|
|
|
|
|
'wpUnicodeCheck' => EditPage::UNICODE_CHECK,
|
2016-02-17 09:09:32 +00:00
|
|
|
];
|
2015-04-15 08:26:22 +00:00
|
|
|
|
2019-10-03 22:20:49 +00:00
|
|
|
$this->expectException( MWException::class );
|
|
|
|
|
$this->expectExceptionMessage( 'This content model is not supported: testing' );
|
2015-04-15 08:26:22 +00:00
|
|
|
|
2019-05-18 16:31:55 +00:00
|
|
|
$this->doEditDummyNonTextPage( $edit );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** @covers EditPage */
|
|
|
|
|
public function testShouldPreventChangingContentModelWhenUserCannotChangeModelForTitle() {
|
|
|
|
|
$this->setTemporaryHook( 'getUserPermissionsErrors',
|
2021-02-07 13:10:36 +00:00
|
|
|
static function ( Title $page, $user, $action, &$result ) {
|
2019-05-18 16:31:55 +00:00
|
|
|
if ( $action === 'editcontentmodel' &&
|
2021-09-03 22:52:31 +00:00
|
|
|
$page->getContentModel() === CONTENT_MODEL_WIKITEXT
|
|
|
|
|
) {
|
2019-05-18 16:31:55 +00:00
|
|
|
$result = false;
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
} );
|
|
|
|
|
|
2021-08-05 06:54:11 +00:00
|
|
|
$user = $this->getTestUser()->getUser();
|
|
|
|
|
|
2019-05-18 16:31:55 +00:00
|
|
|
$status = $this->doEditDummyNonTextPage( [
|
|
|
|
|
'wpTextbox1' => 'some text',
|
2021-08-05 06:54:11 +00:00
|
|
|
'wpEditToken' => $user->getEditToken(),
|
2019-05-18 16:31:55 +00:00
|
|
|
'wpEdittime' => '',
|
2020-03-05 21:23:07 +00:00
|
|
|
'editRevId' => 0,
|
2019-05-18 16:31:55 +00:00
|
|
|
'wpStarttime' => wfTimestampNow(),
|
|
|
|
|
'wpUnicodeCheck' => EditPage::UNICODE_CHECK,
|
|
|
|
|
'model' => CONTENT_MODEL_WIKITEXT,
|
|
|
|
|
'format' => CONTENT_FORMAT_WIKITEXT,
|
|
|
|
|
] );
|
|
|
|
|
|
2022-03-04 22:00:02 +00:00
|
|
|
$this->assertStatusNotOK( $status );
|
|
|
|
|
$this->assertStatusValue( EditPage::AS_NO_CHANGE_CONTENT_MODEL, $status );
|
2015-04-15 08:26:22 +00:00
|
|
|
}
|
|
|
|
|
|
2019-05-18 16:31:55 +00:00
|
|
|
/** @covers EditPage */
|
|
|
|
|
public function testShouldPreventChangingContentModelWhenUserCannotEditTargetTitle() {
|
|
|
|
|
$this->setTemporaryHook( 'getUserPermissionsErrors',
|
2021-02-07 13:10:36 +00:00
|
|
|
static function ( Title $page, $user, $action, &$result ) {
|
2019-05-18 16:31:55 +00:00
|
|
|
if ( $action === 'edit' && $page->getContentModel() === CONTENT_MODEL_WIKITEXT ) {
|
|
|
|
|
$result = false;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
} );
|
|
|
|
|
|
2021-08-05 06:54:11 +00:00
|
|
|
$user = $this->getTestUser()->getUser();
|
|
|
|
|
|
2019-05-18 16:31:55 +00:00
|
|
|
$status = $this->doEditDummyNonTextPage( [
|
|
|
|
|
'wpTextbox1' => 'some text',
|
2021-08-05 06:54:11 +00:00
|
|
|
'wpEditToken' => $user->getEditToken(),
|
2019-05-18 16:31:55 +00:00
|
|
|
'wpEdittime' => '',
|
2020-03-05 21:23:07 +00:00
|
|
|
'editRevId' => 0,
|
2019-05-18 16:31:55 +00:00
|
|
|
'wpStarttime' => wfTimestampNow(),
|
|
|
|
|
'wpUnicodeCheck' => EditPage::UNICODE_CHECK,
|
|
|
|
|
'model' => CONTENT_MODEL_WIKITEXT,
|
|
|
|
|
'format' => CONTENT_FORMAT_WIKITEXT,
|
|
|
|
|
] );
|
|
|
|
|
|
2022-03-04 22:00:02 +00:00
|
|
|
$this->assertStatusNotOK( $status );
|
|
|
|
|
$this->assertStatusValue( EditPage::AS_NO_CHANGE_CONTENT_MODEL, $status );
|
2019-05-18 16:31:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function doEditDummyNonTextPage( array $edit ): Status {
|
|
|
|
|
$title = Title::newFromText( 'Dummy:NonTextPageForEditPage' );
|
|
|
|
|
|
|
|
|
|
$article = new Article( $title );
|
|
|
|
|
$article->getContext()->setTitle( $title );
|
|
|
|
|
$ep = new EditPage( $article );
|
|
|
|
|
$ep->setContextTitle( $title );
|
|
|
|
|
|
|
|
|
|
$req = new FauxRequest( $edit, true );
|
|
|
|
|
$ep->importFormData( $req );
|
|
|
|
|
|
|
|
|
|
return $ep->internalAttemptSave( $result, false );
|
|
|
|
|
}
|
2020-04-07 01:22:01 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* The watchlist expiry field should select the entered value on preview, rather than the
|
|
|
|
|
* calculated number of days till the expiry (as it shows on edit).
|
2023-05-27 09:43:12 +00:00
|
|
|
* @covers MediaWiki\EditPage\EditPage::getCheckboxesDefinition()
|
2020-04-07 01:22:01 +00:00
|
|
|
* @dataProvider provideWatchlistExpiry()
|
|
|
|
|
*/
|
|
|
|
|
public function testWatchlistExpiry( $existingExpiry, $postVal, $selected, $options ) {
|
|
|
|
|
// Set up config and fake current time.
|
2022-07-15 00:07:38 +00:00
|
|
|
$this->overrideConfigValue( MainConfigNames::WatchlistExpiry, true );
|
2020-04-07 01:22:01 +00:00
|
|
|
MWTimestamp::setFakeTime( '20200505120000' );
|
|
|
|
|
$user = $this->getTestUser()->getUser();
|
2020-12-17 23:10:11 +00:00
|
|
|
$this->assertTrue( $user->isRegistered() );
|
2020-04-07 01:22:01 +00:00
|
|
|
|
|
|
|
|
// Create the EditPage.
|
|
|
|
|
$title = Title::newFromText( __METHOD__ );
|
|
|
|
|
$context = new RequestContext();
|
|
|
|
|
$context->setUser( $user );
|
|
|
|
|
$context->setTitle( $title );
|
|
|
|
|
$article = new Article( $title );
|
|
|
|
|
$article->setContext( $context );
|
|
|
|
|
$ep = new EditPage( $article );
|
2021-04-13 03:03:50 +00:00
|
|
|
$this->getServiceContainer()->getWatchlistManager()
|
2021-04-21 04:25:46 +00:00
|
|
|
->setWatch( (bool)$existingExpiry, $user, $title, $existingExpiry );
|
2020-04-07 01:22:01 +00:00
|
|
|
|
|
|
|
|
// Send the request.
|
2023-04-20 12:07:03 +00:00
|
|
|
$req = new FauxRequest( [], true );
|
2020-04-07 01:22:01 +00:00
|
|
|
$context->setRequest( $req );
|
|
|
|
|
$req->getSession()->setUser( $user );
|
|
|
|
|
$ep->importFormData( $req );
|
2023-04-20 12:07:03 +00:00
|
|
|
$def = $ep->getCheckboxesDefinition( [ 'watch' => true, 'wpWatchlistExpiry' => $postVal ] )['wpWatchlistExpiry'];
|
2020-04-07 01:22:01 +00:00
|
|
|
|
|
|
|
|
// Test selected and available options.
|
|
|
|
|
$this->assertSame( $selected, $def['default'] );
|
|
|
|
|
$dropdownOptions = [];
|
|
|
|
|
foreach ( $def['options'] as $option ) {
|
|
|
|
|
// Reformat dropdown options for easier test comparison.
|
|
|
|
|
$dropdownOptions[] = $option['data'];
|
|
|
|
|
}
|
|
|
|
|
$this->assertSame( $options, $dropdownOptions );
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-23 11:36:19 +00:00
|
|
|
public static function provideWatchlistExpiry() {
|
2022-07-20 21:45:04 +00:00
|
|
|
$standardOptions = [ 'infinite', '1 week', '1 month', '3 months', '6 months', '1 year' ];
|
2020-04-07 01:22:01 +00:00
|
|
|
return [
|
|
|
|
|
'not watched, request nothing' => [
|
|
|
|
|
'existingExpiry' => '',
|
|
|
|
|
'postVal' => '',
|
|
|
|
|
'selected' => 'infinite',
|
|
|
|
|
'options' => $standardOptions,
|
|
|
|
|
],
|
|
|
|
|
'not watched' => [
|
|
|
|
|
'existingExpiry' => '',
|
|
|
|
|
'postVal' => '1 month',
|
|
|
|
|
'result' => '1 month',
|
|
|
|
|
'options' => $standardOptions,
|
|
|
|
|
],
|
|
|
|
|
'watched with current selected' => [
|
2020-09-04 08:27:21 +00:00
|
|
|
'existingExpiry' => '2020-05-05T12:00:01Z',
|
|
|
|
|
'postVal' => '2020-05-05T12:00:01Z',
|
|
|
|
|
'result' => '2020-05-05T12:00:01Z',
|
|
|
|
|
'options' => array_merge( [ '2020-05-05T12:00:01Z' ], $standardOptions ),
|
2020-04-07 01:22:01 +00:00
|
|
|
],
|
|
|
|
|
'watched with 1 week selected' => [
|
2020-09-04 08:27:21 +00:00
|
|
|
'existingExpiry' => '2020-05-05T12:00:02Z',
|
2020-04-07 01:22:01 +00:00
|
|
|
'postVal' => '1 week',
|
|
|
|
|
'result' => '1 week',
|
2020-09-04 08:27:21 +00:00
|
|
|
'options' => array_merge( [ '2020-05-05T12:00:02Z' ], $standardOptions ),
|
2020-04-07 01:22:01 +00:00
|
|
|
],
|
|
|
|
|
];
|
|
|
|
|
}
|
2020-03-30 10:39:53 +00:00
|
|
|
|
2021-05-05 01:36:37 +00:00
|
|
|
/**
|
|
|
|
|
* T277204
|
2023-05-27 09:43:12 +00:00
|
|
|
* @covers MediaWiki\EditPage\EditPage
|
2021-05-05 01:36:37 +00:00
|
|
|
*/
|
|
|
|
|
public function testFalseyEditRevId() {
|
|
|
|
|
$elmosEdit['wpTextbox1'] = 'Elmo\'s text';
|
|
|
|
|
$bertasEdit['wpTextbox1'] = 'Berta\'s text';
|
|
|
|
|
|
|
|
|
|
$elmosEdit['wpSummary'] = 'Elmo\'s edit';
|
|
|
|
|
$bertasEdit['wpSummary'] = 'Bertas\'s edit';
|
|
|
|
|
|
|
|
|
|
$bertasEdit['editRevId'] = 0;
|
|
|
|
|
|
|
|
|
|
$this->assertEdit( __METHOD__,
|
|
|
|
|
null, 'Elmo', $elmosEdit,
|
|
|
|
|
EditPage::AS_SUCCESS_NEW_ARTICLE, null, 'expected successful creation' );
|
|
|
|
|
|
|
|
|
|
// A successful update would probably be OK too. The important thing is
|
|
|
|
|
// that it doesn't throw an exception.
|
|
|
|
|
$this->assertEdit( __METHOD__, null, 'Berta', $bertasEdit,
|
|
|
|
|
EditPage::AS_CONFLICT_DETECTED, null, 'expected successful update' );
|
|
|
|
|
}
|
|
|
|
|
|
2011-12-06 23:35:42 +00:00
|
|
|
}
|