34 private ?Title $title;
37 private string $reason;
39 private bool $doSubpages =
false;
41 private ?
string $code;
42 private BagOStuff $mainCache;
43 private PermissionManager $permissionManager;
46 private JobQueueGroup $jobQueueGroup;
47 private string $entityType;
48 private const PAGE_TITLE_MSG = [
49 'messagebundle' =>
'pt-deletepage-mb-title',
50 'translatablepage' =>
'pt-deletepage-tp-title',
51 'translationpage' =>
'pt-deletepage-lang-title'
53 private const WRAPPER_LEGEND_MSG = [
54 'messagebundle' =>
'pt-deletepage-mb-legend',
55 'translatablepage' =>
'pt-deletepage-tp-title',
56 'translationpage' =>
'pt-deletepage-tp-legend'
58 private const LOG_PAGE = [
59 'messagebundle' =>
'Special:Log/messagebundle',
60 'translatablepage' =>
'Special:Log/pagetranslation',
61 'translationpage' =>
'Special:Log/pagetranslation'
64 public function __construct(
66 PermissionManager $permissionManager,
69 JobQueueGroup $jobQueueGroup
71 parent::__construct(
'PageTranslationDeletePage',
'pagetranslation' );
72 $this->mainCache = $mainCache;
73 $this->permissionManager = $permissionManager;
74 $this->bundleFactory = $bundleFactory;
75 $this->subpageBuilder = $subpageBuilder;
76 $this->jobQueueGroup = $jobQueueGroup;
79 public function doesWrites() {
83 public function isListed() {
87 public function execute( $par ) {
88 $this->addHelpLink(
'Help:Deletion_and_undeletion' );
90 $request = $this->getRequest();
95 $this->text = $request->getVal(
'wpTitle', $par );
96 $this->title = Title::newFromText( $this->text );
97 $this->reason = $this->getDeleteReason( $request );
98 $this->doSubpages = $request->getBool(
'subpages' );
100 if ( !$this->doBasicChecks() ) {
104 $out = $this->getOutput();
107 $entityType = $this->identifyEntityType();
108 if ( !$entityType ) {
109 throw new ErrorPageError(
'pt-deletepage-invalid-title',
'pt-deletepage-invalid-text' );
111 $this->entityType = $entityType;
113 if ( $this->isTranslation() ) {
114 [ , $this->code ] = Utilities::figureMessage( $this->title->getText() );
120 $this->msg( self::PAGE_TITLE_MSG[ $this->entityType ], $this->title->getPrefixedText() )
123 if ( !$this->getUser()->isAllowed(
'pagetranslation' ) ) {
124 throw new PermissionsError(
'pagetranslation' );
128 $subactionText = $request->getText(
'subaction' );
129 switch ( $subactionText ) {
130 case $this->msg(
'pt-deletepage-action-check' )->text():
131 $subaction =
'check';
133 case $this->msg(
'pt-deletepage-action-perform' )->text():
134 $subaction =
'perform';
136 case $this->msg(
'pt-deletepage-action-other' )->text():
143 if ( $subaction ===
'check' && $this->checkToken() && $request->wasPosted() ) {
144 $this->showConfirmation();
145 } elseif ( $subaction ===
'perform' && $this->checkToken() && $request->wasPosted() ) {
146 $this->performAction();
157 private function doBasicChecks():
bool {
159 if ( !$this->userCanExecute( $this->getUser() ) ) {
160 $this->displayRestrictionError();
163 if ( $this->title ===
null ) {
164 throw new ErrorPageError(
'notargettitle',
'notargettext' );
167 if ( !$this->title->exists() ) {
168 throw new ErrorPageError(
'nopagetitle',
'nopagetext' );
171 $permissionErrors = $this->permissionManager->getPermissionErrors(
172 'delete', $this->getUser(), $this->title
174 if ( count( $permissionErrors ) ) {
175 throw new PermissionsError(
'delete', $permissionErrors );
178 # Check for database lock
179 $this->checkReadOnly();
189 private function checkToken():
bool {
190 return $this->getContext()->getCsrfTokenSet()->matchTokenField(
'wpEditToken' );
194 private function showForm():
void {
195 $out = $this->getOutput();
196 $out->addBacklinkSubtitle( $this->title );
197 $out->addWikiMsg(
'pt-deletepage-intro', self::LOG_PAGE[ $this->entityType ] );
199 $formDescriptor = $this->getCommonFormFields();
201 HTMLForm::factory(
'ooui', $formDescriptor, $this->getContext() )
202 ->setAction( $this->getPageTitle( $this->text )->getLocalURL() )
203 ->setSubmitName(
'subaction' )
204 ->setSubmitTextMsg(
'pt-deletepage-action-check' )
205 ->setWrapperLegendMsg(
'pt-deletepage-any-legend' )
207 ->displayForm(
false );
214 private function showConfirmation():
void {
215 $out = $this->getOutput();
219 $out->addBacklinkSubtitle( $this->title );
220 $out->addWikiMsg(
'pt-deletepage-intro', self::LOG_PAGE[ $this->entityType ] );
222 $subpages = $this->getPagesForDeletion();
224 $out->wrapWikiMsg(
'== $1 ==',
'pt-deletepage-list-pages' );
226 if ( !$this->isTranslation() ) {
228 $out->addWikiTextAsInterface(
229 $this->getChangeLine( $this->title )
233 $out->wrapWikiMsg(
'=== $1 ===',
'pt-deletepage-list-translation' );
235 foreach ( $subpages[
'translationPages' ] as $old ) {
237 $lines[] = $this->getChangeLine( $old );
239 $this->listPages( $out, $lines );
241 $out->wrapWikiMsg(
'=== $1 ===',
'pt-deletepage-list-section' );
244 foreach ( $subpages[
'translationUnitPages' ] as $old ) {
246 $lines[] = $this->getChangeLine( $old );
248 $this->listPages( $out, $lines );
250 if ( Utilities::allowsSubpages( $this->title ) ) {
251 $out->wrapWikiMsg(
'=== $1 ===',
'pt-deletepage-list-other' );
252 $subpages = $subpages[
'normalSubpages' ];
254 foreach ( $subpages as $old ) {
256 $lines[] = $this->getChangeLine( $old );
258 $this->listPages( $out, $lines );
261 $totalPageCount = $count + $subpageCount;
263 $out->addWikiTextAsInterface(
"----\n" );
265 'pt-deletepage-list-count',
266 $this->getLanguage()->formatNum( $totalPageCount ),
267 $this->getLanguage()->formatNum( $subpageCount )
270 $formDescriptor = $this->getCommonFormFields();
271 $formDescriptor[
'subpages'] = [
273 'name' =>
'subpages',
274 'id' =>
'mw-subpages',
275 'label' => $this->msg(
'pt-deletepage-subpages' )->text(),
276 'default' => $this->doSubpages,
279 $htmlForm = HTMLForm::factory(
'ooui', $formDescriptor, $this->getContext() );
281 ->setWrapperLegendMsg(
282 $this->msg( self::WRAPPER_LEGEND_MSG[ $this->entityType ], $this->title->getPrefixedText() )
284 ->setAction( $this->getPageTitle( $this->text )->getLocalURL() )
285 ->setSubmitTextMsg(
'pt-deletepage-action-perform' )
286 ->setSubmitName(
'subaction' )
287 ->setSubmitDestructive()
289 'name' =>
'subaction',
290 'value' => $this->msg(
'pt-deletepage-action-other' )->text(),
293 ->displayForm(
false );
297 private function getChangeLine( Title $title ):
string {
298 return '* ' . $title->getPrefixedText();
301 private function performAction():
void {
303 $target = $this->title;
304 $base = $this->title->getPrefixedText();
305 $isTranslation = $this->isTranslation();
306 $subpageList = $this->getPagesForDeletion();
307 $bundle = $this->getValidBundleFromTitle();
308 $bundleType = get_class( $bundle );
310 $user = $this->getUser();
311 foreach ( $subpageList[
'translationPages' ] as $old ) {
312 $jobs[$old->getPrefixedText()] = DeleteTranslatableBundleJob::newJob(
313 $old, $base, $bundleType, $isTranslation, $user, $this->reason
317 foreach ( $subpageList[
'translationUnitPages' ] as $old ) {
318 $jobs[$old->getPrefixedText()] = DeleteTranslatableBundleJob::newJob(
319 $old, $base, $bundleType, $isTranslation, $user, $this->reason
323 if ( $this->doSubpages ) {
324 foreach ( $subpageList[
'normalSubpages' ] as $old ) {
325 $jobs[$old->getPrefixedText()] = DeleteTranslatableBundleJob::newJob(
326 $old, $base, $bundleType, $isTranslation, $user, $this->reason
331 if ( !$isTranslation ) {
332 $jobs[$this->title->getPrefixedText()] = DeleteTranslatableBundleJob::newJob(
333 $this->title, $base, $bundleType, $isTranslation, $user, $this->reason
337 $this->jobQueueGroup->push( $jobs );
339 $this->mainCache->set(
340 $this->mainCache->makeKey(
'pt-base', $target->getPrefixedText() ),
342 6 * $this->mainCache::TTL_HOUR
345 if ( !$isTranslation ) {
346 $this->bundleFactory->getStore( $bundle )->delete( $this->title );
349 $this->getOutput()->addWikiMsg(
'pt-deletepage-started', self::LOG_PAGE[ $this->entityType ] );
352 private function getCommonFormFields(): array {
353 $dropdownOptions = $this->msg(
'deletereason-dropdown' )->inContentLanguage()->text();
355 $options = Xml::listDropDownOptions(
358 'other' => $this->msg(
'pt-deletepage-reason-other' )->inContentLanguage()->text()
366 'label-message' =>
'pt-deletepage-current',
368 'default' => $this->title->getPrefixedText(),
371 'wpDeleteReasonList' => [
373 'name' =>
'wpDeleteReasonList',
374 'label-message' =>
'pt-deletepage-reason',
375 'options' => $options,
379 'name' =>
'wpReason',
380 'label-message' =>
'pt-deletepage-reason-details',
381 'default' => $this->reason,
386 private function listPages( OutputPage $out, array $lines ):
void {
388 $out->addWikiTextAsInterface( implode(
"\n", $lines ) );
390 $out->addWikiMsg(
'pt-deletepage-list-no-pages' );
394 private function getDeleteReason( WebRequest $request ):
string {
395 $dropdownSelection = $request->getText(
'wpDeleteReasonList',
'other' );
396 $reasonInput = $request->getText(
'wpReason' );
398 if ( $dropdownSelection ===
'other' ) {
400 } elseif ( $reasonInput !==
'' ) {
402 $separator = $this->msg(
'colon-separator' )->inContentLanguage()->text();
403 return "$dropdownSelection$separator$reasonInput";
405 return $dropdownSelection;
409 private function getPagesForDeletion(): array {
410 if ( $this->isTranslation() ) {
411 $resultSet = $this->subpageBuilder->getEmptyResultSet();
413 [ $titleKey, ] = Utilities::figureMessage( $this->title->getPrefixedDBkey() );
416 $resultSet[
'translationPages'] = [ $this->title ];
417 $resultSet[
'translationUnitPages'] = $translatablePage->getTranslationUnitPages( $this->code );
420 $bundle = $this->bundleFactory->getValidBundle( $this->title );
421 return $this->subpageBuilder->getSubpagesPerType( $bundle,
false );
426 $bundleTitle = $this->title;
427 if ( $this->isTranslation() ) {
428 [ $key, ] = Utilities::figureMessage( $this->title->getPrefixedDBkey() );
429 $bundleTitle = Title::newFromText( $key );
432 return $this->bundleFactory->getValidBundle( $bundleTitle );
436 private function identifyEntityType(): ?
string {
437 $bundle = $this->bundleFactory->getBundle( $this->title );
440 return 'messagebundle';
442 return 'translatablepage';
445 return 'translationpage';
451 private function isTranslation():
bool {
452 return $this->entityType ===
'translationpage';