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