Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
92.31% |
72 / 78 |
|
60.00% |
3 / 5 |
CRAP | |
0.00% |
0 / 1 |
MessagesProcessor | |
92.31% |
72 / 78 |
|
60.00% |
3 / 5 |
27.33 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
getMessages | |
81.82% |
9 / 11 |
|
0.00% |
0 / 1 |
4.10 | |||
mapJsonPointerToMessageKey | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
1 | |||
getControlMessages | |
33.33% |
2 / 6 |
|
0.00% |
0 / 1 |
3.19 | |||
computeEditorMessageKeys | |
100.00% |
55 / 55 |
|
100.00% |
1 / 1 |
19 |
1 | <?php |
2 | |
3 | namespace MediaWiki\Extension\CommunityConfiguration\EditorCapabilities; |
4 | |
5 | use Error; |
6 | use Iterator; |
7 | use MediaWiki\Extension\CommunityConfiguration\Schema\JsonSchema; |
8 | use MediaWiki\Extension\CommunityConfiguration\Schema\JsonSchemaIterator; |
9 | use MessageLocalizer; |
10 | use Psr\Log\LoggerAwareTrait; |
11 | use Psr\Log\NullLogger; |
12 | use RuntimeException; |
13 | |
14 | class MessagesProcessor { |
15 | use LoggerAwareTrait; |
16 | |
17 | private MessageLocalizer $messageLocalizer; |
18 | |
19 | public function __construct( MessageLocalizer $messageLocalizer ) { |
20 | $this->messageLocalizer = $messageLocalizer; |
21 | $this->setLogger( new NullLogger() ); |
22 | } |
23 | |
24 | /** |
25 | * @param string $providerId |
26 | * @param Iterator $schema |
27 | * @param string $messagePrefix |
28 | * @return array |
29 | */ |
30 | public function getMessages( string $providerId, Iterator $schema, string $messagePrefix ): array { |
31 | if ( !$schema instanceof JsonSchemaIterator ) { |
32 | $this->logger->debug( __CLASS__ . ' skipped schema Iterator, because it is not a JsonSchemaIterator.' ); |
33 | return []; |
34 | } |
35 | |
36 | $messages = []; |
37 | $keys = $this->computeEditorMessageKeys( $providerId, $schema, $messagePrefix ); |
38 | // Present for any schema a used in the editor summary dialog |
39 | array_unshift( $keys, strtolower( $messagePrefix . '-' . $providerId . '-title' ) ); |
40 | foreach ( $keys as $key ) { |
41 | $msg = $this->messageLocalizer->msg( $key ); |
42 | if ( $msg->exists() ) { |
43 | $messages[$key] = $msg->plain(); |
44 | } |
45 | } |
46 | |
47 | return $messages; |
48 | } |
49 | |
50 | /** |
51 | * @param string $jsonPointer |
52 | * @param string $prefix |
53 | * @return string |
54 | */ |
55 | private function mapJsonPointerToMessageKey( string $jsonPointer, string $prefix = '' ): string { |
56 | // Remove initial # |
57 | $jsonPointer = str_replace( '#', '', $jsonPointer ); |
58 | // Remove /properties and /items occurrences since we omit them in the message key |
59 | $messageKey = str_replace( [ '/properties', '/items' ], '', $jsonPointer ); |
60 | // Replace slashes by hyphens to follow message key convention |
61 | $messageKey = str_replace( [ '/' ], '-', $messageKey ); |
62 | return strtolower( $prefix . $messageKey ); |
63 | } |
64 | |
65 | /** |
66 | * Returns the value of a given field in the given class. |
67 | * |
68 | * @param string $className |
69 | * @param string $fieldName |
70 | * @return array The value of the class field |
71 | */ |
72 | private function getControlMessages( string $className, string $fieldName ): array { |
73 | try { |
74 | $value = constant( "$className::$fieldName" ); |
75 | } catch ( Error $e ) { |
76 | throw new RuntimeException( |
77 | "Failed resolving reference $fieldName in $className." |
78 | ); |
79 | } |
80 | return $value; |
81 | } |
82 | |
83 | /** |
84 | * @param string $providerId |
85 | * @param JsonSchemaIterator $schemas |
86 | * @param string $messagePrefix |
87 | * @return array |
88 | */ |
89 | private function computeEditorMessageKeys( |
90 | string $providerId, JsonSchemaIterator $schemas, string $messagePrefix |
91 | ): array { |
92 | $messages = []; |
93 | foreach ( $schemas as $sub_schema ) { |
94 | [ |
95 | 'parentType' => $parentType, |
96 | 'pointer' => $pointer, |
97 | 'schema' => $schema |
98 | ] = $sub_schema; |
99 | |
100 | if ( $pointer === '#' ) { |
101 | // We don't generate labels for the root object |
102 | continue; |
103 | } |
104 | |
105 | $schemaBaseKey = $this->mapJsonPointerToMessageKey( $pointer, $messagePrefix . '-' . $providerId ); |
106 | if ( isset( $schema->{JsonSchema::CONTROL} ) ) { |
107 | $messages = array_merge( $messages, $this->getControlMessages( |
108 | $schema->{JsonSchema::CONTROL}, 'MESSAGES' |
109 | ) ); |
110 | } |
111 | if ( isset( $schema->{JsonSchema::ENUM} ) ) { |
112 | $messages[] = $schemaBaseKey . '-label'; |
113 | $messages[] = $schemaBaseKey . '-help-text'; |
114 | $messages[] = $schemaBaseKey . '-description'; |
115 | if ( $parentType === JsonSchema::TYPE_ARRAY ) { |
116 | if ( $schema->{JsonSchema::TYPE} === JsonSchema::TYPE_STRING ) { |
117 | $messages[] = 'mw-widgets-titlesmultiselect-placeholder'; |
118 | $messages[] = 'communityconfiguration-editor-chip-control-aria-chip-description'; |
119 | } |
120 | // We don't generate labels for arrays of simple types |
121 | continue; |
122 | } |
123 | foreach ( $schema->{JsonSchema::ENUM} as $enumValue ) { |
124 | $messages[] = $schemaBaseKey . '-option-' . $enumValue . '-label'; |
125 | } |
126 | // Do not process the schema type for enums as it would generate |
127 | // undesired keys, eg: placeholders |
128 | continue; |
129 | } |
130 | if ( |
131 | $schema->{JsonSchema::TYPE} === JsonSchema::TYPE_STRING || |
132 | $schema->{JsonSchema::TYPE} === JsonSchema::TYPE_NUMBER || |
133 | $schema->{JsonSchema::TYPE} === JsonSchema::TYPE_INTEGER |
134 | ) { |
135 | if ( $parentType === JsonSchema::TYPE_ARRAY ) { |
136 | if ( $schema->{JsonSchema::TYPE} === JsonSchema::TYPE_STRING ) { |
137 | $messages[] = 'mw-widgets-titlesmultiselect-placeholder'; |
138 | $messages[] = 'communityconfiguration-editor-chip-control-aria-chip-description'; |
139 | } |
140 | // We don't generate labels for arrays of simple types |
141 | continue; |
142 | } |
143 | $messages[] = $schemaBaseKey . '-label'; |
144 | $messages[] = $schemaBaseKey . '-help-text'; |
145 | $messages[] = $schemaBaseKey . '-placeholder'; |
146 | $messages[] = $schemaBaseKey . '-description'; |
147 | } |
148 | if ( $schema->{JsonSchema::TYPE} === JsonSchema::TYPE_BOOLEAN ) { |
149 | $messages[] = $schemaBaseKey . '-label'; |
150 | $messages[] = $schemaBaseKey . '-help-text'; |
151 | $messages[] = $schemaBaseKey . '-control-label'; |
152 | $messages[] = $schemaBaseKey . '-description'; |
153 | } |
154 | if ( |
155 | $schema->{JsonSchema::TYPE} === JsonSchema::TYPE_OBJECT || |
156 | $schema->{JsonSchema::TYPE} === JsonSchema::TYPE_ARRAY |
157 | ) { |
158 | $messages[] = $schemaBaseKey . '-label'; |
159 | $messages[] = $schemaBaseKey . '-help-text'; |
160 | $messages[] = $schemaBaseKey . '-description'; |
161 | if ( |
162 | $schema->{JsonSchema::TYPE} === JsonSchema::TYPE_ARRAY && |
163 | // Arrays of strings are rendered on a single field with chips |
164 | $schema->{JsonSchema::ITEMS}->{JsonSchema::TYPE} !== JsonSchema::TYPE_STRING && |
165 | // Assume array types with a custom control will handle its own item labels |
166 | !isset( $schema->{JsonSchema::CONTROL} ) |
167 | ) { |
168 | $messages[] = $schemaBaseKey . '-item-label'; |
169 | $messages[] = $schemaBaseKey . '-add-element-button-label'; |
170 | $messages[] = 'communityconfiguration-editor-array-fallback-add-element-button-label'; |
171 | } |
172 | } |
173 | } |
174 | return $messages; |
175 | } |
176 | } |