Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
58.62% covered (warning)
58.62%
51 / 87
73.33% covered (warning)
73.33%
11 / 15
CRAP
0.00% covered (danger)
0.00%
0 / 1
ZPersistentObject
58.62% covered (warning)
58.62%
51 / 87
73.33% covered (warning)
73.33%
11 / 15
88.59
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 getDefinition
0.00% covered (danger)
0.00%
0 / 28
0.00% covered (danger)
0.00%
0 / 1
2
 isValid
57.14% covered (warning)
57.14%
8 / 14
0.00% covered (danger)
0.00%
0 / 1
10.86
 getZValue
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 validateZMultilingualStringFieldLength
92.31% covered (success)
92.31%
12 / 13
0.00% covered (danger)
0.00%
0 / 1
6.02
 validateDescriptionLength
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 validateLabelLength
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getZid
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getInnerZObject
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getInternalZType
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getLabels
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getLabel
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
2
 getAliases
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getDescriptions
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getDescription
87.50% covered (warning)
87.50%
7 / 8
0.00% covered (danger)
0.00%
0 / 1
3.02
1<?php
2/**
3 * WikiLambda generic ZPersistentObject class
4 *
5 * @file
6 * @ingroup Extensions
7 * @copyright 2020– Abstract Wikipedia team; see AUTHORS.txt
8 * @license MIT
9 */
10
11namespace MediaWiki\Extension\WikiLambda\ZObjects;
12
13use MediaWiki\Context\IContextSource;
14use MediaWiki\Extension\WikiLambda\Registry\ZLangRegistry;
15use MediaWiki\Extension\WikiLambda\Registry\ZTypeRegistry;
16use MediaWiki\Extension\WikiLambda\ZObjectUtils;
17use MediaWiki\Language\Language;
18use MediaWiki\MediaWikiServices;
19use MessageLocalizer;
20
21class ZPersistentObject extends ZObject {
22
23    /** @var array */
24    protected $data = [];
25
26    /**
27     * Construct a ZPersistentObject instance
28     *
29     * @param ZObject $zid ZString representing the Zid that identifies this ZPersistentObject
30     * @param ZObject $value ZObject to be wrapped in this ZPersistentObject
31     * @param ZObject $label ZMultiLingualString that contains this ZPersistentObject's label
32     * @param ZObject|null $aliases ZMultiLingualStringSet with this ZPersistentObject's aliases or null
33     * @param ZObject|null $description ZMultiLingualString that contains this ZPersistentObject's description
34     */
35    public function __construct( $zid, $value, $label, $aliases = null, $description = null ) {
36        $aliases ??= new ZMultiLingualStringSet( [] );
37        $description ??= new ZMultiLingualString( [] );
38        $this->data[ ZTypeRegistry::Z_PERSISTENTOBJECT_ID ] = $zid;
39        $this->data[ ZTypeRegistry::Z_PERSISTENTOBJECT_VALUE ] = $value;
40        $this->data[ ZTypeRegistry::Z_PERSISTENTOBJECT_LABEL ] = $label;
41        $this->data[ ZTypeRegistry::Z_PERSISTENTOBJECT_ALIASES ] = $aliases;
42        $this->data[ ZTypeRegistry::Z_PERSISTENTOBJECT_DESCRIPTION ] = $description;
43    }
44
45    /**
46     * @inheritDoc
47     */
48    public static function getDefinition(): array {
49        return [
50            'type' => [
51                'type' => ZTypeRegistry::Z_REFERENCE,
52                'value' => ZTypeRegistry::Z_PERSISTENTOBJECT,
53            ],
54            'keys' => [
55                ZTypeRegistry::Z_PERSISTENTOBJECT_ID => [
56                    'type' => ZTypeRegistry::Z_STRING,
57                    'required' => true,
58                ],
59                ZTypeRegistry::Z_PERSISTENTOBJECT_VALUE => [
60                    'type' => ZTypeRegistry::Z_OBJECT,
61                    'required' => true,
62                ],
63                ZTypeRegistry::Z_PERSISTENTOBJECT_LABEL => [
64                    'type' => ZTypeRegistry::Z_MULTILINGUALSTRING,
65                    'required' => true,
66                ],
67                ZTypeRegistry::Z_PERSISTENTOBJECT_ALIASES => [
68                    'type' => ZTypeRegistry::Z_MULTILINGUALSTRINGSET,
69                    'required' => false,
70                ],
71                ZTypeRegistry::Z_PERSISTENTOBJECT_DESCRIPTION => [
72                    'type' => ZTypeRegistry::Z_MULTILINGUALSTRING,
73                    'required' => false,
74                ],
75            ],
76        ];
77    }
78
79    /**
80     * @inheritDoc
81     */
82    public function isValid(): bool {
83        if ( !( $this->data[ ZTypeRegistry::Z_PERSISTENTOBJECT_ID ] instanceof ZString ) ) {
84            return false;
85        }
86        $zid = $this->getZid();
87        if ( !ZObjectUtils::isValidOrNullZObjectReference( $zid ) ) {
88            return false;
89        }
90        if ( !( $this->data[ ZTypeRegistry::Z_PERSISTENTOBJECT_VALUE ] instanceof ZObject ) ) {
91            return false;
92        }
93        if ( !( $this->data[ ZTypeRegistry::Z_PERSISTENTOBJECT_LABEL ] instanceof ZMultiLingualString ) ) {
94            return false;
95        }
96        if ( !( $this->data[ ZTypeRegistry::Z_PERSISTENTOBJECT_ALIASES ] instanceof ZMultiLingualStringSet ) ) {
97            return false;
98        }
99        if ( !( $this->data[ ZTypeRegistry::Z_PERSISTENTOBJECT_DESCRIPTION ] instanceof ZMultiLingualString ) ) {
100            return false;
101        }
102        return true;
103    }
104
105    /**
106     * Get the generic content of the inner ZObject wrapped by this ZPersistentObject.
107     *
108     * @return mixed The generic content of the ZObject wrapped by this ZPersistentObject.
109     */
110    public function getZValue() {
111        return $this->getInnerZObject()->getZValue();
112    }
113
114    /**
115     * Validate the length of a field in the ZMultilingualString of this ZPersistentObject.
116     * The field is validated for each language in the ZMultilingualString.
117     *
118     * @param string $fieldName The name of the field to validate
119     * @param int $length The maximum length of the field
120     * @param MessageLocalizer|null $context The context of the action operation, for localisation of messages
121     * @return array An array of invalid languages
122     */
123    private function validateZMultilingualStringFieldLength( $fieldName, $length, $context = null ) {
124        // If we have context available as IContextSource, get user language to localize language names
125        $userLang = ( $context instanceof IContextSource ) ? $context->getLanguage()->getCode() : null;
126
127        // used to go from LANG_CODE -> LANG_NAME
128        // TODO (T362246): Dependency-inject
129        $services = MediaWikiServices::getInstance();
130        $zLangRegistry = ZLangRegistry::singleton();
131        $invalidLanguages = [];
132
133        // Ensure the field exists and is a valid object with getValueAsList method
134        if ( !isset( $this->data[$fieldName] ) || !method_exists( $this->data[$fieldName], 'getValueAsList' ) ) {
135            // Handle the case where the field is not present or is not valid
136            return $invalidLanguages;
137        }
138
139        $fieldValues = $this->data[$fieldName]->getValueAsList();
140        foreach ( $fieldValues as $lang => $value ) {
141            if ( strlen( $value ) > $length ) {
142                // get the language name from the language code
143                $langCode = $zLangRegistry->getLanguageCodeFromZid( $lang );
144                $langTitle = $services->getLanguageNameUtils()->getLanguageName( $langCode, $userLang );
145                $invalidLanguages[] = $langTitle;
146            }
147        }
148        return $invalidLanguages;
149    }
150
151    /**
152     * Validates the length of the description field for a persistent object.
153     *
154     * @param int $length The maximum length allowed for the description.
155     * @param MessageLocalizer|null $context The context of the action operation, for localisation of messages
156     * @return array An array of invalid languages
157     */
158    public function validateDescriptionLength( $length, $context = null ) {
159        return $this->validateZMultilingualStringFieldLength(
160            ZTypeRegistry::Z_PERSISTENTOBJECT_DESCRIPTION, $length, $context );
161    }
162
163    /**
164     * Validates the length of the label field for a persistent object.
165     *
166     * @param int $length The maximum length allowed for the label.
167     * @param MessageLocalizer|null $context The context of the action operation, for localisation of messages
168     * @return array An array of invalid languages
169     */
170    public function validateLabelLength( $length, $context = null ) {
171        return $this->validateZMultilingualStringFieldLength(
172            ZTypeRegistry::Z_PERSISTENTOBJECT_LABEL, $length, $context );
173    }
174
175    /**
176     * Get the Zid that identifies this ZPersistentObject.
177     *
178     * @return string The persisted (or null) ZID
179     */
180    public function getZid(): string {
181        return $this->data[ ZTypeRegistry::Z_PERSISTENTOBJECT_ID ]->getZValue();
182    }
183
184    /**
185     * Get the inner ZObject wrapped by this ZPersistentObject.
186     *
187     * @return ZObject The inner ZObject wrapped by this ZPersistentObject
188     */
189    public function getInnerZObject(): ZObject {
190        return $this->data[ ZTypeRegistry::Z_PERSISTENTOBJECT_VALUE ];
191    }
192
193    /**
194     * Get the type Zid of the ZObject wrapped by this ZPersistentObject.
195     * TODO (T296822): The type can also be a function call.
196     *
197     * @return string The type of the internal ZObject
198     */
199    public function getInternalZType(): string {
200        return $this->data[ ZTypeRegistry::Z_PERSISTENTOBJECT_VALUE ]->getZType();
201    }
202
203    /**
204     * Get the ZMultilingualString that contains the label of this ZPersistentObject.
205     *
206     * @return ZMultilingualString The mulilingual string object with the label
207     */
208    public function getLabels(): ZMultilingualString {
209        return $this->data[ ZTypeRegistry::Z_PERSISTENTOBJECT_LABEL ];
210    }
211
212    /**
213     * Get the label for a given Language (or its fallback).
214     *
215     * @param Language $language Language in which to provide the label.
216     * @param bool $defaultToEnglish
217     * @return ?string
218     */
219    public function getLabel( $language, $defaultToEnglish = false ): ?string {
220        if ( $defaultToEnglish ) {
221            return $this->getLabels()
222                ->buildStringForLanguage( $language )
223                ->fallbackWithEnglish()
224                ->getString();
225        }
226        return $this->getLabels()->getStringForLanguage( $language );
227    }
228
229    /**
230     * Get the ZMultilingualStringSet that contains the aliases for this ZPersistentObject.
231     *
232     * @return ZMultilingualStringSet The mulilingual stringset object with the aliases
233     */
234    public function getAliases(): ZMultiLingualStringSet {
235        return $this->data[ ZTypeRegistry::Z_PERSISTENTOBJECT_ALIASES ];
236    }
237
238    /**
239     * Get the ZMultilingualString that contains the description of this ZPersistentObject.
240     *
241     * @return ?ZMultilingualString The mulilingual string object with the description
242     */
243    public function getDescriptions(): ?ZMultilingualString {
244        return $this->data[ ZTypeRegistry::Z_PERSISTENTOBJECT_DESCRIPTION ];
245    }
246
247    /**
248     * Get the description for a given Language (or its fallback).
249     *
250     * @param Language $language Language in which to provide the description.
251     * @param bool $defaultToEnglish
252     * @return ?string
253     */
254    public function getDescription( $language, $defaultToEnglish = false ): ?string {
255        if ( !$this->getDescriptions() ) {
256            return null;
257        }
258        if ( $defaultToEnglish ) {
259            return $this->getDescriptions()
260                ->buildStringForLanguage( $language )
261                ->fallbackWithEnglish()
262                ->getString();
263        }
264        return $this->getDescriptions()->getStringForLanguage( $language );
265    }
266}