|
1
|
|
|
<?php |
|
2
|
|
|
/** |
|
3
|
|
|
* This file contains only the AppExtension class. |
|
4
|
|
|
*/ |
|
5
|
|
|
|
|
6
|
|
|
namespace AppBundle\Twig; |
|
7
|
|
|
|
|
8
|
|
|
use Xtools\ProjectRepository; |
|
9
|
|
|
use Xtools\User; |
|
10
|
|
|
use NumberFormatter; |
|
11
|
|
|
use IntlDateFormatter; |
|
12
|
|
|
use DateTime; |
|
13
|
|
|
|
|
14
|
|
|
/** |
|
15
|
|
|
* Twig functions and filters for XTools. |
|
16
|
|
|
*/ |
|
17
|
|
|
class AppExtension extends Extension |
|
18
|
|
|
{ |
|
19
|
|
|
/** @var NumberFormatter Instance of NumberFormatter class, used in localizing numbers. */ |
|
20
|
|
|
protected $numFormatter; |
|
21
|
|
|
|
|
22
|
|
|
/** @var IntlDateFormatter Instance of IntlDateFormatter class, used in localizing dates. */ |
|
23
|
|
|
protected $dateFormatter; |
|
24
|
|
|
|
|
25
|
|
|
/** @var float Duration of the current HTTP request in seconds. */ |
|
26
|
|
|
protected $requestTime; |
|
27
|
|
|
|
|
28
|
|
|
/** |
|
29
|
|
|
* Get the name of this extension. |
|
30
|
|
|
* @return string |
|
31
|
|
|
*/ |
|
32
|
13 |
|
public function getName() |
|
33
|
|
|
{ |
|
34
|
13 |
|
return 'app_extension'; |
|
35
|
|
|
} |
|
36
|
|
|
|
|
37
|
|
|
/*********************************** FUNCTIONS ***********************************/ |
|
38
|
|
|
|
|
39
|
|
|
/** |
|
40
|
|
|
* Get all functions that this class provides. |
|
41
|
|
|
* @return array |
|
42
|
|
|
*/ |
|
43
|
12 |
|
public function getFunctions() |
|
44
|
|
|
{ |
|
45
|
12 |
|
$options = ['is_safe' => ['html']]; |
|
46
|
|
|
return [ |
|
47
|
12 |
|
new \Twig_SimpleFunction('request_time', [ $this, 'requestTime' ], $options), |
|
48
|
12 |
|
new \Twig_SimpleFunction('memory_usage', [ $this, 'requestMemory' ], $options), |
|
49
|
12 |
|
new \Twig_SimpleFunction('year', [ $this, 'generateYear' ], $options), |
|
50
|
12 |
|
new \Twig_SimpleFunction('msgPrintExists', [ $this, 'intuitionMessagePrintExists' ], $options), |
|
51
|
12 |
|
new \Twig_SimpleFunction('msgExists', [ $this, 'intuitionMessageExists' ], $options), |
|
52
|
12 |
|
new \Twig_SimpleFunction('msg', [ $this, 'intuitionMessage' ], $options), |
|
53
|
12 |
|
new \Twig_SimpleFunction('lang', [ $this, 'getLang' ], $options), |
|
54
|
12 |
|
new \Twig_SimpleFunction('langName', [ $this, 'getLangName' ], $options), |
|
55
|
12 |
|
new \Twig_SimpleFunction('allLangs', [ $this, 'getAllLangs' ]), |
|
56
|
12 |
|
new \Twig_SimpleFunction('isRTL', [ $this, 'intuitionIsRTL' ]), |
|
57
|
12 |
|
new \Twig_SimpleFunction('isRTLLang', [ $this, 'intuitionIsRTLLang' ]), |
|
58
|
12 |
|
new \Twig_SimpleFunction('shortHash', [ $this, 'gitShortHash' ]), |
|
59
|
12 |
|
new \Twig_SimpleFunction('hash', [ $this, 'gitHash' ]), |
|
60
|
12 |
|
new \Twig_SimpleFunction('releaseDate', [ $this, 'gitDate' ]), |
|
61
|
12 |
|
new \Twig_SimpleFunction('enabled', [ $this, 'tabEnabled' ]), |
|
62
|
12 |
|
new \Twig_SimpleFunction('tools', [ $this, 'allTools' ]), |
|
63
|
12 |
|
new \Twig_SimpleFunction('color', [ $this, 'getColorList' ]), |
|
64
|
12 |
|
new \Twig_SimpleFunction('chartColor', [ $this, 'chartColor' ]), |
|
65
|
12 |
|
new \Twig_SimpleFunction('isSingleWiki', [ $this, 'isSingleWiki' ]), |
|
66
|
12 |
|
new \Twig_SimpleFunction('getReplagThreshold', [ $this, 'getReplagThreshold' ]), |
|
67
|
12 |
|
new \Twig_SimpleFunction('loadStylesheetsFromCDN', [ $this, 'loadStylesheetsFromCDN' ]), |
|
68
|
12 |
|
new \Twig_SimpleFunction('isWMFLabs', [ $this, 'isWMFLabs' ]), |
|
69
|
12 |
|
new \Twig_SimpleFunction('replag', [ $this, 'replag' ]), |
|
70
|
12 |
|
new \Twig_SimpleFunction('link', [ $this, 'link' ]), |
|
71
|
12 |
|
new \Twig_SimpleFunction('quote', [ $this, 'quote' ]), |
|
72
|
12 |
|
new \Twig_SimpleFunction('bugReportURL', [ $this, 'bugReportURL' ]), |
|
73
|
12 |
|
new \Twig_SimpleFunction('logged_in_user', [$this, 'functionLoggedInUser']), |
|
74
|
12 |
|
new \Twig_SimpleFunction('isUserAnon', [$this, 'isUserAnon']), |
|
75
|
12 |
|
new \Twig_SimpleFunction('nsName', [$this, 'nsName']), |
|
76
|
12 |
|
new \Twig_SimpleFunction('formatDuration', [$this, 'formatDuration']), |
|
77
|
12 |
|
new \Twig_SimpleFunction('numberFormat', [$this, 'numberFormat']), |
|
78
|
|
|
]; |
|
79
|
|
|
} |
|
80
|
|
|
|
|
81
|
|
|
/** |
|
82
|
|
|
* Get the duration of the current HTTP request in seconds. |
|
83
|
|
|
* @return double |
|
84
|
|
|
* Untestable since there is no request stack in the tests. |
|
85
|
|
|
* @codeCoverageIgnore |
|
86
|
|
|
*/ |
|
87
|
|
|
public function requestTime() |
|
88
|
|
|
{ |
|
89
|
|
|
if (!isset($this->requestTime)) { |
|
90
|
|
|
$this->requestTime = microtime(true) - $this->getCurrentRequest()->server->get('REQUEST_TIME_FLOAT'); |
|
91
|
|
|
} |
|
92
|
|
|
|
|
93
|
|
|
return $this->requestTime; |
|
94
|
|
|
} |
|
95
|
|
|
|
|
96
|
|
|
/** |
|
97
|
|
|
* Get the formatted real memory usage. |
|
98
|
|
|
* @return float |
|
99
|
|
|
*/ |
|
100
|
12 |
|
public function requestMemory() |
|
101
|
|
|
{ |
|
102
|
12 |
|
$mem = memory_get_usage(false); |
|
103
|
12 |
|
$div = pow(1024, 2); |
|
104
|
12 |
|
return $mem / $div; |
|
105
|
|
|
} |
|
106
|
|
|
|
|
107
|
|
|
/** |
|
108
|
|
|
* Get the current year. |
|
109
|
|
|
* @return string |
|
110
|
|
|
*/ |
|
111
|
|
|
public function generateYear() |
|
112
|
|
|
{ |
|
113
|
|
|
return date('Y'); |
|
114
|
|
|
} |
|
115
|
|
|
|
|
116
|
|
|
/** |
|
117
|
|
|
* See if a given i18n message exists. |
|
118
|
|
|
* @TODO: refactor all intuition stuff so it can be used anywhere |
|
119
|
|
|
* @param string $message The message. |
|
120
|
|
|
* @param array $vars |
|
121
|
|
|
* @return bool |
|
122
|
|
|
*/ |
|
123
|
|
|
public function intuitionMessageExists($message = '', $vars = []) |
|
124
|
|
|
{ |
|
125
|
|
|
return $this->getIntuition()->msgExists($message, array_merge( |
|
126
|
|
|
[ |
|
127
|
|
|
'domain' => 'xtools' |
|
128
|
|
|
], |
|
129
|
|
|
[ |
|
130
|
|
|
'variables' => $vars |
|
131
|
|
|
] |
|
132
|
|
|
)); |
|
133
|
|
|
} |
|
134
|
|
|
|
|
135
|
|
|
/** |
|
136
|
|
|
* Get an i18n message if it exists, otherwise just get the message key. |
|
137
|
|
|
* @param string $message |
|
138
|
|
|
* @param array $vars |
|
139
|
|
|
* @return mixed|null|string |
|
140
|
|
|
*/ |
|
141
|
|
|
public function intuitionMessagePrintExists($message = "", $vars = []) |
|
142
|
|
|
{ |
|
143
|
|
|
if (is_array($message)) { |
|
144
|
|
|
$vars = $message; |
|
145
|
|
|
$message = $message[0]; |
|
146
|
|
|
$vars = array_slice($vars, 1); |
|
147
|
|
|
} |
|
148
|
|
|
if ($this->intuitionMessageExists($message, $vars)) { |
|
149
|
|
|
return $this->intuitionMessage($message, $vars); |
|
150
|
|
|
} else { |
|
151
|
|
|
return $message; |
|
152
|
|
|
} |
|
153
|
|
|
} |
|
154
|
|
|
|
|
155
|
|
|
/** |
|
156
|
|
|
* Get an i18n message. |
|
157
|
|
|
* @param string $message |
|
158
|
|
|
* @param array $vars |
|
159
|
|
|
* @return mixed|null|string |
|
160
|
|
|
*/ |
|
161
|
11 |
|
public function intuitionMessage($message = "", $vars = []) |
|
162
|
|
|
{ |
|
163
|
11 |
|
return $this->getIntuition()->msg($message, [ "domain" => "xtools", "variables" => $vars ]); |
|
164
|
|
|
} |
|
165
|
|
|
|
|
166
|
|
|
/** |
|
167
|
|
|
* Get the current language code. |
|
168
|
|
|
* @return string |
|
169
|
|
|
*/ |
|
170
|
12 |
|
public function getLang() |
|
171
|
|
|
{ |
|
172
|
12 |
|
return $this->getIntuition()->getLang(); |
|
173
|
|
|
} |
|
174
|
|
|
|
|
175
|
|
|
/** |
|
176
|
|
|
* Get the current language name (defaults to 'English'). |
|
177
|
|
|
* @return string |
|
178
|
|
|
*/ |
|
179
|
12 |
|
public function getLangName() |
|
180
|
|
|
{ |
|
181
|
12 |
|
return in_array(ucfirst($this->getIntuition()->getLangName()), $this->getAllLangs()) |
|
182
|
12 |
|
? $this->getIntuition()->getLangName() |
|
183
|
12 |
|
: 'English'; |
|
184
|
|
|
} |
|
185
|
|
|
|
|
186
|
|
|
/** |
|
187
|
|
|
* Get all available languages in the i18n directory |
|
188
|
|
|
* @return array Associative array of langKey => langName |
|
189
|
|
|
*/ |
|
190
|
12 |
|
public function getAllLangs() |
|
191
|
|
|
{ |
|
192
|
12 |
|
$messageFiles = glob($this->container->getParameter("kernel.root_dir") . '/../i18n/*.json'); |
|
193
|
|
|
|
|
194
|
12 |
|
$languages = array_values(array_unique(array_map( |
|
195
|
12 |
|
function ($filename) { |
|
196
|
12 |
|
return basename($filename, '.json'); |
|
197
|
12 |
|
}, |
|
198
|
12 |
|
$messageFiles |
|
199
|
|
|
))); |
|
200
|
|
|
|
|
201
|
12 |
|
$availableLanguages = []; |
|
202
|
|
|
|
|
203
|
12 |
|
foreach ($languages as $lang) { |
|
204
|
12 |
|
$availableLanguages[$lang] = ucfirst($this->getIntuition()->getLangName($lang)); |
|
205
|
|
|
} |
|
206
|
12 |
|
asort($availableLanguages); |
|
207
|
|
|
|
|
208
|
12 |
|
return $availableLanguages; |
|
209
|
|
|
} |
|
210
|
|
|
|
|
211
|
|
|
/** |
|
212
|
|
|
* Whether the current language is right-to-left. |
|
213
|
|
|
* @return bool |
|
214
|
|
|
*/ |
|
215
|
11 |
|
public function intuitionIsRTL() |
|
216
|
|
|
{ |
|
217
|
11 |
|
return $this->getIntuition()->isRTL($this->getIntuition()->getLang()); |
|
218
|
|
|
} |
|
219
|
|
|
|
|
220
|
|
|
/** |
|
221
|
|
|
* Whether the given language is right-to-left. |
|
222
|
|
|
* @param string $lang The language code. |
|
223
|
|
|
* @return bool |
|
224
|
|
|
*/ |
|
225
|
1 |
|
public function intuitionIsRTLLang($lang) |
|
226
|
|
|
{ |
|
227
|
1 |
|
return $this->getIntuition()->isRTL($lang); |
|
228
|
|
|
} |
|
229
|
|
|
|
|
230
|
|
|
/** |
|
231
|
|
|
* Get the short hash of the currently checked-out Git commit. |
|
232
|
|
|
* @return string |
|
233
|
|
|
*/ |
|
234
|
12 |
|
public function gitShortHash() |
|
235
|
|
|
{ |
|
236
|
12 |
|
return exec("git rev-parse --short HEAD"); |
|
237
|
|
|
} |
|
238
|
|
|
|
|
239
|
|
|
/** |
|
240
|
|
|
* Get the full hash of the currently checkout-out Git commit. |
|
241
|
|
|
* @return string |
|
242
|
|
|
*/ |
|
243
|
12 |
|
public function gitHash() |
|
244
|
|
|
{ |
|
245
|
12 |
|
return exec("git rev-parse HEAD"); |
|
246
|
|
|
} |
|
247
|
|
|
|
|
248
|
|
|
/** |
|
249
|
|
|
* Get the date of the HEAD commit. |
|
250
|
|
|
* @return string |
|
251
|
|
|
*/ |
|
252
|
2 |
|
public function gitDate() |
|
253
|
|
|
{ |
|
254
|
2 |
|
$date = new DateTime(exec('git show -s --format=%ci')); |
|
255
|
2 |
|
return $date->format('Y-m-d'); |
|
256
|
|
|
} |
|
257
|
|
|
|
|
258
|
|
|
/** |
|
259
|
|
|
* Check whether a given tool is enabled. |
|
260
|
|
|
* @param string $tool The short name of the tool. |
|
261
|
|
|
* @return bool |
|
262
|
|
|
*/ |
|
263
|
11 |
View Code Duplication |
public function tabEnabled($tool = "index") |
|
|
|
|
|
|
264
|
|
|
{ |
|
265
|
11 |
|
$param = false; |
|
266
|
11 |
|
if ($this->container->hasParameter("enable.$tool")) { |
|
267
|
11 |
|
$param = boolval($this->container->getParameter("enable.$tool")); |
|
268
|
|
|
} |
|
269
|
11 |
|
return $param; |
|
270
|
|
|
} |
|
271
|
|
|
|
|
272
|
|
|
/** |
|
273
|
|
|
* Get a list of the short names of all tools. |
|
274
|
|
|
* @return string[] |
|
275
|
|
|
*/ |
|
276
|
11 |
|
public function allTools() |
|
277
|
|
|
{ |
|
278
|
11 |
|
$retVal = []; |
|
279
|
11 |
|
if ($this->container->hasParameter("tools")) { |
|
280
|
11 |
|
$retVal = $this->container->getParameter("tools"); |
|
281
|
|
|
} |
|
282
|
11 |
|
return $retVal; |
|
283
|
|
|
} |
|
284
|
|
|
|
|
285
|
|
|
/** |
|
286
|
|
|
* Get a list of namespace colours (one or all). |
|
287
|
|
|
* @param bool $num The NS ID to get. |
|
288
|
|
|
* @return string[]|string Indexed by namespace ID. |
|
289
|
|
|
*/ |
|
290
|
|
|
public static function getColorList($num = false) |
|
291
|
|
|
{ |
|
292
|
|
|
$colors = [ |
|
293
|
|
|
0 => '#FF5555', |
|
294
|
|
|
1 => '#55FF55', |
|
295
|
|
|
2 => '#FFEE22', |
|
296
|
|
|
3 => '#FF55FF', |
|
297
|
|
|
4 => '#5555FF', |
|
298
|
|
|
5 => '#55FFFF', |
|
299
|
|
|
6 => '#C00000', |
|
300
|
|
|
7 => '#0000C0', |
|
301
|
|
|
8 => '#008800', |
|
302
|
|
|
9 => '#00C0C0', |
|
303
|
|
|
10 => '#FFAFAF', |
|
304
|
|
|
11 => '#808080', |
|
305
|
|
|
12 => '#00C000', |
|
306
|
|
|
13 => '#404040', |
|
307
|
|
|
14 => '#C0C000', |
|
308
|
|
|
15 => '#C000C0', |
|
309
|
|
|
90 => '#991100', |
|
310
|
|
|
91 => '#99FF00', |
|
311
|
|
|
92 => '#000000', |
|
312
|
|
|
93 => '#777777', |
|
313
|
|
|
100 => '#75A3D1', |
|
314
|
|
|
101 => '#A679D2', |
|
315
|
|
|
102 => '#660000', |
|
316
|
|
|
103 => '#000066', |
|
317
|
|
|
104 => '#FAFFAF', |
|
318
|
|
|
105 => '#408345', |
|
319
|
|
|
106 => '#5c8d20', |
|
320
|
|
|
107 => '#e1711d', |
|
321
|
|
|
108 => '#94ef2b', |
|
322
|
|
|
109 => '#756a4a', |
|
323
|
|
|
110 => '#6f1dab', |
|
324
|
|
|
111 => '#301e30', |
|
325
|
|
|
112 => '#5c9d96', |
|
326
|
|
|
113 => '#a8cd8c', |
|
327
|
|
|
114 => '#f2b3f1', |
|
328
|
|
|
115 => '#9b5828', |
|
329
|
|
|
116 => '#002288', |
|
330
|
|
|
117 => '#0000CC', |
|
331
|
|
|
118 => '#99FFFF', |
|
332
|
|
|
119 => '#99BBFF', |
|
333
|
|
|
120 => '#FF99FF', |
|
334
|
|
|
121 => '#CCFFFF', |
|
335
|
|
|
122 => '#CCFF00', |
|
336
|
|
|
123 => '#CCFFCC', |
|
337
|
|
|
200 => '#33FF00', |
|
338
|
|
|
201 => '#669900', |
|
339
|
|
|
202 => '#666666', |
|
340
|
|
|
203 => '#999999', |
|
341
|
|
|
204 => '#FFFFCC', |
|
342
|
|
|
205 => '#FF00CC', |
|
343
|
|
|
206 => '#FFFF00', |
|
344
|
|
|
207 => '#FFCC00', |
|
345
|
|
|
208 => '#FF0000', |
|
346
|
|
|
209 => '#FF6600', |
|
347
|
|
|
250 => '#6633CC', |
|
348
|
|
|
251 => '#6611AA', |
|
349
|
|
|
252 => '#66FF99', |
|
350
|
|
|
253 => '#66FF66', |
|
351
|
|
|
446 => '#06DCFB', |
|
352
|
|
|
447 => '#892EE4', |
|
353
|
|
|
460 => '#99FF66', |
|
354
|
|
|
461 => '#99CC66', |
|
355
|
|
|
470 => '#CCCC33', |
|
356
|
|
|
471 => '#CCFF33', |
|
357
|
|
|
480 => '#6699FF', |
|
358
|
|
|
481 => '#66FFFF', |
|
359
|
|
|
484 => '#07C8D6', |
|
360
|
|
|
485 => '#2AF1FF', |
|
361
|
|
|
486 => '#79CB21', |
|
362
|
|
|
487 => '#80D822', |
|
363
|
|
|
490 => '#995500', |
|
364
|
|
|
491 => '#998800', |
|
365
|
|
|
710 => '#FFCECE', |
|
366
|
|
|
711 => '#FFC8F2', |
|
367
|
|
|
828 => '#F7DE00', |
|
368
|
|
|
829 => '#BABA21', |
|
369
|
|
|
866 => '#FFFFFF', |
|
370
|
|
|
867 => '#FFCCFF', |
|
371
|
|
|
1198 => '#FF34B3', |
|
372
|
|
|
1199 => '#8B1C62', |
|
373
|
|
|
2300 => '#A900B8', |
|
374
|
|
|
2301 => '#C93ED6', |
|
375
|
|
|
2302 => '#8A09C1', |
|
376
|
|
|
2303 => '#974AB8', |
|
377
|
|
|
2600 => '#000000', |
|
378
|
|
|
]; |
|
379
|
|
|
|
|
380
|
|
|
if ($num === false) { |
|
381
|
|
|
return $colors; |
|
382
|
|
|
} elseif (isset($colors[$num])) { |
|
383
|
|
|
return $colors[$num]; |
|
384
|
|
|
} else { |
|
385
|
|
|
// Default to grey. |
|
386
|
|
|
return '#CCC'; |
|
387
|
|
|
} |
|
388
|
|
|
} |
|
389
|
|
|
|
|
390
|
|
|
/** |
|
391
|
|
|
* Get color-blind friendly colors for use in charts |
|
392
|
|
|
* @param Integer $num Index of color |
|
393
|
|
|
* @return String RGBA color (so you can more easily adjust the opacity) |
|
394
|
|
|
*/ |
|
395
|
|
|
public function chartColor($num) |
|
396
|
|
|
{ |
|
397
|
|
|
$colors = [ |
|
398
|
|
|
'rgba(171, 212, 235, 1)', |
|
399
|
|
|
'rgba(178, 223, 138, 1)', |
|
400
|
|
|
'rgba(251, 154, 153, 1)', |
|
401
|
|
|
'rgba(253, 191, 111, 1)', |
|
402
|
|
|
'rgba(202, 178, 214, 1)', |
|
403
|
|
|
'rgba(207, 182, 128, 1)', |
|
404
|
|
|
'rgba(141, 211, 199, 1)', |
|
405
|
|
|
'rgba(252, 205, 229, 1)', |
|
406
|
|
|
'rgba(255, 247, 161, 1)', |
|
407
|
|
|
'rgba(217, 217, 217, 1)', |
|
408
|
|
|
]; |
|
409
|
|
|
|
|
410
|
|
|
return $colors[$num % count($colors)]; |
|
411
|
|
|
} |
|
412
|
|
|
|
|
413
|
|
|
/** |
|
414
|
|
|
* Whether XTools is running in single-project mode. |
|
415
|
|
|
* @return bool |
|
416
|
|
|
*/ |
|
417
|
8 |
View Code Duplication |
public function isSingleWiki() |
|
|
|
|
|
|
418
|
|
|
{ |
|
419
|
8 |
|
$param = true; |
|
420
|
8 |
|
if ($this->container->hasParameter('app.single_wiki')) { |
|
421
|
8 |
|
$param = boolval($this->container->getParameter('app.single_wiki')); |
|
422
|
|
|
} |
|
423
|
8 |
|
return $param; |
|
424
|
|
|
} |
|
425
|
|
|
|
|
426
|
|
|
/** |
|
427
|
|
|
* Get the database replication-lag threshold. |
|
428
|
|
|
* @return int |
|
429
|
|
|
*/ |
|
430
|
|
|
public function getReplagThreshold() |
|
431
|
|
|
{ |
|
432
|
|
|
$param = 30; |
|
433
|
|
|
if ($this->container->hasParameter('app.replag_threshold')) { |
|
434
|
|
|
$param = $this->container->getParameter('app.replag_threshold'); |
|
435
|
|
|
}; |
|
436
|
|
|
return $param; |
|
437
|
|
|
} |
|
438
|
|
|
|
|
439
|
|
|
/** |
|
440
|
|
|
* Whether we should load stylesheets from external CDNs or not. |
|
441
|
|
|
* @return bool |
|
442
|
|
|
*/ |
|
443
|
11 |
View Code Duplication |
public function loadStylesheetsFromCDN() |
|
|
|
|
|
|
444
|
|
|
{ |
|
445
|
11 |
|
$param = false; |
|
446
|
11 |
|
if ($this->container->hasParameter('app.load_stylesheets_from_cdn')) { |
|
447
|
11 |
|
$param = boolval($this->container->getParameter('app.load_stylesheets_from_cdn')); |
|
448
|
|
|
} |
|
449
|
11 |
|
return $param; |
|
450
|
|
|
} |
|
451
|
|
|
|
|
452
|
|
|
/** |
|
453
|
|
|
* Whether XTools is running in WMF Labs mode. |
|
454
|
|
|
* @return bool |
|
455
|
|
|
*/ |
|
456
|
11 |
View Code Duplication |
public function isWMFLabs() |
|
|
|
|
|
|
457
|
|
|
{ |
|
458
|
11 |
|
$param = false; |
|
459
|
11 |
|
if ($this->container->hasParameter('app.is_labs')) { |
|
460
|
11 |
|
$param = boolval($this->container->getParameter('app.is_labs')); |
|
461
|
|
|
} |
|
462
|
11 |
|
return $param; |
|
463
|
|
|
} |
|
464
|
|
|
|
|
465
|
|
|
/** |
|
466
|
|
|
* The current replication lag. |
|
467
|
|
|
* @return int |
|
468
|
|
|
* @codeCoverageIgnore |
|
469
|
|
|
*/ |
|
470
|
|
|
public function replag() |
|
471
|
|
|
{ |
|
472
|
|
|
$retVal = 0; |
|
473
|
|
|
|
|
474
|
|
|
if ($this->isWMFLabs()) { |
|
475
|
|
|
$project = $this->getCurrentRequest()->get('project'); |
|
476
|
|
|
|
|
477
|
|
|
if (!isset($project)) { |
|
478
|
|
|
$project = 'enwiki'; |
|
479
|
|
|
} |
|
480
|
|
|
|
|
481
|
|
|
$dbName = ProjectRepository::getProject($project, $this->container) |
|
482
|
|
|
->getDatabaseName(); |
|
483
|
|
|
|
|
484
|
|
|
$stmt = "SELECT lag FROM `heartbeat_p`.`heartbeat` h |
|
485
|
|
|
RIGHT JOIN `meta_p`.`wiki` w ON concat(h.shard, \".labsdb\")=w.slice |
|
486
|
|
|
WHERE dbname LIKE :project LIMIT 1"; |
|
487
|
|
|
|
|
488
|
|
|
$conn = $this->container->get('doctrine')->getManager('replicas')->getConnection(); |
|
489
|
|
|
|
|
490
|
|
|
// Prepare the query and execute |
|
491
|
|
|
$resultQuery = $conn->prepare($stmt); |
|
492
|
|
|
$resultQuery->bindParam('project', $dbName); |
|
493
|
|
|
$resultQuery->execute(); |
|
494
|
|
|
|
|
495
|
|
|
if ($resultQuery->errorCode() == 0) { |
|
496
|
|
|
$results = $resultQuery->fetchAll(); |
|
497
|
|
|
|
|
498
|
|
|
if (isset($results[0]['lag'])) { |
|
499
|
|
|
$retVal = $results[0]['lag']; |
|
500
|
|
|
} |
|
501
|
|
|
} |
|
502
|
|
|
} |
|
503
|
|
|
|
|
504
|
|
|
return $retVal; |
|
505
|
|
|
} |
|
506
|
|
|
|
|
507
|
|
|
/** |
|
508
|
|
|
* Get a random quote for the footer |
|
509
|
|
|
* @return string |
|
510
|
|
|
*/ |
|
511
|
11 |
|
public function quote() |
|
512
|
|
|
{ |
|
513
|
|
|
// Don't show if bash is turned off, but always show for Labs |
|
514
|
|
|
// (so quote is in footer but not in nav). |
|
515
|
11 |
|
$isLabs = $this->container->getParameter('app.is_labs'); |
|
516
|
11 |
|
if (!$isLabs && !$this->container->getParameter('enable.bash')) { |
|
517
|
11 |
|
return ''; |
|
518
|
|
|
} |
|
519
|
|
|
$quotes = $this->container->getParameter('quotes'); |
|
520
|
|
|
$id = array_rand($quotes); |
|
521
|
|
|
return $quotes[$id]; |
|
522
|
|
|
} |
|
523
|
|
|
|
|
524
|
|
|
/** |
|
525
|
|
|
* Get the currently logged in user's details. |
|
526
|
|
|
* @return string[] |
|
527
|
|
|
*/ |
|
528
|
11 |
|
public function functionLoggedInUser() |
|
529
|
|
|
{ |
|
530
|
11 |
|
return $this->container->get('session')->get('logged_in_user'); |
|
531
|
|
|
} |
|
532
|
|
|
|
|
533
|
|
|
|
|
534
|
|
|
/*********************************** FILTERS ***********************************/ |
|
535
|
|
|
|
|
536
|
|
|
/** |
|
537
|
|
|
* Get all filters for this extension. |
|
538
|
|
|
* @return array |
|
539
|
|
|
*/ |
|
540
|
12 |
|
public function getFilters() |
|
541
|
|
|
{ |
|
542
|
|
|
return [ |
|
543
|
12 |
|
new \Twig_SimpleFilter('capitalize_first', [ $this, 'capitalizeFirst' ]), |
|
544
|
12 |
|
new \Twig_SimpleFilter('percent_format', [ $this, 'percentFormat' ]), |
|
545
|
12 |
|
new \Twig_SimpleFilter('diff_format', [ $this, 'diffFormat' ], [ 'is_safe' => [ 'html' ] ]), |
|
546
|
12 |
|
new \Twig_SimpleFilter('num_format', [$this, 'numberFormat']), |
|
547
|
12 |
|
new \Twig_SimpleFilter('date_format', [$this, 'dateFormat']), |
|
548
|
|
|
]; |
|
549
|
|
|
} |
|
550
|
|
|
|
|
551
|
|
|
/** |
|
552
|
|
|
* Format a number based on language settings. |
|
553
|
|
|
* @param int|float $number |
|
554
|
|
|
* @param int $decimals Number of decimals to format to. |
|
555
|
|
|
* @return string |
|
556
|
|
|
*/ |
|
557
|
15 |
|
public function numberFormat($number, $decimals = 0) |
|
558
|
|
|
{ |
|
559
|
15 |
|
if (!isset($this->numFormatter)) { |
|
560
|
15 |
|
$lang = $this->getIntuition()->getLang(); |
|
561
|
15 |
|
$this->numFormatter = new NumberFormatter($lang, NumberFormatter::DECIMAL); |
|
562
|
|
|
} |
|
563
|
|
|
|
|
564
|
|
|
// Get separator symbols. |
|
565
|
15 |
|
$decimal = $this->numFormatter->getSymbol(NumberFormatter::DECIMAL_SEPARATOR_SYMBOL); |
|
566
|
15 |
|
$thousands = $this->numFormatter->getSymbol(NumberFormatter::GROUPING_SEPARATOR_SYMBOL); |
|
567
|
|
|
|
|
568
|
15 |
|
$formatted = number_format($number, $decimals, $decimal, $thousands); |
|
569
|
|
|
|
|
570
|
|
|
// Remove trailing .0's (e.g. 40.00 -> 40). |
|
571
|
15 |
|
return preg_replace("/\\".$decimal."0+$/", '', $formatted); |
|
572
|
|
|
} |
|
573
|
|
|
|
|
574
|
|
|
/** |
|
575
|
|
|
* Localize the given date based on language settings. |
|
576
|
|
|
* @param string|DateTime $datetime |
|
577
|
|
|
* @return string |
|
578
|
|
|
*/ |
|
579
|
1 |
|
public function dateFormat($datetime) |
|
580
|
|
|
{ |
|
581
|
1 |
|
if (!isset($this->dateFormatter)) { |
|
582
|
1 |
|
$this->dateFormatter = new IntlDateFormatter( |
|
583
|
1 |
|
$this->getIntuition()->getLang(), |
|
584
|
1 |
|
IntlDateFormatter::SHORT, |
|
585
|
1 |
|
IntlDateFormatter::SHORT |
|
586
|
|
|
); |
|
587
|
|
|
} |
|
588
|
|
|
|
|
589
|
1 |
|
if (is_string($datetime)) { |
|
590
|
1 |
|
$datetime = new DateTime($datetime); |
|
591
|
|
|
} |
|
592
|
|
|
|
|
593
|
1 |
|
return $this->dateFormatter->format($datetime); |
|
594
|
|
|
} |
|
595
|
|
|
|
|
596
|
|
|
/** |
|
597
|
|
|
* Mysteriously missing Twig helper to capitalize only the first character. |
|
598
|
|
|
* E.g. used for table headings for translated messages |
|
599
|
|
|
* @param string $str The string |
|
600
|
|
|
* @return string The string, capitalized |
|
601
|
|
|
*/ |
|
602
|
5 |
|
public function capitalizeFirst($str) |
|
603
|
|
|
{ |
|
604
|
5 |
|
return ucfirst($str); |
|
605
|
|
|
} |
|
606
|
|
|
|
|
607
|
|
|
/** |
|
608
|
|
|
* Format a given number or fraction as a percentage. |
|
609
|
|
|
* @param number $numerator Numerator or single fraction if denominator is ommitted. |
|
610
|
|
|
* @param number $denominator Denominator. |
|
611
|
|
|
* @param integer $precision Number of decimal places to show. |
|
612
|
|
|
* @return string Formatted percentage. |
|
613
|
|
|
*/ |
|
614
|
1 |
|
public function percentFormat($numerator, $denominator = null, $precision = 1) |
|
615
|
|
|
{ |
|
616
|
1 |
|
if (!$denominator) { |
|
617
|
1 |
|
$quotient = $numerator; |
|
618
|
|
|
} else { |
|
619
|
1 |
|
$quotient = ( $numerator / $denominator ) * 100; |
|
620
|
|
|
} |
|
621
|
|
|
|
|
622
|
1 |
|
return $this->numberFormat($quotient, $precision) . '%'; |
|
623
|
|
|
} |
|
624
|
|
|
|
|
625
|
|
|
/** |
|
626
|
|
|
* Helper to return whether the given user is an anonymous (logged out) user. |
|
627
|
|
|
* @param User|string $user User object or username as a string. |
|
628
|
|
|
* @return bool |
|
629
|
|
|
*/ |
|
630
|
|
|
public function isUserAnon($user) |
|
631
|
|
|
{ |
|
632
|
|
|
if ($user instanceof User) { |
|
633
|
|
|
$username = $user->username; |
|
|
|
|
|
|
634
|
|
|
} else { |
|
635
|
|
|
$username = $user; |
|
636
|
|
|
} |
|
637
|
|
|
|
|
638
|
|
|
return filter_var($username, FILTER_VALIDATE_IP); |
|
639
|
|
|
} |
|
640
|
|
|
|
|
641
|
|
|
/** |
|
642
|
|
|
* Helper to properly translate a namespace name |
|
643
|
|
|
* @param int|string $namespace Namespace key as a string or ID |
|
644
|
|
|
* @param array $namespaces List of available namespaces |
|
645
|
|
|
* as retrieved from Project::getNamespaces |
|
646
|
|
|
* @return string Namespace name |
|
647
|
|
|
*/ |
|
648
|
|
|
public function nsName($namespace, $namespaces) |
|
649
|
|
|
{ |
|
650
|
|
|
if ($namespace === 'all') { |
|
651
|
|
|
return $this->getIntuition()->msg('all'); |
|
652
|
|
|
} elseif ($namespace === '0' || $namespace === 0 || $namespace === 'Main') { |
|
653
|
|
|
return $this->getIntuition()->msg('mainspace'); |
|
654
|
|
|
} else { |
|
655
|
|
|
return $namespaces[$namespace]; |
|
656
|
|
|
} |
|
657
|
|
|
} |
|
658
|
|
|
|
|
659
|
|
|
/** |
|
660
|
|
|
* Format a given number as a diff, colouring it green if it's postive, red if negative, gary if zero |
|
661
|
|
|
* @param number $size Diff size |
|
662
|
|
|
* @return string Markup with formatted number |
|
663
|
|
|
*/ |
|
664
|
1 |
|
public function diffFormat($size) |
|
665
|
|
|
{ |
|
666
|
1 |
|
if ($size < 0) { |
|
667
|
1 |
|
$class = 'diff-neg'; |
|
668
|
1 |
|
} elseif ($size > 0) { |
|
669
|
1 |
|
$class = 'diff-pos'; |
|
670
|
|
|
} else { |
|
671
|
1 |
|
$class = 'diff-zero'; |
|
672
|
|
|
} |
|
673
|
|
|
|
|
674
|
1 |
|
$size = $this->numberFormat($size); |
|
675
|
|
|
|
|
676
|
1 |
|
return "<span class='$class'>$size</span>"; |
|
677
|
|
|
} |
|
678
|
|
|
|
|
679
|
|
|
/** |
|
680
|
|
|
* Format a time duration as humanized string. |
|
681
|
|
|
* @param int $seconds Number of seconds. |
|
682
|
|
|
* @param bool $translate Used for unit testing. Set to false to return |
|
683
|
|
|
* the value and i18n key, instead of the actual translation. |
|
684
|
|
|
* @return string|array Examples: '30 seconds', '2 minutes', '15 hours', '500 days', |
|
685
|
|
|
* or [30, 'num-seconds'] (etc.) if $translate is false. |
|
686
|
|
|
*/ |
|
687
|
1 |
|
public function formatDuration($seconds, $translate = true) |
|
688
|
|
|
{ |
|
689
|
1 |
|
list($val, $key) = $this->getDurationMessageKey($seconds); |
|
690
|
|
|
|
|
691
|
1 |
|
if ($translate) { |
|
692
|
|
|
return $this->numberFormat($val) . ' ' . $this->intuitionMessage("num-$key", [$val]); |
|
693
|
|
|
} else { |
|
694
|
1 |
|
return [$this->numberFormat($val), "num-$key"]; |
|
695
|
|
|
} |
|
696
|
|
|
} |
|
697
|
|
|
|
|
698
|
|
|
/** |
|
699
|
|
|
* Given a time duration in seconds, generate a i18n message key and value. |
|
700
|
|
|
* @param int $seconds Number of seconds. |
|
701
|
|
|
* @return array<integer|string> [int - message value, string - message key] |
|
702
|
|
|
*/ |
|
703
|
1 |
|
private function getDurationMessageKey($seconds) |
|
704
|
|
|
{ |
|
705
|
|
|
/** @var int Value to show in message */ |
|
706
|
1 |
|
$val = $seconds; |
|
707
|
|
|
|
|
708
|
|
|
/** @var string Unit of time, used in the key for the i18n message */ |
|
709
|
1 |
|
$key = 'seconds'; |
|
710
|
|
|
|
|
711
|
1 |
|
if ($seconds >= 86400) { |
|
712
|
|
|
// Over a day |
|
713
|
1 |
|
$val = (int) floor($seconds / 86400); |
|
714
|
1 |
|
$key = 'days'; |
|
715
|
1 |
|
} elseif ($seconds >= 3600) { |
|
716
|
|
|
// Over an hour, less than a day |
|
717
|
1 |
|
$val = (int) floor($seconds / 3600); |
|
718
|
1 |
|
$key = 'hours'; |
|
719
|
1 |
|
} elseif ($seconds >= 60) { |
|
720
|
|
|
// Over a minute, less than an hour |
|
721
|
1 |
|
$val = (int) floor($seconds / 60); |
|
722
|
1 |
|
$key = 'minutes'; |
|
723
|
|
|
} |
|
724
|
|
|
|
|
725
|
1 |
|
return [$val, $key]; |
|
726
|
|
|
} |
|
727
|
|
|
} |
|
728
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.