2021-08-20 19:56:10 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
namespace MediaWiki\Tests\Unit\Page;
|
|
|
|
|
|
2021-10-18 13:19:15 +00:00
|
|
|
use BadMethodCallException;
|
DeletePage: add option to delete the associated talk page
Currently this is implemented internally as a second method call. In the
future, it might be possible to optimize the implementation, e.g. to
reduce the amount of DB queries/jobs etc. without changing anything for
callers.
Since the implementation of e.g. the result getters is generic, a future
commit may add an option to delete subpages with relatively low effort.
The revision limit for big deletions is now using the total number of
revisions (base page + talk page). This is because both deletions would
happen in the same main transaction, and we presumably want to optimize
the deletion as a whole, not as two separated steps. This would become
even more important if the aforementioned improvements are ever
implemented. Note, the whole concept of "big deletion" might have been
superseded by batched deletions in the author's opinion, but as long as
such a limit exists, it should serve its purpose.
The current interpretation of the associated talk option is that the
request is validated, and an exception is raised if we cannot delete the
associated talk, as opposed to silently ignoring the parameter if the
talk cannot be deleted. The only exception to this is that it will not
fail hard if the talk page turns out not to exist by the time the
deletion is attempted, as that's hard to foresee due to race conditions.
Note that the summary for the talk deletion is prefixed with the
delete-talk-summary-prefix message. The main reason behind this is that
the delete UI can sometimes offer an auto-generated summary like "the
only contributor was XXX" or "the content was YYY", and since this may
not apply to the talk page as well, the log entry might look confusing.
Bug: T263209
Bug: T27471
Change-Id: Ife1f4e8258a69528dd1ce8fef8ae809761aa6f1d
2021-09-01 12:56:43 +00:00
|
|
|
use Generator;
|
2021-08-20 19:56:10 +00:00
|
|
|
use JobQueueGroup;
|
2021-09-08 22:07:01 +00:00
|
|
|
use MediaWiki\Cache\BacklinkCacheFactory;
|
2023-12-04 11:27:42 +00:00
|
|
|
use MediaWiki\CommentStore\CommentStore;
|
2021-08-20 19:56:10 +00:00
|
|
|
use MediaWiki\Config\ServiceOptions;
|
DeletePage: add option to delete the associated talk page
Currently this is implemented internally as a second method call. In the
future, it might be possible to optimize the implementation, e.g. to
reduce the amount of DB queries/jobs etc. without changing anything for
callers.
Since the implementation of e.g. the result getters is generic, a future
commit may add an option to delete subpages with relatively low effort.
The revision limit for big deletions is now using the total number of
revisions (base page + talk page). This is because both deletions would
happen in the same main transaction, and we presumably want to optimize
the deletion as a whole, not as two separated steps. This would become
even more important if the aforementioned improvements are ever
implemented. Note, the whole concept of "big deletion" might have been
superseded by batched deletions in the author's opinion, but as long as
such a limit exists, it should serve its purpose.
The current interpretation of the associated talk option is that the
request is validated, and an exception is raised if we cannot delete the
associated talk, as opposed to silently ignoring the parameter if the
talk cannot be deleted. The only exception to this is that it will not
fail hard if the talk page turns out not to exist by the time the
deletion is attempted, as that's hard to foresee due to race conditions.
Note that the summary for the talk deletion is prefixed with the
delete-talk-summary-prefix message. The main reason behind this is that
the delete UI can sometimes offer an auto-generated summary like "the
only contributor was XXX" or "the content was YYY", and since this may
not apply to the talk page as well, the log entry might look confusing.
Bug: T263209
Bug: T27471
Change-Id: Ife1f4e8258a69528dd1ce8fef8ae809761aa6f1d
2021-09-01 12:56:43 +00:00
|
|
|
use MediaWiki\Linker\LinkTarget;
|
2022-08-17 20:33:06 +00:00
|
|
|
use MediaWiki\MainConfigNames;
|
2021-08-20 19:56:10 +00:00
|
|
|
use MediaWiki\Page\DeletePage;
|
|
|
|
|
use MediaWiki\Page\PageIdentity;
|
DeletePage: add option to delete the associated talk page
Currently this is implemented internally as a second method call. In the
future, it might be possible to optimize the implementation, e.g. to
reduce the amount of DB queries/jobs etc. without changing anything for
callers.
Since the implementation of e.g. the result getters is generic, a future
commit may add an option to delete subpages with relatively low effort.
The revision limit for big deletions is now using the total number of
revisions (base page + talk page). This is because both deletions would
happen in the same main transaction, and we presumably want to optimize
the deletion as a whole, not as two separated steps. This would become
even more important if the aforementioned improvements are ever
implemented. Note, the whole concept of "big deletion" might have been
superseded by batched deletions in the author's opinion, but as long as
such a limit exists, it should serve its purpose.
The current interpretation of the associated talk option is that the
request is validated, and an exception is raised if we cannot delete the
associated talk, as opposed to silently ignoring the parameter if the
talk cannot be deleted. The only exception to this is that it will not
fail hard if the talk page turns out not to exist by the time the
deletion is attempted, as that's hard to foresee due to race conditions.
Note that the summary for the talk deletion is prefixed with the
delete-talk-summary-prefix message. The main reason behind this is that
the delete UI can sometimes offer an auto-generated summary like "the
only contributor was XXX" or "the content was YYY", and since this may
not apply to the talk page as well, the log entry might look confusing.
Bug: T263209
Bug: T27471
Change-Id: Ife1f4e8258a69528dd1ce8fef8ae809761aa6f1d
2021-09-01 12:56:43 +00:00
|
|
|
use MediaWiki\Page\PageIdentityValue;
|
2021-08-20 19:56:10 +00:00
|
|
|
use MediaWiki\Page\ProperPageIdentity;
|
2024-05-05 10:46:56 +00:00
|
|
|
use MediaWiki\Page\RedirectStore;
|
2021-08-20 19:56:10 +00:00
|
|
|
use MediaWiki\Page\WikiPageFactory;
|
|
|
|
|
use MediaWiki\Permissions\Authority;
|
|
|
|
|
use MediaWiki\Permissions\PermissionStatus;
|
|
|
|
|
use MediaWiki\Permissions\UltimateAuthority;
|
|
|
|
|
use MediaWiki\Revision\RevisionRecord;
|
|
|
|
|
use MediaWiki\Revision\RevisionStore;
|
|
|
|
|
use MediaWiki\Tests\Unit\Permissions\MockAuthorityTrait;
|
2023-09-18 14:17:28 +00:00
|
|
|
use MediaWiki\Title\NamespaceInfo;
|
2023-03-01 20:33:26 +00:00
|
|
|
use MediaWiki\Title\Title;
|
2021-08-20 19:56:10 +00:00
|
|
|
use MediaWiki\User\UserFactory;
|
|
|
|
|
use MediaWiki\User\UserIdentityValue;
|
|
|
|
|
use MediaWikiUnitTestCase;
|
|
|
|
|
use PHPUnit\Framework\MockObject\MockObject;
|
DeletePage: add option to delete the associated talk page
Currently this is implemented internally as a second method call. In the
future, it might be possible to optimize the implementation, e.g. to
reduce the amount of DB queries/jobs etc. without changing anything for
callers.
Since the implementation of e.g. the result getters is generic, a future
commit may add an option to delete subpages with relatively low effort.
The revision limit for big deletions is now using the total number of
revisions (base page + talk page). This is because both deletions would
happen in the same main transaction, and we presumably want to optimize
the deletion as a whole, not as two separated steps. This would become
even more important if the aforementioned improvements are ever
implemented. Note, the whole concept of "big deletion" might have been
superseded by batched deletions in the author's opinion, but as long as
such a limit exists, it should serve its purpose.
The current interpretation of the associated talk option is that the
request is validated, and an exception is raised if we cannot delete the
associated talk, as opposed to silently ignoring the parameter if the
talk cannot be deleted. The only exception to this is that it will not
fail hard if the talk page turns out not to exist by the time the
deletion is attempted, as that's hard to foresee due to race conditions.
Note that the summary for the talk deletion is prefixed with the
delete-talk-summary-prefix message. The main reason behind this is that
the delete UI can sometimes offer an auto-generated summary like "the
only contributor was XXX" or "the content was YYY", and since this may
not apply to the talk page as well, the log entry might look confusing.
Bug: T263209
Bug: T27471
Change-Id: Ife1f4e8258a69528dd1ce8fef8ae809761aa6f1d
2021-09-01 12:56:43 +00:00
|
|
|
use Wikimedia\Message\ITextFormatter;
|
2024-07-09 13:37:44 +00:00
|
|
|
use Wikimedia\ObjectCache\BagOStuff;
|
2021-09-18 18:00:05 +00:00
|
|
|
use Wikimedia\Rdbms\IDatabase;
|
2021-08-20 19:56:10 +00:00
|
|
|
use Wikimedia\Rdbms\IResultWrapper;
|
|
|
|
|
use Wikimedia\Rdbms\LBFactory;
|
2023-03-02 00:46:04 +00:00
|
|
|
use Wikimedia\Rdbms\SelectQueryBuilder;
|
2021-08-20 19:56:10 +00:00
|
|
|
use WikiPage;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @coversDefaultClass \MediaWiki\Page\DeletePage
|
|
|
|
|
*/
|
|
|
|
|
class DeletePageTest extends MediaWikiUnitTestCase {
|
|
|
|
|
use MockAuthorityTrait;
|
|
|
|
|
|
|
|
|
|
private function getMockPage(): WikiPage {
|
|
|
|
|
$ret = $this->createMock( WikiPage::class );
|
|
|
|
|
$ret->method( 'canExist' )->willReturn( true );
|
|
|
|
|
$ret->method( 'exists' )->willReturn( true );
|
|
|
|
|
$ret->method( 'getId' )->willReturn( 123 );
|
|
|
|
|
$ret->method( 'getRevisionRecord' )->willReturn( $this->createMock( RevisionRecord::class ) );
|
2022-07-23 19:22:19 +00:00
|
|
|
|
|
|
|
|
$title = $this->createMock( Title::class );
|
|
|
|
|
$title->method( 'getPrefixedText' )->willReturn( 'Foo' );
|
|
|
|
|
$title->method( 'getText' )->willReturn( 'Foo' );
|
|
|
|
|
$title->method( 'getDBkey' )->willReturn( 'Foo' );
|
|
|
|
|
$title->method( 'getNamespace' )->willReturn( 0 );
|
|
|
|
|
$ret->method( 'getTitle' )->willReturn( $title );
|
2021-08-20 19:56:10 +00:00
|
|
|
return $ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @return RevisionStore|MockObject
|
|
|
|
|
*/
|
|
|
|
|
private function getMockRevisionStore(): RevisionStore {
|
|
|
|
|
$ret = $this->createMock( RevisionStore::class );
|
|
|
|
|
$ret->method( 'getQueryInfo' )->willReturn( [ 'tables' => [], 'fields' => [], 'joins' => [] ] );
|
|
|
|
|
return $ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function getServiceOptions( int $deleteLimit = 100 ): ServiceOptions {
|
|
|
|
|
return new ServiceOptions(
|
|
|
|
|
DeletePage::CONSTRUCTOR_OPTIONS,
|
|
|
|
|
[
|
2022-08-17 20:33:06 +00:00
|
|
|
MainConfigNames::DeleteRevisionsBatchSize => 100,
|
|
|
|
|
MainConfigNames::DeleteRevisionsLimit => $deleteLimit
|
2021-08-20 19:56:10 +00:00
|
|
|
]
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function getDeletePage(
|
|
|
|
|
ProperPageIdentity $page,
|
2024-10-16 18:58:33 +00:00
|
|
|
?Authority $deleter = null,
|
|
|
|
|
?ServiceOptions $options = null,
|
|
|
|
|
?RevisionStore $revStore = null,
|
|
|
|
|
?WikiPageFactory $wpFactory = null,
|
|
|
|
|
?NamespaceInfo $nsInfo = null
|
2021-08-20 19:56:10 +00:00
|
|
|
): DeletePage {
|
DeletePage: add option to delete the associated talk page
Currently this is implemented internally as a second method call. In the
future, it might be possible to optimize the implementation, e.g. to
reduce the amount of DB queries/jobs etc. without changing anything for
callers.
Since the implementation of e.g. the result getters is generic, a future
commit may add an option to delete subpages with relatively low effort.
The revision limit for big deletions is now using the total number of
revisions (base page + talk page). This is because both deletions would
happen in the same main transaction, and we presumably want to optimize
the deletion as a whole, not as two separated steps. This would become
even more important if the aforementioned improvements are ever
implemented. Note, the whole concept of "big deletion" might have been
superseded by batched deletions in the author's opinion, but as long as
such a limit exists, it should serve its purpose.
The current interpretation of the associated talk option is that the
request is validated, and an exception is raised if we cannot delete the
associated talk, as opposed to silently ignoring the parameter if the
talk cannot be deleted. The only exception to this is that it will not
fail hard if the talk page turns out not to exist by the time the
deletion is attempted, as that's hard to foresee due to race conditions.
Note that the summary for the talk deletion is prefixed with the
delete-talk-summary-prefix message. The main reason behind this is that
the delete UI can sometimes offer an auto-generated summary like "the
only contributor was XXX" or "the content was YYY", and since this may
not apply to the talk page as well, the log entry might look confusing.
Bug: T263209
Bug: T27471
Change-Id: Ife1f4e8258a69528dd1ce8fef8ae809761aa6f1d
2021-09-01 12:56:43 +00:00
|
|
|
if ( !$wpFactory ) {
|
|
|
|
|
$wpFactory = $this->createMock( WikiPageFactory::class );
|
|
|
|
|
$wpFactory->method( 'newFromTitle' )->willReturn( $this->getMockPage() );
|
|
|
|
|
}
|
2021-08-20 19:56:10 +00:00
|
|
|
|
|
|
|
|
// NOTE: The following could be avoided if the relevant methods were return-typehinted
|
|
|
|
|
$db = $this->createMock( IDatabase::class );
|
|
|
|
|
$db->method( 'select' )->willReturn( $this->createMock( IResultWrapper::class ) );
|
2021-08-23 12:11:22 +00:00
|
|
|
$db->method( 'selectRowCount' )->willReturn( 42 );
|
2023-03-02 00:46:04 +00:00
|
|
|
$db->method( 'newSelectQueryBuilder' )->willReturn( new SelectQueryBuilder( $db ) );
|
|
|
|
|
|
2021-08-20 19:56:10 +00:00
|
|
|
$lbFactory = $this->createMock( LBFactory::class );
|
2023-02-28 15:20:36 +00:00
|
|
|
$lbFactory->method( 'getPrimaryDatabase' )->willReturn( $db );
|
2021-08-20 19:56:10 +00:00
|
|
|
|
|
|
|
|
$ret = new DeletePage(
|
|
|
|
|
$this->createHookContainer(),
|
|
|
|
|
$revStore ?? $this->getMockRevisionStore(),
|
|
|
|
|
$lbFactory,
|
|
|
|
|
$this->createMock( JobQueueGroup::class ),
|
|
|
|
|
$this->createMock( CommentStore::class ),
|
|
|
|
|
$options ?? $this->getServiceOptions(),
|
|
|
|
|
$this->createMock( BagOStuff::class ),
|
|
|
|
|
'wiki-id',
|
|
|
|
|
'req-foo-bar',
|
|
|
|
|
$wpFactory,
|
|
|
|
|
$this->createMock( UserFactory::class ),
|
2021-11-25 16:02:49 +00:00
|
|
|
$this->createMock( BacklinkCacheFactory::class ),
|
DeletePage: add option to delete the associated talk page
Currently this is implemented internally as a second method call. In the
future, it might be possible to optimize the implementation, e.g. to
reduce the amount of DB queries/jobs etc. without changing anything for
callers.
Since the implementation of e.g. the result getters is generic, a future
commit may add an option to delete subpages with relatively low effort.
The revision limit for big deletions is now using the total number of
revisions (base page + talk page). This is because both deletions would
happen in the same main transaction, and we presumably want to optimize
the deletion as a whole, not as two separated steps. This would become
even more important if the aforementioned improvements are ever
implemented. Note, the whole concept of "big deletion" might have been
superseded by batched deletions in the author's opinion, but as long as
such a limit exists, it should serve its purpose.
The current interpretation of the associated talk option is that the
request is validated, and an exception is raised if we cannot delete the
associated talk, as opposed to silently ignoring the parameter if the
talk cannot be deleted. The only exception to this is that it will not
fail hard if the talk page turns out not to exist by the time the
deletion is attempted, as that's hard to foresee due to race conditions.
Note that the summary for the talk deletion is prefixed with the
delete-talk-summary-prefix message. The main reason behind this is that
the delete UI can sometimes offer an auto-generated summary like "the
only contributor was XXX" or "the content was YYY", and since this may
not apply to the talk page as well, the log entry might look confusing.
Bug: T263209
Bug: T27471
Change-Id: Ife1f4e8258a69528dd1ce8fef8ae809761aa6f1d
2021-09-01 12:56:43 +00:00
|
|
|
$nsInfo ?? $this->createMock( NamespaceInfo::class ),
|
|
|
|
|
$this->createMock( ITextFormatter::class ),
|
2024-05-05 10:46:56 +00:00
|
|
|
$this->createMock( RedirectStore::class ),
|
2021-08-20 19:56:10 +00:00
|
|
|
$page,
|
DeletePage: add option to delete the associated talk page
Currently this is implemented internally as a second method call. In the
future, it might be possible to optimize the implementation, e.g. to
reduce the amount of DB queries/jobs etc. without changing anything for
callers.
Since the implementation of e.g. the result getters is generic, a future
commit may add an option to delete subpages with relatively low effort.
The revision limit for big deletions is now using the total number of
revisions (base page + talk page). This is because both deletions would
happen in the same main transaction, and we presumably want to optimize
the deletion as a whole, not as two separated steps. This would become
even more important if the aforementioned improvements are ever
implemented. Note, the whole concept of "big deletion" might have been
superseded by batched deletions in the author's opinion, but as long as
such a limit exists, it should serve its purpose.
The current interpretation of the associated talk option is that the
request is validated, and an exception is raised if we cannot delete the
associated talk, as opposed to silently ignoring the parameter if the
talk cannot be deleted. The only exception to this is that it will not
fail hard if the talk page turns out not to exist by the time the
deletion is attempted, as that's hard to foresee due to race conditions.
Note that the summary for the talk deletion is prefixed with the
delete-talk-summary-prefix message. The main reason behind this is that
the delete UI can sometimes offer an auto-generated summary like "the
only contributor was XXX" or "the content was YYY", and since this may
not apply to the talk page as well, the log entry might look confusing.
Bug: T263209
Bug: T27471
Change-Id: Ife1f4e8258a69528dd1ce8fef8ae809761aa6f1d
2021-09-01 12:56:43 +00:00
|
|
|
$deleter ?? $this->createMock( Authority::class )
|
2021-08-20 19:56:10 +00:00
|
|
|
);
|
|
|
|
|
$ret->setIsDeletePageUnitTest( true );
|
|
|
|
|
return $ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @covers ::deleteIfAllowed
|
|
|
|
|
* @covers ::authorizeDeletion
|
|
|
|
|
* @covers ::isBigDeletion
|
|
|
|
|
* @dataProvider providePermissions
|
|
|
|
|
*/
|
|
|
|
|
public function testPermissions(
|
|
|
|
|
Authority $authority,
|
|
|
|
|
bool $expectedGood,
|
2024-10-16 18:58:33 +00:00
|
|
|
?string $expectedMessage = null,
|
|
|
|
|
?ServiceOptions $options = null,
|
|
|
|
|
?RevisionStore $revStore = null
|
2021-08-20 19:56:10 +00:00
|
|
|
) {
|
|
|
|
|
$dp = $this->getDeletePage(
|
|
|
|
|
$this->getMockPage(),
|
|
|
|
|
$authority,
|
|
|
|
|
$options,
|
|
|
|
|
$revStore
|
|
|
|
|
);
|
|
|
|
|
$status = $dp->deleteIfAllowed( 'foobar' );
|
|
|
|
|
$this->assertSame( $expectedGood, $status->isGood() );
|
|
|
|
|
if ( $expectedMessage !== null ) {
|
2022-03-04 22:00:02 +00:00
|
|
|
$this->assertStatusError( $expectedMessage, $status );
|
2021-08-20 19:56:10 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function providePermissions(): iterable {
|
|
|
|
|
$cannotDeleteMsg = "You shall not delete!";
|
|
|
|
|
$cannotDeleteAuthority = $this->mockAnonAuthority(
|
|
|
|
|
static function (
|
|
|
|
|
string $permission,
|
DeletePage: add option to delete the associated talk page
Currently this is implemented internally as a second method call. In the
future, it might be possible to optimize the implementation, e.g. to
reduce the amount of DB queries/jobs etc. without changing anything for
callers.
Since the implementation of e.g. the result getters is generic, a future
commit may add an option to delete subpages with relatively low effort.
The revision limit for big deletions is now using the total number of
revisions (base page + talk page). This is because both deletions would
happen in the same main transaction, and we presumably want to optimize
the deletion as a whole, not as two separated steps. This would become
even more important if the aforementioned improvements are ever
implemented. Note, the whole concept of "big deletion" might have been
superseded by batched deletions in the author's opinion, but as long as
such a limit exists, it should serve its purpose.
The current interpretation of the associated talk option is that the
request is validated, and an exception is raised if we cannot delete the
associated talk, as opposed to silently ignoring the parameter if the
talk cannot be deleted. The only exception to this is that it will not
fail hard if the talk page turns out not to exist by the time the
deletion is attempted, as that's hard to foresee due to race conditions.
Note that the summary for the talk deletion is prefixed with the
delete-talk-summary-prefix message. The main reason behind this is that
the delete UI can sometimes offer an auto-generated summary like "the
only contributor was XXX" or "the content was YYY", and since this may
not apply to the talk page as well, the log entry might look confusing.
Bug: T263209
Bug: T27471
Change-Id: Ife1f4e8258a69528dd1ce8fef8ae809761aa6f1d
2021-09-01 12:56:43 +00:00
|
|
|
PageIdentity $page = null,
|
2021-08-20 19:56:10 +00:00
|
|
|
PermissionStatus $status = null
|
|
|
|
|
) use ( $cannotDeleteMsg ): bool {
|
|
|
|
|
if ( $permission === 'delete' ) {
|
|
|
|
|
if ( $status ) {
|
|
|
|
|
$status->fatal( $cannotDeleteMsg );
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
yield 'Cannot delete' => [ $cannotDeleteAuthority, false, $cannotDeleteMsg ];
|
|
|
|
|
|
DeletePage: add option to delete the associated talk page
Currently this is implemented internally as a second method call. In the
future, it might be possible to optimize the implementation, e.g. to
reduce the amount of DB queries/jobs etc. without changing anything for
callers.
Since the implementation of e.g. the result getters is generic, a future
commit may add an option to delete subpages with relatively low effort.
The revision limit for big deletions is now using the total number of
revisions (base page + talk page). This is because both deletions would
happen in the same main transaction, and we presumably want to optimize
the deletion as a whole, not as two separated steps. This would become
even more important if the aforementioned improvements are ever
implemented. Note, the whole concept of "big deletion" might have been
superseded by batched deletions in the author's opinion, but as long as
such a limit exists, it should serve its purpose.
The current interpretation of the associated talk option is that the
request is validated, and an exception is raised if we cannot delete the
associated talk, as opposed to silently ignoring the parameter if the
talk cannot be deleted. The only exception to this is that it will not
fail hard if the talk page turns out not to exist by the time the
deletion is attempted, as that's hard to foresee due to race conditions.
Note that the summary for the talk deletion is prefixed with the
delete-talk-summary-prefix message. The main reason behind this is that
the delete UI can sometimes offer an auto-generated summary like "the
only contributor was XXX" or "the content was YYY", and since this may
not apply to the talk page as well, the log entry might look confusing.
Bug: T263209
Bug: T27471
Change-Id: Ife1f4e8258a69528dd1ce8fef8ae809761aa6f1d
2021-09-01 12:56:43 +00:00
|
|
|
$cannotBigDeleteMsg = 'delete-toomanyrevisions';
|
2021-08-20 19:56:10 +00:00
|
|
|
$cannotBigDeleteAuthority = $this->mockAnonAuthority(
|
|
|
|
|
static function (
|
|
|
|
|
string $permission,
|
DeletePage: add option to delete the associated talk page
Currently this is implemented internally as a second method call. In the
future, it might be possible to optimize the implementation, e.g. to
reduce the amount of DB queries/jobs etc. without changing anything for
callers.
Since the implementation of e.g. the result getters is generic, a future
commit may add an option to delete subpages with relatively low effort.
The revision limit for big deletions is now using the total number of
revisions (base page + talk page). This is because both deletions would
happen in the same main transaction, and we presumably want to optimize
the deletion as a whole, not as two separated steps. This would become
even more important if the aforementioned improvements are ever
implemented. Note, the whole concept of "big deletion" might have been
superseded by batched deletions in the author's opinion, but as long as
such a limit exists, it should serve its purpose.
The current interpretation of the associated talk option is that the
request is validated, and an exception is raised if we cannot delete the
associated talk, as opposed to silently ignoring the parameter if the
talk cannot be deleted. The only exception to this is that it will not
fail hard if the talk page turns out not to exist by the time the
deletion is attempted, as that's hard to foresee due to race conditions.
Note that the summary for the talk deletion is prefixed with the
delete-talk-summary-prefix message. The main reason behind this is that
the delete UI can sometimes offer an auto-generated summary like "the
only contributor was XXX" or "the content was YYY", and since this may
not apply to the talk page as well, the log entry might look confusing.
Bug: T263209
Bug: T27471
Change-Id: Ife1f4e8258a69528dd1ce8fef8ae809761aa6f1d
2021-09-01 12:56:43 +00:00
|
|
|
PageIdentity $page = null,
|
2021-08-20 19:56:10 +00:00
|
|
|
PermissionStatus $status = null
|
|
|
|
|
) use ( $cannotBigDeleteMsg ): bool {
|
|
|
|
|
if ( $permission === 'bigdelete' ) {
|
|
|
|
|
if ( $status ) {
|
|
|
|
|
$status->fatal( $cannotBigDeleteMsg );
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
$revDeleteLimit = -1;
|
|
|
|
|
$cannotBigDeleteOptions = $this->getServiceOptions( $revDeleteLimit );
|
|
|
|
|
$revStore = $this->getMockRevisionStore();
|
|
|
|
|
$revStore->expects( $this->atLeastOnce() )
|
|
|
|
|
->method( 'countRevisionsByPageId' )
|
|
|
|
|
->willReturn( $revDeleteLimit + 42 );
|
|
|
|
|
yield 'Cannot bigdelete' => [
|
|
|
|
|
$cannotBigDeleteAuthority,
|
|
|
|
|
false,
|
|
|
|
|
$cannotBigDeleteMsg,
|
|
|
|
|
$cannotBigDeleteOptions,
|
|
|
|
|
$revStore
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
$successAuthority = new UltimateAuthority( new UserIdentityValue( 42, 'Deleter' ) );
|
|
|
|
|
yield 'Successful' => [ $successAuthority, true ];
|
|
|
|
|
}
|
2021-10-18 13:19:15 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @covers ::getSuccessfulDeletionsIDs
|
|
|
|
|
*/
|
|
|
|
|
public function testGetSuccessfulDeletionsIDs(): void {
|
|
|
|
|
$delPage = $this->getDeletePage(
|
|
|
|
|
$this->createMock( ProperPageIdentity::class ),
|
|
|
|
|
$this->createMock( Authority::class )
|
|
|
|
|
);
|
|
|
|
|
$delPage->deleteUnsafe( 'foo' );
|
2021-11-25 16:02:49 +00:00
|
|
|
$this->assertArrayHasKey( DeletePage::PAGE_BASE, $delPage->getSuccessfulDeletionsIDs() );
|
2021-10-18 13:19:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @covers ::getSuccessfulDeletionsIDs
|
|
|
|
|
*/
|
|
|
|
|
public function testGetSuccessfulDeletionsIDs__notAttempted(): void {
|
|
|
|
|
$delPage = $this->getDeletePage(
|
|
|
|
|
$this->createMock( ProperPageIdentity::class ),
|
|
|
|
|
$this->createMock( Authority::class )
|
|
|
|
|
);
|
|
|
|
|
$this->expectException( BadMethodCallException::class );
|
|
|
|
|
$delPage->getSuccessfulDeletionsIDs();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2021-11-25 16:02:49 +00:00
|
|
|
* @covers ::deletionsWereScheduled
|
2021-10-18 13:19:15 +00:00
|
|
|
*/
|
2021-11-25 16:02:49 +00:00
|
|
|
public function testDeletionsWereScheduled(): void {
|
2021-10-18 13:19:15 +00:00
|
|
|
$delPage = $this->getDeletePage(
|
|
|
|
|
$this->createMock( ProperPageIdentity::class ),
|
|
|
|
|
$this->createMock( Authority::class )
|
|
|
|
|
);
|
|
|
|
|
$delPage->deleteUnsafe( 'foo' );
|
2021-11-25 16:02:49 +00:00
|
|
|
$this->assertFalse( $delPage->deletionsWereScheduled()[DeletePage::PAGE_BASE] );
|
2021-10-18 13:19:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2021-11-25 16:02:49 +00:00
|
|
|
* @covers ::deletionsWereScheduled
|
2021-10-18 13:19:15 +00:00
|
|
|
*/
|
2021-11-25 16:02:49 +00:00
|
|
|
public function testDeletionsWereScheduled__notAttempted(): void {
|
2021-10-18 13:19:15 +00:00
|
|
|
$delPage = $this->getDeletePage(
|
|
|
|
|
$this->createMock( ProperPageIdentity::class ),
|
|
|
|
|
$this->createMock( Authority::class )
|
|
|
|
|
);
|
|
|
|
|
$this->expectException( BadMethodCallException::class );
|
2021-11-25 16:02:49 +00:00
|
|
|
$delPage->deletionsWereScheduled();
|
2021-10-18 13:19:15 +00:00
|
|
|
}
|
DeletePage: add option to delete the associated talk page
Currently this is implemented internally as a second method call. In the
future, it might be possible to optimize the implementation, e.g. to
reduce the amount of DB queries/jobs etc. without changing anything for
callers.
Since the implementation of e.g. the result getters is generic, a future
commit may add an option to delete subpages with relatively low effort.
The revision limit for big deletions is now using the total number of
revisions (base page + talk page). This is because both deletions would
happen in the same main transaction, and we presumably want to optimize
the deletion as a whole, not as two separated steps. This would become
even more important if the aforementioned improvements are ever
implemented. Note, the whole concept of "big deletion" might have been
superseded by batched deletions in the author's opinion, but as long as
such a limit exists, it should serve its purpose.
The current interpretation of the associated talk option is that the
request is validated, and an exception is raised if we cannot delete the
associated talk, as opposed to silently ignoring the parameter if the
talk cannot be deleted. The only exception to this is that it will not
fail hard if the talk page turns out not to exist by the time the
deletion is attempted, as that's hard to foresee due to race conditions.
Note that the summary for the talk deletion is prefixed with the
delete-talk-summary-prefix message. The main reason behind this is that
the delete UI can sometimes offer an auto-generated summary like "the
only contributor was XXX" or "the content was YYY", and since this may
not apply to the talk page as well, the log entry might look confusing.
Bug: T263209
Bug: T27471
Change-Id: Ife1f4e8258a69528dd1ce8fef8ae809761aa6f1d
2021-09-01 12:56:43 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param ProperPageIdentity $page
|
|
|
|
|
* @param WikiPageFactory $wpFactory
|
|
|
|
|
* @param NamespaceInfo|null $nsInfo
|
|
|
|
|
* @param string|null $expectedMsg
|
|
|
|
|
* @covers ::canProbablyDeleteAssociatedTalk
|
|
|
|
|
* @dataProvider provideAssociatedTalk
|
|
|
|
|
*/
|
|
|
|
|
public function testCanProbablyDeleteAssociatedTalk(
|
|
|
|
|
ProperPageIdentity $page,
|
|
|
|
|
WikiPageFactory $wpFactory,
|
|
|
|
|
?NamespaceInfo $nsInfo,
|
|
|
|
|
?string $expectedMsg
|
|
|
|
|
): void {
|
|
|
|
|
$delPage = $this->getDeletePage( $page, null, null, null, $wpFactory, $nsInfo );
|
|
|
|
|
|
|
|
|
|
$res = $delPage->canProbablyDeleteAssociatedTalk();
|
|
|
|
|
if ( $expectedMsg === null ) {
|
2022-03-04 22:00:02 +00:00
|
|
|
$this->assertStatusGood( $res );
|
DeletePage: add option to delete the associated talk page
Currently this is implemented internally as a second method call. In the
future, it might be possible to optimize the implementation, e.g. to
reduce the amount of DB queries/jobs etc. without changing anything for
callers.
Since the implementation of e.g. the result getters is generic, a future
commit may add an option to delete subpages with relatively low effort.
The revision limit for big deletions is now using the total number of
revisions (base page + talk page). This is because both deletions would
happen in the same main transaction, and we presumably want to optimize
the deletion as a whole, not as two separated steps. This would become
even more important if the aforementioned improvements are ever
implemented. Note, the whole concept of "big deletion" might have been
superseded by batched deletions in the author's opinion, but as long as
such a limit exists, it should serve its purpose.
The current interpretation of the associated talk option is that the
request is validated, and an exception is raised if we cannot delete the
associated talk, as opposed to silently ignoring the parameter if the
talk cannot be deleted. The only exception to this is that it will not
fail hard if the talk page turns out not to exist by the time the
deletion is attempted, as that's hard to foresee due to race conditions.
Note that the summary for the talk deletion is prefixed with the
delete-talk-summary-prefix message. The main reason behind this is that
the delete UI can sometimes offer an auto-generated summary like "the
only contributor was XXX" or "the content was YYY", and since this may
not apply to the talk page as well, the log entry might look confusing.
Bug: T263209
Bug: T27471
Change-Id: Ife1f4e8258a69528dd1ce8fef8ae809761aa6f1d
2021-09-01 12:56:43 +00:00
|
|
|
} else {
|
2022-03-04 22:00:02 +00:00
|
|
|
$this->assertStatusError( $expectedMsg, $res );
|
DeletePage: add option to delete the associated talk page
Currently this is implemented internally as a second method call. In the
future, it might be possible to optimize the implementation, e.g. to
reduce the amount of DB queries/jobs etc. without changing anything for
callers.
Since the implementation of e.g. the result getters is generic, a future
commit may add an option to delete subpages with relatively low effort.
The revision limit for big deletions is now using the total number of
revisions (base page + talk page). This is because both deletions would
happen in the same main transaction, and we presumably want to optimize
the deletion as a whole, not as two separated steps. This would become
even more important if the aforementioned improvements are ever
implemented. Note, the whole concept of "big deletion" might have been
superseded by batched deletions in the author's opinion, but as long as
such a limit exists, it should serve its purpose.
The current interpretation of the associated talk option is that the
request is validated, and an exception is raised if we cannot delete the
associated talk, as opposed to silently ignoring the parameter if the
talk cannot be deleted. The only exception to this is that it will not
fail hard if the talk page turns out not to exist by the time the
deletion is attempted, as that's hard to foresee due to race conditions.
Note that the summary for the talk deletion is prefixed with the
delete-talk-summary-prefix message. The main reason behind this is that
the delete UI can sometimes offer an auto-generated summary like "the
only contributor was XXX" or "the content was YYY", and since this may
not apply to the talk page as well, the log entry might look confusing.
Bug: T263209
Bug: T27471
Change-Id: Ife1f4e8258a69528dd1ce8fef8ae809761aa6f1d
2021-09-01 12:56:43 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function provideAssociatedTalk(): Generator {
|
|
|
|
|
$getWpFactory = function ( bool $talkExists ): WikiPageFactory {
|
|
|
|
|
$wpFactory = $this->createMock( WikiPageFactory::class );
|
2022-06-26 21:21:02 +00:00
|
|
|
$wpFactory->method( 'newFromTitle' )->willReturnCallback( function ( $t ) {
|
|
|
|
|
$title = Title::castFromPageReference( $t );
|
|
|
|
|
$wikiPage = $this->createMock( WikiPage::class );
|
|
|
|
|
$wikiPage->method( 'getTitle' )->willReturn( $title );
|
|
|
|
|
$wikiPage->method( 'getNamespace' )->willReturn( $title->getNamespace() );
|
|
|
|
|
return $wikiPage;
|
DeletePage: add option to delete the associated talk page
Currently this is implemented internally as a second method call. In the
future, it might be possible to optimize the implementation, e.g. to
reduce the amount of DB queries/jobs etc. without changing anything for
callers.
Since the implementation of e.g. the result getters is generic, a future
commit may add an option to delete subpages with relatively low effort.
The revision limit for big deletions is now using the total number of
revisions (base page + talk page). This is because both deletions would
happen in the same main transaction, and we presumably want to optimize
the deletion as a whole, not as two separated steps. This would become
even more important if the aforementioned improvements are ever
implemented. Note, the whole concept of "big deletion" might have been
superseded by batched deletions in the author's opinion, but as long as
such a limit exists, it should serve its purpose.
The current interpretation of the associated talk option is that the
request is validated, and an exception is raised if we cannot delete the
associated talk, as opposed to silently ignoring the parameter if the
talk cannot be deleted. The only exception to this is that it will not
fail hard if the talk page turns out not to exist by the time the
deletion is attempted, as that's hard to foresee due to race conditions.
Note that the summary for the talk deletion is prefixed with the
delete-talk-summary-prefix message. The main reason behind this is that
the delete UI can sometimes offer an auto-generated summary like "the
only contributor was XXX" or "the content was YYY", and since this may
not apply to the talk page as well, the log entry might look confusing.
Bug: T263209
Bug: T27471
Change-Id: Ife1f4e8258a69528dd1ce8fef8ae809761aa6f1d
2021-09-01 12:56:43 +00:00
|
|
|
} );
|
|
|
|
|
$wpFactory->method( 'newFromLinkTarget' )->willReturnCallback(
|
|
|
|
|
function ( LinkTarget $t ) use ( $talkExists ) {
|
2021-11-08 14:40:46 +00:00
|
|
|
$existingTalk = $this->createMock( WikiPage::class );
|
DeletePage: add option to delete the associated talk page
Currently this is implemented internally as a second method call. In the
future, it might be possible to optimize the implementation, e.g. to
reduce the amount of DB queries/jobs etc. without changing anything for
callers.
Since the implementation of e.g. the result getters is generic, a future
commit may add an option to delete subpages with relatively low effort.
The revision limit for big deletions is now using the total number of
revisions (base page + talk page). This is because both deletions would
happen in the same main transaction, and we presumably want to optimize
the deletion as a whole, not as two separated steps. This would become
even more important if the aforementioned improvements are ever
implemented. Note, the whole concept of "big deletion" might have been
superseded by batched deletions in the author's opinion, but as long as
such a limit exists, it should serve its purpose.
The current interpretation of the associated talk option is that the
request is validated, and an exception is raised if we cannot delete the
associated talk, as opposed to silently ignoring the parameter if the
talk cannot be deleted. The only exception to this is that it will not
fail hard if the talk page turns out not to exist by the time the
deletion is attempted, as that's hard to foresee due to race conditions.
Note that the summary for the talk deletion is prefixed with the
delete-talk-summary-prefix message. The main reason behind this is that
the delete UI can sometimes offer an auto-generated summary like "the
only contributor was XXX" or "the content was YYY", and since this may
not apply to the talk page as well, the log entry might look confusing.
Bug: T263209
Bug: T27471
Change-Id: Ife1f4e8258a69528dd1ce8fef8ae809761aa6f1d
2021-09-01 12:56:43 +00:00
|
|
|
$existingTalk->expects( $this->atLeastOnce() )->method( 'exists' )->willReturn( $talkExists );
|
|
|
|
|
return $existingTalk;
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
return $wpFactory;
|
|
|
|
|
};
|
2023-07-15 14:45:21 +00:00
|
|
|
$nsInfo = new NamespaceInfo( $this->createMock( ServiceOptions::class ), $this->createHookContainer(), [], [] );
|
DeletePage: add option to delete the associated talk page
Currently this is implemented internally as a second method call. In the
future, it might be possible to optimize the implementation, e.g. to
reduce the amount of DB queries/jobs etc. without changing anything for
callers.
Since the implementation of e.g. the result getters is generic, a future
commit may add an option to delete subpages with relatively low effort.
The revision limit for big deletions is now using the total number of
revisions (base page + talk page). This is because both deletions would
happen in the same main transaction, and we presumably want to optimize
the deletion as a whole, not as two separated steps. This would become
even more important if the aforementioned improvements are ever
implemented. Note, the whole concept of "big deletion" might have been
superseded by batched deletions in the author's opinion, but as long as
such a limit exists, it should serve its purpose.
The current interpretation of the associated talk option is that the
request is validated, and an exception is raised if we cannot delete the
associated talk, as opposed to silently ignoring the parameter if the
talk cannot be deleted. The only exception to this is that it will not
fail hard if the talk page turns out not to exist by the time the
deletion is attempted, as that's hard to foresee due to race conditions.
Note that the summary for the talk deletion is prefixed with the
delete-talk-summary-prefix message. The main reason behind this is that
the delete UI can sometimes offer an auto-generated summary like "the
only contributor was XXX" or "the content was YYY", and since this may
not apply to the talk page as well, the log entry might look confusing.
Bug: T263209
Bug: T27471
Change-Id: Ife1f4e8258a69528dd1ce8fef8ae809761aa6f1d
2021-09-01 12:56:43 +00:00
|
|
|
|
|
|
|
|
$talkPage = new PageIdentityValue( 42, NS_TALK, 'Test talk page', PageIdentity::LOCAL );
|
|
|
|
|
yield 'Talk page' => [ $talkPage, $getWpFactory( false ), $nsInfo, 'delete-error-associated-alreadytalk' ];
|
|
|
|
|
|
|
|
|
|
$nonTalkPage = new PageIdentityValue( 44, NS_MAIN, 'Test article', PageIdentity::LOCAL );
|
|
|
|
|
yield 'Article without talk page' =>
|
|
|
|
|
[ $nonTalkPage, $getWpFactory( false ), $nsInfo, 'delete-error-associated-doesnotexist' ];
|
|
|
|
|
|
|
|
|
|
yield 'Article with talk page' => [ $nonTalkPage, $getWpFactory( true ), $nsInfo, null ];
|
|
|
|
|
}
|
2021-08-20 19:56:10 +00:00
|
|
|
}
|