Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 67 |
|
0.00% |
0 / 5 |
CRAP | |
0.00% |
0 / 1 |
SubpageListBuilder | |
0.00% |
0 / 67 |
|
0.00% |
0 / 5 |
552 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
getSubpagesPerType | |
0.00% |
0 / 37 |
|
0.00% |
0 / 1 |
132 | |||
getEmptyResultSet | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
2 | |||
filterOtherTranslationPages | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
20 | |||
getTalkPages | |
0.00% |
0 / 12 |
|
0.00% |
0 / 1 |
42 |
1 | <?php |
2 | declare( strict_types = 1 ); |
3 | |
4 | namespace MediaWiki\Extension\Translate\MessageGroupProcessing; |
5 | |
6 | use MediaWiki\Cache\LinkBatchFactory; |
7 | use MediaWiki\Extension\Translate\Utilities\Utilities; |
8 | use 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 | */ |
17 | class 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 | } |