Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 75
0.00% covered (danger)
0.00%
0 / 25
CRAP
0.00% covered (danger)
0.00%
0 / 1
MessageBundleMessageGroup
0.00% covered (danger)
0.00%
0 / 75
0.00% covered (danger)
0.00%
0 / 25
1260
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
 getGroupId
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getBundlePageId
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getData
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 1
30
 makeGroupKeys
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 getId
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getLabel
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getDescription
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
12
 getIcon
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getNamespace
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 isMeta
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 exists
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getValidator
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getMangler
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 initCollection
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 load
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getDefinitions
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
 getKeys
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getTags
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getMessage
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 getSourceLanguage
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getMessageGroupStates
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 getTranslatableLanguages
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getSupportConfig
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getRelatedPage
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3declare( strict_types = 1 );
4
5namespace MediaWiki\Extension\Translate\MessageBundleTranslation;
6
7use LogicException;
8use MediaWiki\Context\IContextSource;
9use MediaWiki\Extension\Translate\MessageGroupProcessing\MessageGroupStates;
10use MediaWiki\Extension\Translate\MessageLoading\MessageCollection;
11use MediaWiki\Extension\Translate\MessageLoading\MessageDefinitions;
12use MediaWiki\Extension\Translate\MessageProcessing\StringMatcher;
13use MediaWiki\Extension\Translate\Services;
14use MediaWiki\Extension\Translate\Validation\ValidationRunner;
15use MediaWiki\Linker\LinkTarget;
16use MediaWiki\MediaWikiServices;
17use MediaWiki\Revision\SlotRecord;
18use MediaWiki\Title\Title;
19use MessageGroup;
20use const NS_TRANSLATIONS;
21
22/**
23 * @author Niklas Laxström
24 * @license GPL-2.0-or-later
25 * @since 2021.12
26 */
27class MessageBundleMessageGroup implements MessageGroup {
28    /** Name of the bundle (prefixed text of the bundle page) */
29    private string $name;
30    private string $groupId;
31    private int $pageId;
32    private int $revisionId;
33    private ?array $data = null;
34    private ?string $description;
35    private ?string $label;
36    private ?Title $title;
37
38    public function __construct(
39        string $groupId,
40        string $name,
41        int $pageId,
42        int $revisionId,
43        ?string $description,
44        ?string $label
45    ) {
46        $this->groupId = $groupId;
47        $this->name = $name;
48        $this->pageId = $pageId;
49        $this->revisionId = $revisionId;
50        $this->description = $description;
51        $this->label = $label;
52    }
53
54    /** Suggested default naming pattern */
55    public static function getGroupId( string $name ): string {
56        return "messagebundle-$name";
57    }
58
59    public function getBundlePageId(): int {
60        return $this->pageId;
61    }
62
63    private function getData(): array {
64        if ( $this->data !== null ) {
65            return $this->data;
66        }
67
68        $revisionStore = MediaWikiServices::getInstance()->getRevisionStore();
69        $revision = $revisionStore->getRevisionById( $this->revisionId );
70
71        if ( $revision === null ) {
72            throw new LogicException( "Could not find revision id $this->revisionId" );
73        }
74
75        $content = $revision->getContent( SlotRecord::MAIN );
76        if ( !$content instanceof MessageBundleContent ) {
77            throw new LogicException(
78                "Content with revision id $this->revisionId has wrong content format"
79            );
80        }
81
82        $data = json_decode( $content->getText(), true );
83        if ( !$data ) {
84            throw new LogicException(
85                "Content with revision id $this->revisionId is not valid JSON"
86            );
87        }
88
89        $this->data = $data;
90        return $this->data;
91    }
92
93    private function makeGroupKeys( array $keys ): array {
94        $result = [];
95        foreach ( $keys as $key ) {
96            $result[] = str_replace( ' ', '_', "$this->name/$key" );
97        }
98        return $result;
99    }
100
101    /** @inheritDoc */
102    public function getId(): string {
103        return $this->groupId;
104    }
105
106    /** @inheritDoc */
107    public function getLabel( ?IContextSource $context = null ): string {
108        return $this->label ?? $this->name;
109    }
110
111    /** @inheritDoc */
112    public function getDescription( ?IContextSource $context = null ): string {
113        $titleText = Title::newFromID( $this->pageId )->getPrefixedText();
114        $linkTargetText = ":$titleText";
115        if ( $context ) {
116            $message = $context->msg( 'translate-messagebundle-group-description' );
117        } else {
118            $message = wfMessage( 'translate-messagebundle-group-description' )
119                ->inContentLanguage();
120        }
121
122        $plainMessage = $message->params( $titleText, $linkTargetText )->plain();
123
124        if ( $this->description === null ) {
125            return $plainMessage;
126        }
127
128        return $plainMessage . ' ' . $this->description;
129    }
130
131    /** @inheritDoc */
132    public function getIcon(): ?string {
133        return null;
134    }
135
136    /** @inheritDoc */
137    public function getNamespace(): int {
138        return NS_TRANSLATIONS;
139    }
140
141    /** @inheritDoc */
142    public function isMeta(): bool {
143        return false;
144    }
145
146    /** @inheritDoc */
147    public function exists(): bool {
148        return true;
149    }
150
151    /** @inheritDoc */
152    public function getValidator(): ?ValidationRunner {
153        return null;
154    }
155
156    /** @inheritDoc */
157    public function getMangler(): ?StringMatcher {
158        return null;
159    }
160
161    /** @inheritDoc */
162    public function initCollection( $code ): MessageCollection {
163        $defs = new MessageDefinitions( $this->getDefinitions(), $this->getNamespace() );
164        $collection = MessageCollection::newFromDefinitions( $defs, $code );
165
166        foreach ( $this->getTags() as $type => $tags ) {
167            $collection->setTags( $type, $tags );
168        }
169
170        return $collection;
171    }
172
173    /** @inheritDoc */
174    public function load( $code ): array {
175        return [];
176    }
177
178    /** @inheritDoc */
179    public function getDefinitions(): array {
180        $data = $this->getData();
181        unset( $data['@metadata'] );
182
183        return array_combine(
184            $this->makeGroupKeys( array_keys( $data ) ),
185            array_values( $data )
186        );
187    }
188
189    /** @inheritDoc */
190    public function getKeys(): array {
191        return array_keys( $this->getDefinitions() );
192    }
193
194    /** @inheritDoc */
195    public function getTags( $type = null ): array {
196        return [];
197    }
198
199    /** @inheritDoc */
200    public function getMessage( $key, $code ): ?string {
201        if ( $code === $this->getSourceLanguage() ) {
202            return $this->getDefinitions()[$key] ?? null;
203        }
204
205        return null;
206    }
207
208    /** @inheritDoc */
209    public function getSourceLanguage(): string {
210        return Title::newFromText( $this->name )->getPageLanguage()->getCode();
211    }
212
213    /** @inheritDoc */
214    public function getMessageGroupStates(): MessageGroupStates {
215        global $wgTranslateWorkflowStates;
216        $conf = $wgTranslateWorkflowStates ?: [];
217
218        Services::getInstance()->getHookRunner()
219            ->onTranslate_modifyMessageGroupStates( $this->getId(), $conf );
220
221        return new MessageGroupStates( $conf );
222    }
223
224    /** @inheritDoc */
225    public function getTranslatableLanguages(): ?array {
226        return null;
227    }
228
229    /** @inheritDoc */
230    public function getSupportConfig(): ?array {
231        return null;
232    }
233
234    /** @inheritDoc */
235    public function getRelatedPage(): ?LinkTarget {
236        $this->title ??= Title::newFromID( $this->pageId );
237        return $this->title;
238    }
239}