Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 82
0.00% covered (danger)
0.00%
0 / 19
CRAP
0.00% covered (danger)
0.00%
0 / 1
Entity
0.00% covered (danger)
0.00%
0 / 82
0.00% covered (danger)
0.00%
0 / 19
1482
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 getType
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getMessageNames
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getId
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getElection
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
6
 getChildren
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getDescendants
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
6
 loadMessages
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
12
 loadProperties
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 getRawMessage
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 getMessage
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
20
 parseMessage
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
12
 parseMessageInline
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getLangList
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 getProperty
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 getAllProperties
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 getConfXml
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getConfXmlEntityStuff
0.00% covered (danger)
0.00%
0 / 19
0.00% covered (danger)
0.00%
0 / 1
42
 getPropertyDumpExclusion
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace MediaWiki\Extension\SecurePoll\Entities;
4
5use MediaWiki\Extension\SecurePoll\Context;
6use MediaWiki\MediaWikiServices;
7use MediaWiki\Parser\Parser;
8use MediaWiki\SpecialPage\SpecialPage;
9use MediaWiki\Xml\Xml;
10
11/**
12 * There are three types of entity: elections, questions and options. The
13 * entity abstraction provides generic i18n support, allowing localised message
14 * text to be attached to the entity, without introducing a dependency on the
15 * editability of the MediaWiki namespace. Users are only allowed to edit messages
16 * for the elections that they administer.
17 *
18 * Entities also provide a persistent key/value pair interface for non-localised
19 * properties, and a descendant tree which is used to accelerate message loading.
20 */
21class Entity {
22    /** @var int|false */
23    public $id;
24    /** @var int|null */
25    public $electionId;
26    /** @var Context */
27    public $context;
28    /** @var array */
29    public $messagesLoaded = [];
30    /** @var array|null */
31    public $properties;
32    /** @var string */
33    public $type;
34
35    /**
36     * Create an entity of the given type. This is typically called from the
37     * child constructor.
38     * @param Context $context
39     * @param string $type
40     * @param array $info Associative array of entity info
41     */
42    public function __construct( $context, $type, $info ) {
43        $this->context = $context;
44        $this->type = $type;
45        $this->id = $info['id'] ?? false;
46        $this->electionId = $info['election'] ?? null;
47    }
48
49    /**
50     * Get the type of the entity.
51     * @return string
52     */
53    public function getType() {
54        return $this->type;
55    }
56
57    /**
58     * Get a list of localisable message names. This is used to provide the
59     * translate subpage with a list of messages to localise.
60     * @return array
61     */
62    public function getMessageNames() {
63        # STUB
64        return [];
65    }
66
67    /**
68     * Get the entity ID.
69     * @return int
70     */
71    public function getId() {
72        return $this->id;
73    }
74
75    /**
76     * Get the parent election
77     * @return Election|null
78     */
79    public function getElection() {
80        return $this->electionId !== null ? $this->context->getElection( $this->electionId ) : null;
81    }
82
83    /**
84     * Get the child entity objects. When the messages of an object are loaded,
85     * the messages of the children are loaded automatically, to reduce the
86     * query count.
87     *
88     * @return array
89     */
90    public function getChildren() {
91        return [];
92    }
93
94    /**
95     * Get all children, grandchildren, etc. in a single flat array of entity
96     * objects.
97     * @return array
98     */
99    public function getDescendants() {
100        $descendants = [];
101        $children = $this->getChildren();
102        foreach ( $children as $child ) {
103            $descendants[] = $child;
104            $descendants = array_merge( $descendants, $child->getDescendants() );
105        }
106
107        return $descendants;
108    }
109
110    /**
111     * Load messages for a given language. It's not generally necessary to call
112     * this since getMessage() does it automatically.
113     * @param string|false $lang
114     */
115    public function loadMessages( $lang = false ) {
116        if ( $lang === false ) {
117            $lang = reset( $this->context->languages );
118        }
119        $ids = [ $this->getId() ];
120        foreach ( $this->getDescendants() as $child ) {
121            $ids[] = $child->getId();
122        }
123        $this->context->getMessages( $lang, $ids );
124        $this->messagesLoaded[$lang] = true;
125    }
126
127    /**
128     * Load the properties for the entity. It is not generally necessary to
129     * call this function from another class since getProperty() does it
130     * automatically.
131     */
132    public function loadProperties() {
133        $properties = $this->context->getStore()->getProperties( [ $this->getId() ] );
134        if ( count( $properties ) ) {
135            $this->properties = reset( $properties );
136        } else {
137            $this->properties = [];
138        }
139    }
140
141    /**
142     * Get a message, or false if the message does not exist. Does not use
143     * the fallback sequence.
144     *
145     * @param string $name
146     * @param string $language
147     * @return string|false
148     */
149    public function getRawMessage( $name, $language ) {
150        if ( empty( $this->messagesLoaded[$language] ) ) {
151            $this->loadMessages( $language );
152        }
153
154        return $this->context->getMessage( $language, $this->getId(), $name );
155    }
156
157    /**
158     * Get a message, and go through the fallback sequence if it is not found.
159     * If the message is not found even after looking at all possible languages,
160     * a placeholder string is returned.
161     *
162     * @param string $name
163     * @return string
164     */
165    public function getMessage( $name ) {
166        foreach ( $this->context->languages as $language ) {
167            if ( empty( $this->messagesLoaded[$language] ) ) {
168                $this->loadMessages( $language );
169            }
170            $message = $this->getRawMessage( $name, $language );
171            if ( $message !== false ) {
172                return $message;
173            }
174        }
175
176        return "[$name]";
177    }
178
179    /**
180     * Get a message, and interpret it as wikitext, converting it to HTML.
181     * @param string $name
182     * @param bool $block
183     * @return string
184     */
185    public function parseMessage( $name, $block = true ) {
186        // phpcs:ignore MediaWiki.Usage.DeprecatedGlobalVariables.Deprecated$wgTitle
187        global $wgTitle;
188        $parserOptions = $this->context->getParserOptions();
189        if ( $wgTitle ) {
190            $title = $wgTitle;
191        } else {
192            $title = SpecialPage::getTitleFor( 'SecurePoll' );
193        }
194        $wikiText = $this->getMessage( $name );
195        $out = MediaWikiServices::getInstance()->getParser()->parse(
196            $wikiText,
197            $title,
198            $parserOptions
199        );
200
201        $html = $out->runOutputPipeline( $parserOptions, [ 'unwrap' => true ] )->getContentHolderText();
202        if ( !$block ) {
203            $html = Parser::stripOuterParagraph( $html );
204        }
205        return $html;
206    }
207
208    /**
209     * Get a message and convert it from wikitext to HTML, without <p> tags.
210     * @param string $name
211     * @return string
212     */
213    public function parseMessageInline( $name ) {
214        return $this->parseMessage( $name, false );
215    }
216
217    /**
218     * Get a list of languages for which we have translations, for this entity
219     * and its descendants.
220     * @return string[]
221     */
222    public function getLangList() {
223        $ids = [ $this->getId() ];
224        foreach ( $this->getDescendants() as $child ) {
225            $ids[] = $child->getId();
226        }
227
228        return $this->context->getStore()->getLangList( $ids );
229    }
230
231    /**
232     * Get a property value. If it does not exist, the $default parameter
233     * is passed back.
234     * @param string $name
235     * @param mixed $default
236     * @return bool|mixed
237     */
238    public function getProperty( $name, $default = false ) {
239        if ( $this->properties === null ) {
240            $this->loadProperties();
241        }
242
243        return $this->properties[$name] ?? $default;
244    }
245
246    /**
247     * Get all defined properties as an associative array
248     * @return array
249     */
250    public function getAllProperties() {
251        if ( $this->properties === null ) {
252            $this->loadProperties();
253        }
254
255        return $this->properties;
256    }
257
258    /**
259     * Get configuration XML. Overridden by most subclasses.
260     * @param array $params
261     * @return string
262     */
263    public function getConfXml( $params = [] ) {
264        return "<{$this->type}>\n" . $this->getConfXmlEntityStuff( $params ) . "</{$this->type}>\n";
265    }
266
267    /**
268     * Get an XML snippet giving the messages and properties
269     * @param array $params
270     * @return string
271     */
272    public function getConfXmlEntityStuff( $params = [] ) {
273        $s = Xml::element( 'id', [], (string)$this->getId() ) . "\n";
274        $excludedNames = $this->getPropertyDumpExclusion( $params );
275        foreach ( $this->getAllProperties() as $name => $value ) {
276            if ( !in_array( $name, $excludedNames ) ) {
277                $s .= Xml::element( 'property', [ 'name' => $name ], $value ) . "\n";
278            }
279        }
280        $langs = $params['langs'] ?? $this->context->languages;
281        foreach ( $this->getMessageNames() as $name ) {
282            foreach ( $langs as $lang ) {
283                $value = $this->getRawMessage( $name, $lang );
284                if ( $value !== false ) {
285                    $s .= Xml::element(
286                            'message',
287                            [
288                                'name' => $name,
289                                'lang' => $lang
290                            ],
291                            $value
292                        ) . "\n";
293                }
294            }
295        }
296
297        return $s;
298    }
299
300    /**
301     * Get property names which aren't included in an XML dump.
302     * Overloaded by Election.
303     * @param array $params
304     * @return array
305     */
306    public function getPropertyDumpExclusion( $params = [] ) {
307        return [];
308    }
309
310}