Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 67
0.00% covered (danger)
0.00%
0 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
SubpageListBuilder
0.00% covered (danger)
0.00%
0 / 67
0.00% covered (danger)
0.00%
0 / 5
552
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 getSubpagesPerType
0.00% covered (danger)
0.00%
0 / 37
0.00% covered (danger)
0.00%
0 / 1
132
 getEmptyResultSet
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
2
 filterOtherTranslationPages
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
20
 getTalkPages
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
42
1<?php
2declare( strict_types = 1 );
3
4namespace MediaWiki\Extension\Translate\MessageGroupProcessing;
5
6use MediaWiki\Cache\LinkBatchFactory;
7use MediaWiki\Extension\Translate\Utilities\Utilities;
8use MediaWiki\Title\Title;
9
10/**
11 * Generates list of subpages for the translatable bundle that can be
12 * moved or deleted
13 * @author Abijeet Patro
14 * @license GPL-2.0-or-later
15 * @since 2022.04
16 */
17class SubpageListBuilder {
18    private TranslatableBundleFactory $bundleFactory;
19    private LinkBatchFactory $linkBatchFactory;
20
21    public function __construct(
22        TranslatableBundleFactory $bundleFactory,
23        LinkBatchFactory $linkBatchFactory
24    ) {
25        $this->bundleFactory = $bundleFactory;
26        $this->linkBatchFactory = $linkBatchFactory;
27    }
28
29    public function getSubpagesPerType( TranslatableBundle $bundle, bool $fetchTalkPages ): array {
30        $classifiedSubPages = $this->getEmptyResultSet();
31
32        $classifiedSubPages['translationPages'] = $bundle->getTranslationPages();
33        $classifiedSubPages['translationUnitPages'] = $bundle->getTranslationUnitPages();
34
35        // It's possible that subpages may not be allowed and getSubpages will return an
36        // empty array but that's not a problem.
37        $allSubpages = $bundle->getTitle()->getSubpages();
38
39        // Index the subpages
40        $allSubpagesIndexed = [];
41        foreach ( $allSubpages as $page ) {
42            $allSubpagesIndexed[ $page->getPrefixedDBkey() ] = $page;
43        }
44
45        // Remove translation pages from subpages
46        foreach ( $classifiedSubPages[ 'translationPages' ] as $translationPage ) {
47            if ( isset( $allSubpagesIndexed[ $translationPage->getPrefixedDBkey() ] ) ) {
48                unset( $allSubpagesIndexed[ $translationPage->getPrefixedDBkey() ] );
49            }
50        }
51
52        // Remove subpages that are translatable bundles
53        foreach ( $allSubpagesIndexed as $index => $subpage ) {
54            if ( $this->bundleFactory->getBundle( $subpage ) ) {
55                $classifiedSubPages['translatableSubpages'][] = $subpage;
56                unset( $allSubpagesIndexed[$index] );
57            }
58        }
59
60        // Remove translation pages for translatable pages found
61        $allSubpagesIndexed = $this->filterOtherTranslationPages(
62            $allSubpagesIndexed, $classifiedSubPages['translatableSubpages']
63        );
64
65        $classifiedSubPages['normalSubpages'] = $allSubpagesIndexed;
66
67        if ( $fetchTalkPages && !$bundle->getTitle()->isTalkPage() ) {
68            // We don't fetch talk pages for translatable subpages
69            $talkPages = $this->getTalkPages(
70                array_merge(
71                    [ $bundle->getTitle() ],
72                    $classifiedSubPages['translationPages'],
73                    $classifiedSubPages['translationUnitPages'],
74                    $classifiedSubPages['normalSubpages']
75                )
76            );
77
78            $translatableTalkPages = [];
79            foreach ( $talkPages as $key => $talkPage ) {
80                if ( $talkPage === null ) {
81                    continue;
82                }
83
84                if ( $this->bundleFactory->getBundle( $talkPage ) ) {
85                    $translatableTalkPages[] = $talkPage;
86                    unset( $talkPages[$key] );
87                }
88            }
89
90            $classifiedSubPages['talkPages'] = $talkPages;
91            $classifiedSubPages['translatableTalkPages'] = $translatableTalkPages;
92        }
93
94        return $classifiedSubPages;
95    }
96
97    public function getEmptyResultSet(): array {
98        return [
99            'translationPages' => [],
100            'translatableSubpages' => [],
101            'translationUnitPages' => [],
102            'normalSubpages' => [],
103            'talkPages' => [],
104            'translatableTalkPages' => []
105        ];
106    }
107
108    /**
109     * Remove translation pages for translatable pages from the list of all pages
110     * @param Title[] $allPages
111     * @param Title[] $translatablePages
112     */
113    private function filterOtherTranslationPages( array $allPages, array $translatablePages ): array {
114        $mappedTranslatablePages = [];
115        foreach ( $translatablePages as $index => $page ) {
116            $mappedTranslatablePages[ $page->getText() ] = $index;
117        }
118
119        foreach ( $allPages as $prefixedDbKeyTitle => $subpage ) {
120            [ $key, ] = Utilities::figureMessage( $subpage->getText() );
121            if ( isset( $mappedTranslatablePages[ $key ] ) ) {
122                unset( $allPages[ $prefixedDbKeyTitle ] );
123            }
124        }
125
126        return $allPages;
127    }
128
129    /**
130     * To identify the talk pages, we first gather the possible talk pages into an array
131     * and then check that they exist. Title::exists perform a database check so we gather
132     * them into LinkBatch to reduce the performance impact.
133     * @param Title[] $pages
134     * @return Title[]
135     */
136    private function getTalkPages( array $pages ): array {
137        $lb = $this->linkBatchFactory->newLinkBatch();
138        $talkPageList = [];
139
140        foreach ( $pages as $page ) {
141            $talkPage = $page->getTalkPageIfDefined();
142            $talkPageList[ $page->getPrefixedDBkey() ] = $talkPage;
143            if ( $talkPage ) {
144                $lb->addObj( $talkPage );
145            }
146        }
147
148        $lb->setCaller( __METHOD__ )->execute();
149        foreach ( $talkPageList as $index => $talkPage ) {
150            if ( !$talkPage || !$talkPage->exists() ) {
151                $talkPageList[$index] = null;
152            }
153        }
154
155        return $talkPageList;
156    }
157}