Total Complexity | 97 |
Total Lines | 974 |
Duplicated Lines | 0 % |
Changes | 16 | ||
Bugs | 1 | Features | 3 |
Complex classes like Localization_Editor often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use Localization_Editor, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
28 | class Localization_Editor implements Interface_Optionable |
||
29 | { |
||
30 | use Traits_Optionable; |
||
31 | |||
32 | const MESSAGE_INFO = 'info'; |
||
33 | const MESSAGE_ERROR = 'danger'; |
||
34 | const MESSAGE_WARNING = 'warning'; |
||
35 | const MESSAGE_SUCCESS = 'success'; |
||
36 | |||
37 | const ERROR_NO_SOURCES_AVAILABLE = 40001; |
||
38 | const ERROR_LOCAL_PATH_NOT_FOUND = 40002; |
||
39 | |||
40 | /** |
||
41 | * @var string |
||
42 | */ |
||
43 | protected $installPath; |
||
44 | |||
45 | /** |
||
46 | * @var Localization_Source[] |
||
47 | */ |
||
48 | protected $sources; |
||
49 | |||
50 | /** |
||
51 | * @var Request |
||
52 | */ |
||
53 | protected $request; |
||
54 | |||
55 | /** |
||
56 | * @var Localization_Source |
||
57 | */ |
||
58 | protected $activeSource; |
||
59 | |||
60 | /** |
||
61 | * @var Localization_Scanner |
||
62 | */ |
||
63 | protected $scanner; |
||
64 | |||
65 | /** |
||
66 | * @var Localization_Locale[] |
||
67 | */ |
||
68 | protected $appLocales = array(); |
||
69 | |||
70 | /** |
||
71 | * @var Localization_Locale |
||
72 | */ |
||
73 | protected $activeAppLocale; |
||
74 | |||
75 | /** |
||
76 | * @var Localization_Editor_Filters |
||
77 | */ |
||
78 | protected $filters; |
||
79 | |||
80 | /** |
||
81 | * @var array<string,string> |
||
82 | */ |
||
83 | protected $requestParams = array(); |
||
84 | |||
85 | /** |
||
86 | * @var string |
||
87 | */ |
||
88 | protected $varPrefix = 'applocalize_'; |
||
89 | |||
90 | /** |
||
91 | * @var int |
||
92 | */ |
||
93 | protected $perPage = 20; |
||
94 | |||
95 | /** |
||
96 | * @throws Localization_Exception |
||
97 | * @see \AppLocalize\Localization_Editor::ERROR_LOCAL_PATH_NOT_FOUND |
||
98 | */ |
||
99 | public function __construct() |
||
100 | { |
||
101 | $path = realpath(__DIR__.'/../'); |
||
102 | if($path === false) |
||
103 | { |
||
104 | throw new Localization_Exception( |
||
105 | 'Local path not found', |
||
106 | sprintf( |
||
107 | 'Could not get the parent folder\'s real path from [%s].', |
||
108 | __DIR__ |
||
109 | ), |
||
110 | self::ERROR_LOCAL_PATH_NOT_FOUND |
||
111 | ); |
||
112 | } |
||
113 | |||
114 | $this->installPath = $path; |
||
115 | $this->request = new Request(); |
||
116 | $this->scanner = Localization::createScanner(); |
||
117 | $this->scanner->load(); |
||
118 | |||
119 | $this->initSession(); |
||
120 | $this->initAppLocales(); |
||
121 | } |
||
122 | |||
123 | public function getRequest() : Request |
||
124 | { |
||
125 | return $this->request; |
||
126 | } |
||
127 | |||
128 | /** |
||
129 | * Adds a request parameter that will be persisted in all URLs |
||
130 | * within the editor. This can be used when integrating the |
||
131 | * editor in an existing page that needs specific request params. |
||
132 | * |
||
133 | * @param string $name |
||
134 | * @param string $value |
||
135 | * @return Localization_Editor |
||
136 | */ |
||
137 | public function addRequestParam(string $name, string $value) : Localization_Editor |
||
138 | { |
||
139 | $this->requestParams[$name] = $value; |
||
140 | return $this; |
||
141 | } |
||
142 | |||
143 | public function getActiveLocale() : Localization_Locale |
||
144 | { |
||
145 | return $this->activeAppLocale; |
||
146 | } |
||
147 | |||
148 | public function getActiveSource() : Localization_Source |
||
149 | { |
||
150 | return $this->activeSource; |
||
151 | } |
||
152 | |||
153 | protected function initSession() : void |
||
154 | { |
||
155 | if(session_status() != PHP_SESSION_ACTIVE) { |
||
156 | session_start(); |
||
157 | } |
||
158 | |||
159 | if(!isset($_SESSION['localization_messages'])) { |
||
160 | $_SESSION['localization_messages'] = array(); |
||
161 | } |
||
162 | } |
||
163 | |||
164 | public function getVarName(string $name) : string |
||
165 | { |
||
166 | return $this->varPrefix.$name; |
||
167 | } |
||
168 | |||
169 | /** |
||
170 | * @throws Localization_Exception |
||
171 | */ |
||
172 | protected function initSources() : void |
||
173 | { |
||
174 | $this->sources = Localization::getSources(); |
||
175 | |||
176 | if(empty($this->sources)) |
||
177 | { |
||
178 | throw new Localization_Exception( |
||
179 | 'Cannot start editor: no sources defined.', |
||
180 | null, |
||
181 | self::ERROR_NO_SOURCES_AVAILABLE |
||
182 | ); |
||
183 | } |
||
184 | |||
185 | $activeID = $this->request->registerParam($this->getVarName('source'))->setEnum(Localization::getSourceIDs())->get(); |
||
186 | if(empty($activeID)) { |
||
187 | $activeID = $this->getDefaultSourceID(); |
||
188 | } |
||
189 | |||
190 | $this->activeSource = Localization::getSourceByID($activeID); |
||
191 | } |
||
192 | |||
193 | protected function getDefaultSourceID() : string |
||
194 | { |
||
195 | $default = $this->getOption('default-source'); |
||
196 | if(!empty($default) && Localization::sourceAliasExists($default)) { |
||
197 | return Localization::getSourceByAlias($default)->getID(); |
||
198 | } |
||
199 | |||
200 | return $this->sources[0]->getID(); |
||
201 | } |
||
202 | |||
203 | protected function initAppLocales() : void |
||
204 | { |
||
205 | $names = array(); |
||
206 | |||
207 | $locales = Localization::getAppLocales(); |
||
208 | foreach($locales as $locale) { |
||
209 | if(!$locale->isNative()) { |
||
210 | $this->appLocales[] = $locale; |
||
211 | $names[] = $locale->getName(); |
||
212 | } |
||
213 | } |
||
214 | |||
215 | // use the default locale if no other is available. |
||
216 | if(empty($names)) { |
||
217 | $this->activeAppLocale = Localization::getAppLocale(); |
||
218 | return; |
||
219 | } |
||
220 | |||
221 | $activeID = $this->request->registerParam($this->getVarName('locale'))->setEnum($names)->get(); |
||
222 | if(empty($activeID)) { |
||
223 | $activeID = $this->appLocales[0]->getName(); |
||
224 | } |
||
225 | |||
226 | $this->activeAppLocale = Localization::getAppLocaleByName($activeID); |
||
227 | |||
228 | Localization::selectAppLocale($activeID); |
||
229 | } |
||
230 | |||
231 | protected function handleActions() : void |
||
232 | { |
||
233 | $this->initSources(); |
||
234 | |||
235 | $this->filters = new Localization_Editor_Filters($this); |
||
236 | |||
237 | if($this->request->getBool($this->getVarName('scan'))) |
||
238 | { |
||
239 | $this->executeScan(); |
||
240 | } |
||
241 | else if($this->request->getBool($this->getVarName('save'))) |
||
242 | { |
||
243 | $this->executeSave(); |
||
244 | } |
||
245 | } |
||
246 | |||
247 | public function getScanner() : Localization_Scanner |
||
248 | { |
||
249 | return $this->scanner; |
||
250 | } |
||
251 | |||
252 | /** |
||
253 | * @return string |
||
254 | * @throws Localization_Exception |
||
255 | * @see \AppLocalize\Localization_Editor::ERROR_RENDERING_FAILED |
||
256 | */ |
||
257 | public function render() : string |
||
258 | { |
||
259 | $this->handleActions(); |
||
260 | |||
261 | $appName = $this->getAppName(); |
||
262 | |||
263 | OutputBuffering::start(); |
||
264 | |||
265 | ?><!doctype html> |
||
266 | <html lang="en"> |
||
267 | <head> |
||
268 | <meta charset="utf-8"> |
||
269 | <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> |
||
270 | <meta name="description" content=""> |
||
271 | <title><?php echo $appName ?></title> |
||
272 | <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script> |
||
273 | <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script> |
||
274 | <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous"> |
||
275 | <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script> |
||
276 | <script src="https://kit.fontawesome.com/54212b9b2b.js" crossorigin="anonymous"></script> |
||
277 | <script><?php echo $this->getJavascript() ?></script> |
||
278 | <style><?php echo $this->getCSS() ?></style> |
||
279 | </head> |
||
280 | <body> |
||
281 | <nav class="navbar navbar-expand-md navbar-dark bg-dark fixed-top"> |
||
282 | <a class="navbar-brand" href="<?php echo $this->getURL() ?>"><?php echo $appName ?></a> |
||
283 | <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarsExampleDefault" aria-controls="navbarsExampleDefault" aria-expanded="false" aria-label="Toggle navigation"> |
||
284 | <span class="navbar-toggler-icon"></span> |
||
285 | </button> |
||
286 | <div class="collapse navbar-collapse" id="navbarsExampleDefault"> |
||
287 | <?php |
||
288 | if(!empty($this->appLocales)) |
||
289 | { |
||
290 | ?> |
||
291 | <ul class="navbar-nav mr-auto"> |
||
292 | <li class="nav-item dropdown"> |
||
293 | <a class="nav-link dropdown-toggle" href="#" id="dropdown01" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> |
||
294 | <?php pt('Text sources') ?> |
||
295 | </a> |
||
296 | <div class="dropdown-menu" aria-labelledby="dropdown01"> |
||
297 | <?php |
||
298 | foreach($this->sources as $source) |
||
299 | { |
||
300 | ?> |
||
301 | <a class="dropdown-item" href="<?php echo $this->getSourceURL($source) ?>"> |
||
302 | <?php |
||
303 | if($source->getID() === $this->activeSource->getID()) |
||
304 | { |
||
305 | ?> |
||
306 | <b><?php echo $source->getLabel() ?></b> |
||
307 | <?php |
||
308 | } |
||
309 | else |
||
310 | { |
||
311 | echo $source->getLabel(); |
||
312 | } |
||
313 | ?> |
||
314 | <?php |
||
315 | $untranslated = $source->countUntranslated($this->scanner); |
||
316 | if($untranslated > 0) { |
||
317 | ?> |
||
318 | (<span class="text-danger" title="<?php ptex('%1$s texts have not been translated in this text source.', 'Amount of texts', $untranslated) ?>"><?php echo $untranslated ?></span>) |
||
319 | <?php |
||
320 | } |
||
321 | ?> |
||
322 | </a> |
||
323 | <?php |
||
324 | } |
||
325 | ?> |
||
326 | </div> |
||
327 | </li> |
||
328 | <li class="nav-item dropdown"> |
||
329 | <a class="nav-link dropdown-toggle" href="#" id="dropdown01" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> |
||
330 | <?php echo $this->activeAppLocale->getLabel() ?> |
||
331 | </a> |
||
332 | <div class="dropdown-menu" aria-labelledby="dropdown01"> |
||
333 | <?php |
||
334 | foreach($this->appLocales as $locale) |
||
335 | { |
||
336 | ?> |
||
337 | <a class="dropdown-item" href="<?php echo $this->getLocaleURL($locale) ?>"> |
||
338 | <?php echo $locale->getLabel() ?> |
||
339 | </a> |
||
340 | <?php |
||
341 | } |
||
342 | ?> |
||
343 | </div> |
||
344 | </li> |
||
345 | <li class="nav-item"> |
||
346 | <a href="<?php echo $this->getScanURL() ?>" class="btn btn-light btn-sm" title="<?php pt('Scan all source files to find translatable texts.') ?>" data-toggle="tooltip"> |
||
347 | <i class="fa fa-refresh"></i> |
||
348 | <?php pt('Scan') ?> |
||
349 | </a> |
||
350 | </li> |
||
351 | <?php |
||
352 | if($this->scanner->hasWarnings()) { |
||
353 | ?> |
||
354 | <li class="nav-item"> |
||
355 | <a href="<?php echo $this->getWarningsURL() ?>"> |
||
356 | <span class="badge badge-warning" title="<?php pts('The last scan for translatable texts reported warnings.'); pts('Click for details.'); ?>" data-toggle="tooltip"> |
||
357 | <i class="fa fa-exclamation-triangle"></i> |
||
358 | <?php echo $this->scanner->countWarnings() ?> |
||
359 | </span> |
||
360 | </a> |
||
361 | </li> |
||
362 | <?php |
||
363 | } |
||
364 | ?> |
||
365 | </ul> |
||
366 | <?php |
||
367 | } |
||
368 | ?> |
||
369 | <?php |
||
370 | $backURL = $this->getOption('back-url'); |
||
371 | if(!empty($backURL)) |
||
372 | { |
||
373 | ?> |
||
374 | <a href="<?php echo $backURL ?>" class="btn btn-light btn-sm"> |
||
375 | <i class="fas fa-arrow-circle-left"></i> |
||
376 | <?php echo $this->getOption('back-label'); ?> |
||
377 | </a> |
||
378 | <?php |
||
379 | } |
||
380 | ?> |
||
381 | </div> |
||
382 | </nav> |
||
383 | <main role="main" class="container"> |
||
384 | <div> |
||
385 | <?php |
||
386 | if(empty($this->appLocales)) |
||
387 | { |
||
388 | ?> |
||
389 | <div class="alert alert-danger"> |
||
390 | <i class="fa fa-exclamation-triangle"></i> |
||
391 | <b><?php pt('Nothing to translate:') ?></b> |
||
392 | <?php pt('No application locales were added to translate to.') ?> |
||
393 | </div> |
||
394 | <?php |
||
395 | } |
||
396 | else if($this->request->getBool($this->getVarName('warnings'))) |
||
397 | { |
||
398 | echo $this->renderWarnings(); |
||
399 | } |
||
400 | else |
||
401 | { |
||
402 | ?> |
||
403 | <h1><?php echo $this->activeSource->getLabel() ?></h1> |
||
404 | <?php |
||
405 | if(!empty($_SESSION['localization_messages'])) |
||
406 | { |
||
407 | foreach($_SESSION['localization_messages'] as $def) |
||
408 | { |
||
409 | ?> |
||
410 | <div class="alert alert-<?php echo $def['type'] ?>" role="alert"> |
||
411 | <?php echo $def['text'] ?> |
||
412 | <button type="button" class="close" data-dismiss="alert" aria-label="<?php pt('Close') ?>" title="<?php pt('Dismiss this message.') ?>" data-toggle="tooltip"> |
||
413 | <span aria-hidden="true">×</span> |
||
414 | </button> |
||
415 | </div> |
||
416 | <?php |
||
417 | } |
||
418 | |||
419 | // reset the messages after having displayed them |
||
420 | $_SESSION['localization_messages'] = array(); |
||
421 | } |
||
422 | ?> |
||
423 | <p> |
||
424 | <?php |
||
425 | pt( |
||
426 | 'You are translating to %1$s', |
||
427 | '<span class="badge badge-info">'. |
||
428 | $this->activeAppLocale->getLabel(). |
||
429 | '</span>' |
||
430 | ); |
||
431 | ?><br> |
||
432 | <?php pt('Found %1$s texts to translate.', $this->activeSource->countUntranslated($this->scanner)) ?> |
||
433 | </p> |
||
434 | <br> |
||
435 | <?php |
||
436 | if(!$this->scanner->isScanAvailable()) |
||
437 | { |
||
438 | ?> |
||
439 | <div class="alert alert-primary" role="alert"> |
||
440 | <b><?php pt('No texts found:') ?></b> |
||
441 | <?php pt('The source folders have not been scanned yet.') ?> |
||
442 | </div> |
||
443 | <p> |
||
444 | <a href="<?php echo $this->getScanURL() ?>" class="btn btn-primary"> |
||
445 | <i class="fa fa-refresh"></i> |
||
446 | <?php pt('Scan files now') ?> |
||
447 | </a> |
||
448 | </p> |
||
449 | <?php |
||
450 | } |
||
451 | else |
||
452 | { |
||
453 | echo $this->filters->renderForm(); |
||
454 | |||
455 | $this->displayList(); |
||
456 | } |
||
457 | } |
||
458 | ?> |
||
459 | </div> |
||
460 | </main> |
||
461 | </body> |
||
462 | </html> |
||
463 | <?php |
||
464 | |||
465 | return OutputBuffering::getClean(); |
||
466 | } |
||
467 | |||
468 | protected function renderWarnings() : string |
||
469 | { |
||
470 | OutputBuffering::start(); |
||
471 | |||
472 | ?> |
||
473 | <h1><?php pt('Warnings') ?></h1> |
||
474 | <p class="abstract"> |
||
475 | <?php |
||
476 | pts('The following shows all texts where the system decided that they cannot be translated.'); |
||
477 | ?> |
||
478 | </p> |
||
479 | <dl> |
||
480 | <?php |
||
481 | $warnings = $this->scanner->getWarnings(); |
||
482 | |||
483 | foreach($warnings as $warning) |
||
484 | { |
||
485 | ?> |
||
486 | <dt><?php echo FileHelper::relativizePathByDepth($warning->getFile(), 3) ?>:<?php echo $warning->getLine() ?></dt> |
||
487 | <dd><?php echo $warning->getMessage() ?></dd> |
||
488 | <?php |
||
489 | } |
||
490 | |||
491 | ?> |
||
492 | </dl> |
||
493 | <?php |
||
494 | |||
495 | return OutputBuffering::getClean(); |
||
496 | } |
||
497 | |||
498 | /** |
||
499 | * @return Localization_Scanner_StringHash[] |
||
500 | */ |
||
501 | protected function getFilteredStrings() : array |
||
502 | { |
||
503 | $strings = $this->activeSource->getHashes($this->scanner); |
||
504 | |||
505 | $result = array(); |
||
506 | |||
507 | foreach($strings as $string) |
||
508 | { |
||
509 | if($this->filters->isStringMatch($string)) { |
||
510 | $result[] = $string; |
||
511 | } |
||
512 | } |
||
513 | |||
514 | return $result; |
||
515 | } |
||
516 | |||
517 | public function getRequestParams() : array |
||
518 | { |
||
519 | $params = $this->requestParams; |
||
520 | $params[$this->getVarName('locale')] = $this->activeAppLocale->getName(); |
||
521 | $params[$this->getVarName('source')] = $this->activeSource->getID(); |
||
522 | $params[$this->getVarName('page')] = $this->getPage(); |
||
523 | |||
524 | return $params; |
||
525 | } |
||
526 | |||
527 | protected function getPage() : int |
||
528 | { |
||
529 | return intval($this->request |
||
530 | ->registerParam($this->getVarName('page')) |
||
531 | ->setInteger() |
||
532 | ->get(0) |
||
533 | ); |
||
534 | } |
||
535 | |||
536 | protected function displayList() : void |
||
632 | </p> |
||
633 | </form> |
||
634 | |||
635 | <?php |
||
636 | } |
||
637 | |||
638 | protected function getPaginationURL(int $page, array $params=array()) : string |
||
643 | } |
||
644 | |||
645 | protected function renderListEntry(Localization_Scanner_StringHash $string) : void |
||
646 | { |
||
647 | $hash = $string->getHash(); |
||
648 | $text = $string->getText(); |
||
649 | |||
650 | $previewText = $string->getTranslatedText(); |
||
651 | if(empty($previewText)) { |
||
652 | $previewText = $text->getText(); |
||
653 | } |
||
654 | |||
655 | $shortText = $this->renderText($previewText, 50); |
||
656 | |||
657 | $files = $string->getFiles(); |
||
658 | |||
659 | ?> |
||
660 | <tr class="string-entry inactive" onclick="Editor.Toggle('<?php echo $hash ?>')" data-hash="<?php echo $hash ?>"> |
||
661 | <td class="string-text"><?php echo $shortText ?></td> |
||
662 | <td class="align-center string-status"><?php echo $this->renderStatus($string) ?></td> |
||
663 | <td class="align-center"><?php echo $this->renderTypes($string) ?></td> |
||
664 | <td class="align-right"><?php echo $this->renderFileNames($string) ?></td> |
||
665 | </tr> |
||
666 | <tr class="string-form"> |
||
667 | <td colspan="4"> |
||
668 | <?php pt('Native text:') ?> |
||
669 | <p class="native-text"><?php echo $this->renderText($text->getText()) ?></p> |
||
670 | <p> |
||
671 | <textarea rows="4" class="form-control" name="<?php echo $this->getVarName('strings') ?>[<?php echo $hash ?>]"><?php echo $string->getTranslatedText() ?></textarea> |
||
672 | </p> |
||
673 | <?php |
||
674 | $explanation = $text->getExplanation(); |
||
675 | if(!empty($explanation)) |
||
676 | { |
||
677 | ?> |
||
678 | <p> |
||
679 | <?php pt('Context information:') ?><br> |
||
680 | <span class="native-text"><?php echo $explanation ?></span> |
||
681 | </p> |
||
682 | <?php |
||
683 | } |
||
684 | ?> |
||
685 | <p> |
||
686 | <button type="button" class="btn btn-outline-primary btn-sm" onclick="Editor.Confirm('<?php echo $hash ?>')"> |
||
687 | <?php ptex('OK', 'Button') ?> |
||
688 | </button> |
||
689 | <button type="button" class="btn btn-outline-secondary btn-sm" onclick="Editor.Toggle('<?php echo $hash ?>')"> |
||
690 | <?php ptex('Cancel', 'Button') ?> |
||
691 | </button> |
||
692 | </p> |
||
693 | <div class="files-list"> |
||
694 | <p> |
||
695 | <?php |
||
696 | $totalFiles = count($files); |
||
697 | |||
698 | if($totalFiles == 1) |
||
699 | { |
||
700 | pt('Found in a single file:'); |
||
701 | } |
||
702 | else |
||
703 | { |
||
704 | pt('Found in %1$s files:', $totalFiles); |
||
705 | } |
||
706 | ?> |
||
707 | </p> |
||
708 | <div class="files-scroller"> |
||
709 | <ul> |
||
710 | <?php |
||
711 | $locations = $string->getStrings(); |
||
712 | |||
713 | foreach($locations as $location) |
||
714 | { |
||
715 | $file = $location->getSourceFile(); |
||
716 | $line = $location->getLine(); |
||
717 | |||
718 | $ext = FileHelper::getExtension($file); |
||
719 | |||
720 | if($ext == 'php') { |
||
721 | $icon = 'fab fa-php'; |
||
722 | } else if($ext == 'js') { |
||
723 | $icon = 'fab fa-js-square'; |
||
724 | } else { |
||
725 | $icon = 'fas fa-file-code'; |
||
726 | } |
||
727 | |||
728 | ?> |
||
729 | <li> |
||
730 | <i class="<?php echo $icon ?>"></i> |
||
731 | <?php echo $file ?><span class="line-number">:<?php echo $line ?></span> |
||
732 | </li> |
||
733 | <?php |
||
734 | } |
||
735 | ?> |
||
736 | </ul> |
||
737 | </div> |
||
738 | </div> |
||
739 | </td> |
||
740 | </tr> |
||
741 | <?php |
||
742 | } |
||
743 | |||
744 | protected function renderText(string $text, int $cutAt=0) : string |
||
745 | { |
||
746 | if(empty($text)) { |
||
747 | return ''; |
||
748 | } |
||
749 | |||
750 | if($cutAt > 0) { |
||
751 | $text = ConvertHelper::text_cut($text, $cutAt); |
||
752 | } |
||
753 | |||
754 | $text = htmlspecialchars($text); |
||
755 | |||
756 | $vars = $this->detectVariables($text); |
||
757 | |||
758 | foreach($vars as $var) { |
||
759 | $text = str_replace($var, '<span class="placeholder">'.$var.'</span>', $text); |
||
760 | } |
||
761 | |||
762 | return $text; |
||
763 | } |
||
764 | |||
765 | protected function detectVariables(string $string) : array |
||
766 | { |
||
767 | $result = array(); |
||
768 | preg_match_all('/%[0-9]+d|%s|%[0-9]+\$s/i', $string, $result, PREG_PATTERN_ORDER); |
||
769 | |||
770 | if(isset($result[0]) && !empty($result[0])) { |
||
771 | return $result[0]; |
||
772 | } |
||
773 | |||
774 | return array(); |
||
775 | } |
||
776 | |||
777 | protected function renderFileNames(Localization_Scanner_StringHash $hash) : string |
||
778 | { |
||
779 | $max = 2; |
||
780 | $total = $hash->countFiles(); |
||
781 | $keep = $hash->getFileNames(); |
||
782 | $keepTotal = count($keep); // with duplicate file names, this can be less than the file total |
||
783 | |||
784 | // add a counter of the additional files if the total |
||
785 | // is higher than the maximum to show |
||
786 | if($total > $max) |
||
787 | { |
||
788 | $length = $max; |
||
789 | if($length > $keepTotal) { |
||
790 | $length = $keepTotal; |
||
791 | } |
||
792 | |||
793 | $keep = array_slice($keep, 0, $length); |
||
794 | $keep[] = '+'.($total - $length); |
||
795 | } |
||
796 | |||
797 | $result = implode(', ', $keep); |
||
798 | |||
799 | return $result; |
||
800 | } |
||
801 | |||
802 | public function display() : void |
||
803 | { |
||
804 | echo $this->render(); |
||
805 | } |
||
806 | |||
807 | protected function getJavascript() : string |
||
808 | { |
||
809 | return FileHelper::readContents($this->installPath.'/js/editor.js'); |
||
810 | } |
||
811 | |||
812 | protected function getCSS() : string |
||
813 | { |
||
814 | return FileHelper::readContents($this->installPath.'/css/editor.css'); |
||
815 | } |
||
816 | |||
817 | public function getSourceURL(Localization_Source $source, array $params=array()) : string |
||
818 | { |
||
819 | $params[$this->getVarName('source')] = $source->getID(); |
||
820 | |||
821 | return $this->getURL($params); |
||
822 | } |
||
823 | |||
824 | public function getLocaleURL(Localization_Locale $locale, array $params=array()) : string |
||
825 | { |
||
826 | $params[$this->getVarName('locale')] = $locale->getName(); |
||
827 | |||
828 | return $this->getURL($params); |
||
829 | } |
||
830 | |||
831 | public function getScanURL() : string |
||
832 | { |
||
833 | return $this->getSourceURL($this->activeSource, array($this->getVarName('scan') => 'yes')); |
||
834 | } |
||
835 | |||
836 | public function getWarningsURL() : string |
||
837 | { |
||
838 | return $this->getSourceURL($this->activeSource, array($this->getVarName('warnings') => 'yes')); |
||
839 | } |
||
840 | |||
841 | public function getURL(array $params=array()) : string |
||
842 | { |
||
843 | $persist = $this->getRequestParams(); |
||
844 | |||
845 | foreach($persist as $name => $value) { |
||
846 | if(!isset($params[$name])) { |
||
847 | $params[$name] = $value; |
||
848 | } |
||
849 | } |
||
850 | |||
851 | return '?'.http_build_query($params); |
||
852 | } |
||
853 | |||
854 | /** |
||
855 | * @param string $url |
||
856 | * @return never-returns |
||
|
|||
857 | */ |
||
858 | public function redirect(string $url) : void |
||
859 | { |
||
860 | header('Location:'.$url); |
||
861 | exit; |
||
862 | } |
||
863 | |||
864 | protected function executeScan() : void |
||
865 | { |
||
866 | $this->scanner->scan(); |
||
867 | |||
868 | $this->addMessage( |
||
869 | t('The source files have been analyzed successfully at %1$s.', date('H:i:s')), |
||
870 | self::MESSAGE_SUCCESS |
||
871 | ); |
||
872 | |||
873 | $this->redirect($this->getSourceURL($this->activeSource)); |
||
874 | } |
||
875 | |||
876 | protected function executeSave() : void |
||
877 | { |
||
878 | $data = $_POST; |
||
879 | |||
880 | $translator = Localization::getTranslator($this->activeAppLocale); |
||
881 | |||
882 | $strings = $data[$this->getVarName('strings')]; |
||
883 | foreach($strings as $hash => $text) |
||
884 | { |
||
885 | $text = trim($text); |
||
886 | |||
887 | if(empty($text)) { |
||
888 | continue; |
||
889 | } |
||
890 | |||
891 | $translator->setTranslation($hash, $text); |
||
892 | } |
||
893 | |||
894 | $translator->save($this->activeSource, $this->scanner->getCollection()); |
||
895 | |||
896 | // refresh all the client files |
||
897 | Localization::writeClientFiles(true); |
||
898 | |||
899 | $this->addMessage( |
||
900 | t('The texts have been updated successfully at %1$s.', date('H:i:s')), |
||
901 | self::MESSAGE_SUCCESS |
||
902 | ); |
||
903 | |||
904 | $this->redirect($this->getURL()); |
||
905 | } |
||
906 | |||
907 | protected function renderStatus(Localization_Scanner_StringHash $hash) : string |
||
908 | { |
||
909 | if($hash->isTranslated()) { |
||
910 | return '<i class="fa fa-check text-success"></i>'; |
||
911 | } |
||
912 | |||
913 | return '<i class="fa fa-ban text-danger"></i>'; |
||
914 | } |
||
915 | |||
916 | protected function renderTypes(Localization_Scanner_StringHash $hash) : string |
||
917 | { |
||
918 | $types = array(); |
||
919 | |||
920 | if($hash->hasLanguageType('PHP')) { |
||
921 | $types[] = t('Server'); |
||
922 | } |
||
923 | |||
924 | if($hash->hasLanguageType('Javascript')) { |
||
925 | $types[] = t('Client'); |
||
926 | } |
||
927 | |||
928 | return implode(', ', $types); |
||
929 | } |
||
930 | |||
931 | protected function addMessage(string $message, string $type=self::MESSAGE_INFO) : void |
||
936 | ); |
||
937 | } |
||
938 | |||
939 | /** |
||
940 | * @return array<string,string> |
||
941 | */ |
||
942 | public function getDefaultOptions() : array |
||
943 | { |
||
944 | return array( |
||
945 | 'appname' => '', |
||
946 | 'default-source' => '', |
||
947 | 'back-url' => '', |
||
948 | 'back-label' => '' |
||
949 | ); |
||
950 | } |
||
951 | |||
952 | /** |
||
953 | * Sets the application name shown in the main navigation |
||
954 | * in the user interface. |
||
955 | * |
||
956 | * @param string $name |
||
957 | * @return Localization_Editor |
||
958 | */ |
||
959 | public function setAppName(string $name) : Localization_Editor |
||
960 | { |
||
961 | $this->setOption('appname', $name); |
||
962 | return $this; |
||
963 | } |
||
964 | |||
965 | public function getAppName() : string |
||
966 | { |
||
967 | $name = $this->getOption('appname'); |
||
968 | if(!empty($name)) { |
||
969 | return $name; |
||
970 | } |
||
971 | |||
972 | return t('Localization editor'); |
||
973 | } |
||
974 | |||
975 | /** |
||
976 | * Selects the default source to use if none has been |
||
977 | * explicitly selected. |
||
978 | * |
||
979 | * @param string $sourceID |
||
980 | * @return Localization_Editor |
||
981 | */ |
||
982 | public function selectDefaultSource(string $sourceID) : Localization_Editor |
||
983 | { |
||
984 | $this->setOption('default-source', $sourceID); |
||
985 | return $this; |
||
986 | } |
||
987 | |||
988 | /** |
||
989 | * Sets an URL that the translators can use to go back to |
||
990 | * the main application, for example if it is integrated into |
||
991 | * an existing application. |
||
992 | * |
||
993 | * @param string $url The URL to use for the link |
||
994 | * @param string $label Label of the link |
||
995 | * @return Localization_Editor |
||
996 | */ |
||
997 | public function setBackURL(string $url, string $label) : Localization_Editor |
||
1002 | } |
||
1003 | } |
||
1004 |