Passed
Push — master ( 52b2ea...eaf67c )
by Sebastian
08:23
created

Localization_Editor::getDefaultSourceID()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 4
c 0
b 0
f 0
nc 2
nop 0
dl 0
loc 8
rs 10
1
<?php
2
/**
3
 * File containing the {@link Localization_Editor} class.
4
 * 
5
 * @package Localization
6
 * @subpackage Editor
7
 * @see Localization_Translator
8
 */
9
10
declare(strict_types=1);
11
12
namespace AppLocalize;
13
14
use AppUtils\Traits_Optionable;
15
use AppUtils\Interface_Optionable;
16
use AppUtils\FileHelper;
17
use AppUtils\Request;
18
19
/**
20
 * User Inteface handler for editing localization files.
21
 *
22
 * @package Localization
23
 * @subpackage Editor
24
 * @author Sebastian Mordziol <[email protected]>
25
 */
26
class Localization_Editor implements Interface_Optionable
27
{
28
    use Traits_Optionable;
29
    
30
    const MESSAGE_INFO = 'info';
31
    const MESSAGE_ERROR = 'danger';
32
    const MESSAGE_WARNING = 'warning';
33
    const MESSAGE_SUCCESS = 'success';
34
    
35
    const ERROR_NO_SOURCES_AVAILABLE = 40001;
36
    
37
   /**
38
    * @var string
39
    */
40
    protected $installPath;
41
    
42
   /**
43
    * @var Localization_Source[]
44
    */
45
    protected $sources;
46
    
47
   /**
48
    * @var \AppUtils\Request
49
    */
50
    protected $request;
51
    
52
   /**
53
    * @var Localization_Source
54
    */
55
    protected $activeSource;
56
    
57
   /**
58
    * @var Localization_Scanner
59
    */
60
    protected $scanner;
61
    
62
   /**
63
    * @var Localization_Locale[]
64
    */
65
    protected $appLocales = array();
66
    
67
   /**
68
    * @var Localization_Locale
69
    */
70
    protected $activeAppLocale;
71
    
72
   /**
73
    * @var Localization_Editor_Filters
74
    */
75
    protected $filters;
76
77
   /**
78
    * @var string[]string
79
    */
80
    protected $requestParams = array();
81
    
82
   /**
83
    * @var string
84
    */
85
    protected $varPrefix = 'applocalize_';
86
    
87
    public function __construct()
88
    {
89
        $this->installPath = realpath(__DIR__.'/../');
90
        $this->request = new Request();
91
        $this->scanner = Localization::createScanner();
92
        $this->scanner->load();
93
94
        $this->initSession();
95
        $this->initAppLocales();
96
    }
97
    
98
    public function getRequest() : Request
99
    {
100
        return $this->request;
101
    }
102
    
103
   /**
104
    * Adds a request parameter that will be persisted in all URLs
105
    * within the editor. This can be used when integrating the
106
    * editor in an existing page that needs specific request params.
107
    * 
108
    * @param string $name
109
    * @param string $value
110
    * @return Localization_Editor
111
    */
112
    public function addRequestParam(string $name, string $value) : Localization_Editor
113
    {
114
        $this->requestParams[$name] = $value;
115
        return $this;
116
    }
117
    
118
    public function getActiveLocale() : Localization_Locale
119
    {
120
        return $this->activeAppLocale;
121
    }
122
    
123
    public function getActiveSource() : Localization_Source 
124
    {
125
        return $this->activeSource;
126
    }
127
    
128
    protected function initSession()
129
    {
130
        if(session_status() != PHP_SESSION_ACTIVE) {
131
            session_start();
132
        }
133
        
134
        if(!isset($_SESSION['localization_messages'])) {
135
            $_SESSION['localization_messages'] = array();
136
        }
137
    }
138
    
139
    public function getVarName($name)
140
    {
141
        return $this->varPrefix.$name;
142
    }
143
    
144
    protected function initSources()
145
    {
146
        $this->sources = Localization::getSources();
147
        
148
        if(empty($this->sources)) 
149
        {
150
            throw new Localization_Exception(
151
                'Cannot start editor: no sources defined.',
152
                null,
153
                self::ERROR_NO_SOURCES_AVAILABLE
154
            );
155
        }
156
        
157
        $activeID = $this->request->registerParam($this->getVarName('source'))->setEnum(Localization::getSourceIDs())->get();
158
        if(empty($activeID)) {
159
            $activeID = $this->getDefaultSourceID();
160
        }
161
        
162
        $this->activeSource = Localization::getSourceByID($activeID);
163
    }
164
    
165
    protected function getDefaultSourceID()
166
    {
167
        $default = $this->getOption('default-source');
168
        if(!empty($default) && Localization::sourceAliasExists($default)) {
169
            return Localization::getSourceByAlias($default)->getID();
170
        }
171
        
172
        return $this->sources[0]->getID();
173
    }
174
    
175
    protected function initAppLocales()
176
    {
177
        $names = array();
178
        
179
        $locales = Localization::getAppLocales();
180
        foreach($locales as $locale) {
181
            if(!$locale->isNative()) {
182
                $this->appLocales[] = $locale;
183
                $names[] = $locale->getName();
184
            }
185
        }
186
        
187
        // use the default locale if no other is available.
188
        if(empty($names)) {
189
            $this->activeAppLocale = Localization::getAppLocale();
190
            return;
191
        }
192
       
193
        $activeID = $this->request->registerParam($this->getVarName('locale'))->setEnum($names)->get();
194
        if(empty($activeID)) {
195
            $activeID = $this->appLocales[0]->getName();
196
        }
197
        
198
        $this->activeAppLocale = Localization::getAppLocaleByName($activeID);
199
        
200
        Localization::selectAppLocale($activeID);
201
    }
202
    
203
    protected function handleActions()
204
    {
205
        $this->initSources();
206
        
207
        $this->filters = new Localization_Editor_Filters($this);
208
        
209
        if($this->request->getBool($this->getVarName('scan'))) 
210
        {
211
            $this->executeScan();
212
        } 
213
        else if($this->request->getBool($this->getVarName('save'))) 
214
        {
215
            $this->executeSave();
216
        }
217
    }
218
    
219
    public function getScanner() : Localization_Scanner
220
    {
221
        return $this->scanner;
222
    }
223
    
224
    public function render()
225
    {
226
        $this->handleActions();
227
        
228
        $appName = $this->getAppName();
229
        
230
        ob_start();
231
        
232
?><!doctype html>
233
<html lang="en">
234
	<head>
235
        <meta charset="utf-8">
236
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
237
        <meta name="description" content="">
238
        <title><?php echo $appName ?></title>
239
        <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
240
        <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
241
        <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">
242
        <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
243
        <script src="https://kit.fontawesome.com/54212b9b2b.js" crossorigin="anonymous"></script>
244
        <script><?php echo $this->getJavascript() ?></script>
245
        <style><?php echo $this->getCSS() ?></style>
246
	</head>
247
	<body>
248
        <nav class="navbar navbar-expand-md navbar-dark bg-dark fixed-top">
249
            <a class="navbar-brand" href="<?php echo $this->getURL() ?>"><?php echo $appName ?></a>
250
            <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarsExampleDefault" aria-controls="navbarsExampleDefault" aria-expanded="false" aria-label="Toggle navigation">
251
                <span class="navbar-toggler-icon"></span>
252
            </button>
253
			
254
            <div class="collapse navbar-collapse" id="navbarsExampleDefault">
255
                <?php 
256
                    if(!empty($this->appLocales))
257
                    {
258
                        ?>
259
                            <ul class="navbar-nav mr-auto">
260
                        		<li class="nav-item dropdown">
261
                                    <a class="nav-link dropdown-toggle" href="#" id="dropdown01" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
262
                                    	<?php pt('Text sources') ?>
263
                                	</a>
264
                                    <div class="dropdown-menu" aria-labelledby="dropdown01">
265
                                    	<?php 
266
                                    	    foreach($this->sources as $source)
267
                                    	    {
268
                                    	       ?>
269
                                        			<a class="dropdown-item" href="<?php echo $this->getSourceURL($source) ?>">
270
                                        				<?php 
271
                                            				if($source->getID() === $this->activeSource->getID()) 
272
                                            				{
273
                                            				    ?>
274
                                            				    	<b><?php echo $source->getLabel() ?></b>
275
                                        				    	<?php 
276
                                            				}
277
                                            				else
278
                                            				{
279
                                            				    echo $source->getLabel();
280
                                            				}
281
                                        				?>
282
                                        				<?php
283
                                        				    $untranslated = $source->countUntranslated($this->scanner);
284
                                        				    if($untranslated > 0) {
285
                                        				        ?>
286
                                        				        	(<span class="text-danger" title="<?php pt('%1$s texts have not been translated in this text source.', $untranslated) ?>"><?php echo $untranslated ?></span>)
287
                                				            	<?php 
288
                                        				    }
289
                                    				    ?>
290
                                    				</a>
291
                                    			<?php 
292
                                    	    }
293
                                	    ?>
294
                                    </div>
295
                                </li>
296
                                <li class="nav-item dropdown">
297
                                    <a class="nav-link dropdown-toggle" href="#" id="dropdown01" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
298
                                    	<?php echo $this->activeAppLocale->getLabel() ?>
299
                                	</a>
300
                                    <div class="dropdown-menu" aria-labelledby="dropdown01">
301
                                    	<?php 
302
                                    	    foreach($this->appLocales as $locale)
303
                                    	    {
304
                                    	       ?>
305
                                        			<a class="dropdown-item" href="<?php echo $this->getLocaleURL($locale) ?>">
306
                                        				<?php echo $locale->getLabel() ?>
307
                                    				</a>
308
                                    			<?php 
309
                                    	    }
310
                                	    ?>
311
                                    </div>
312
                                </li>
313
                                <li class="nav-item">
314
                    				<a href="<?php echo $this->getScanURL() ?>" class="btn btn-light btn-sm" title="<?php pt('Scan all source files to find translateable texts.') ?>" data-toggle="tooltip">
315
                                    	<i class="fa fa-refresh"></i>
316
                                    	<?php pt('Scan') ?>
317
                                    </a>
318
                    			</li>
319
                    			<?php 
320
                        			if($this->scanner->hasWarnings()) {
321
                        			    ?>
322
                        			    	<li class="nav-item">
323
                        			    		<a href="<?php echo $this->getWarningsURL() ?>">
324
                            			    		<span class="badge badge-warning" title="<?php pts('The last scan for translateable texts reported warnings.'); pts('Click for details.'); ?>" data-toggle="tooltip">
325
                            			    			<i class="fa fa-exclamation-triangle"></i>
326
                            			    			<?php echo $this->scanner->countWarnings() ?>
327
                            			    		</span>
328
                        			    		</a>
329
                        			    	</li>
330
                        			    <?php 
331
                        			}
332
                    			?>
333
                            </ul>
334
                        <?php 
335
                    }
336
                ?>
337
                <?php 
338
                    $backURL = $this->getOption('back-url');
339
                    if(!empty($backURL)) 
340
                    {
341
                        ?>
342
                            <a href="<?php echo $backURL ?>" class="btn btn-light btn-sm">
343
                            	<i class="fas fa-arrow-circle-left"></i>
344
                            	<?php echo $this->getOption('back-label'); ?>
345
                        	</a>
346
                    	<?php 
347
                    }
348
            	?>
349
    		</div>
350
		</nav>
351
		<main role="main" class="container">
352
			<div>
353
    			<?php 
354
    			    if(empty($this->appLocales))
355
    			    {
356
    			        ?>
357
    			        	<div class="alert alert-danger">
358
    			        		<i class="fa fa-exclamation-triangle"></i>
359
    			        		<b><?php pt('Nothing to translate:') ?></b>
360
    			        		<?php pt('No application locales were added to translate to.') ?>
361
    			        	</div>
362
    			        <?php 
363
    			    }
364
    			    else if($this->request->getBool($this->getVarName('warnings')))
365
    			    {
366
    			        echo $this->renderWarnings();
367
    			    }
368
    			    else
369
    			    {
370
    			        ?>
371
            				<h1><?php echo $this->activeSource->getLabel() ?></h1>
372
            				<?php 
373
                				if(!empty($_SESSION['localization_messages'])) 
374
                				{
375
                				    foreach($_SESSION['localization_messages'] as $def)
376
                				    {
377
                				        ?>
378
                				        	<div class="alert alert-<?php echo $def['type'] ?>" role="alert">
379
                                        		<?php echo $def['text'] ?>
380
                                        		<button type="button" class="close" data-dismiss="alert" aria-label="<?php pt('Close') ?>" title="<?php pt('Dismiss this message.') ?>" data-toggle="tooltip">
381
                									<span aria-hidden="true">&times;</span>
382
            									</button>
383
                                        	</div>
384
            				        	<?php 
385
                				    }
386
                				    
387
                				    // reset the messages after having displayed them
388
                				    $_SESSION['localization_messages'] = array();
389
                				}
390
            				?>
391
            				<p>
392
            					<?php 
393
            				        pt(
394
                					    'You are translating to %1$s', 
395
                					    '<span class="badge badge-info">'.
396
                					       $this->activeAppLocale->getLabel().
397
                				        '</span>'
398
                                    );
399
            				    ?><br>
400
            					<?php pt('Found %1$s texts to translate.', $this->activeSource->countUntranslated($this->scanner)) ?>
401
            				</p>
402
            				<br>
403
            				<?php
404
                				if(!$this->scanner->isScanAvailable()) 
405
                				{
406
                				    ?>
407
                				    	<div class="alert alert-primary" role="alert">
408
                                        	<b><?php pt('No texts found:') ?></b> 
409
                                        	<?php pt('The source folders have not been scanned yet.') ?>
410
                                        </div>
411
                                        <p>
412
                                            <a href="<?php echo $this->getScanURL() ?>" class="btn btn-primary">
413
                                            	<i class="fa fa-refresh"></i>
414
                                            	<?php pt('Scan files now') ?>
415
                                            </a>
416
                                        </p>
417
                				    <?php 
418
                				}
419
                				else
420
                				{
421
                				    echo $this->filters->renderForm();
422
                				    echo $this->renderList();
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->renderList() targeting AppLocalize\Localization_Editor::renderList() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
423
                				}
424
            				
425
        				}
426
    				?>
427
			</div>
428
		</main>
429
	</body>
430
</html>
431
<?php
432
433
        return ob_get_clean();
434
    }
435
436
    protected function renderWarnings()
437
    {
438
        ob_start();
439
        
440
        ?>
441
        	<h1><?php pt('Warnings') ?></h1>
442
        	<p class="abstract">
443
        		<?php 
444
        		    pts('The following shows all texts where the system decided that they cannot be translated.');
445
       		    ?>
446
        	</p>
447
        	<dl>
448
        		<?php 
449
        		    $warnings = $this->scanner->getWarnings();
450
        		    
451
        		    foreach($warnings as $warning)
452
        		    {
453
        		        ?>
454
        		        	<dt><?php echo FileHelper::relativizePathByDepth($warning->getFile(), 3) ?>:<?php echo $warning->getLine() ?></dt>
455
        		        	<dd><?php echo $warning->getMessage() ?></dd>
456
        		        <?php 
457
        		    }
458
        		        
459
        		?>
460
        	</dl>
461
    	<?php 
462
    	
463
        return ob_get_clean();
464
    }
465
    
466
    protected function getFilteredStrings()
467
    {
468
        $strings = $this->activeSource->getHashes($this->scanner);
469
        
470
        $result = array();
471
        
472
        foreach($strings as $string)
473
        {
474
            if($this->filters->isStringMatch($string)) {
475
                $result[] = $string;
476
            }
477
        }
478
479
        return $result;
480
    }
481
    
482
    public function getRequestParams() : array
483
    {
484
        $params = $this->requestParams;
485
        $params[$this->getVarName('locale')] = $this->activeAppLocale->getName();
486
        $params[$this->getVarName('source')] = $this->activeSource->getID();
487
        $params[$this->getVarName('page')] = $this->getPage();
488
489
        return $params;
490
    }
491
    
492
    protected function getPage() : int
493
    {
494
        return intval($this->request
495
            ->registerParam($this->getVarName('page'))
496
            ->setInteger()
497
            ->get(0)
498
        );
499
    }
500
    
501
    protected $perPage = 20;
502
    
503
    protected function renderList()
504
    {
505
        $strings = $this->getFilteredStrings();
506
        
507
        if(empty($strings))
508
        {
509
            ?>
510
            	<div class="alert alert-info">
511
            		<?php pt('No matching strings found.') ?>
512
            	</div>
513
            <?php 
514
            
515
            return;
516
        }
517
        
518
        $total = count($strings);
519
        $page = $this->getPage();
520
        $pager = new \AppUtils\PaginationHelper($total, $this->perPage, $page);
521
        
522
        $keep = array_slice($strings, $pager->getOffsetStart(), $this->perPage);
523
        
524
        ?>
525
			<form method="post">
526
				<div class="form-hiddens">
527
					<?php 
528
    					$params = $this->getRequestParams();
529
    					foreach($params as $name => $value) {
530
    					    ?>
531
    					    	<input type="hidden" name="<?php echo $name ?>" value="<?php echo $value ?>">
532
    					    <?php 
533
    					}
534
					?>
535
				</div>
536
            	<table class="table table-hover">
537
    				<thead>
538
    					<tr>
539
    						<th><?php pt('Text') ?></th>
540
    						<th class="align-center"><?php pt('Translated?') ?></th>
541
    						<th class="align-center"><?php pt('Location') ?></th>
542
    						<th class="align-right"><?php pt('Sources') ?></th>
543
    					</tr>
544
    				</thead>
545
    				<tbody>
546
    					<?php 
547
    					    foreach($keep as $string)
548
    					    {
549
    					        $this->renderListEntry($string);
550
    					    }
551
    					?>
552
    				</tbody>
553
    			</table>
554
    			<?php 
555
        			if($pager->hasPages()) 
556
        			{
557
        			    $prevUrl = $this->getPaginationURL($pager->getPreviousPage());
558
        			    $nextUrl = $this->getPaginationURL($pager->getNextPage());
559
        			    
560
        			    ?>
561
        			    	<nav aria-label="<?php pt('Navigate available pages of texts.') ?>">
562
                                <ul class="pagination">
563
                                    <li class="page-item">
564
                                    	<a class="page-link" href="<?php echo $prevUrl ?>">
565
                                    		<i class="fa fa-arrow-left"></i>
566
                                		</a>
567
                            		</li>
568
                            		<?php 
569
                            		    $numbers = $pager->getPageNumbers();
570
                            		    foreach($numbers as $number) 
571
                            		    {
572
                            		        $url = $this->getPaginationURL($number);
573
                            		        
574
                            		        ?>
575
                            		        	<li class="page-item <?php if($pager->isCurrentPage($number)) { echo 'active'; } ?>">
576
                            		        		<a class="page-link" href="<?php echo $url ?>">
577
                            		        			<?php echo $number ?>
578
                        		        			</a>
579
                        		        		</li>
580
                            		        <?php 
581
                            		    }
582
                            		?>
583
                                    <li class="page-item">
584
                                    	<a class="page-link" href="<?php echo $nextUrl ?>">
585
                                    		<i class="fa fa-arrow-right"></i>
586
                                		</a>
587
                                	</li>
588
                                </ul>
589
                            </nav>
590
        			    <?php 
591
        			}
592
    			?>
593
				<br>
594
				<p>
595
					<button type="submit" name="<?php echo $this->getVarName('save') ?>" value="yes" class="btn btn-primary">
596
						<i class="fas fa-save"></i>
597
						<?php pt('Save now') ?>
598
					</button>
599
				</p>
600
			</form>
601
			
602
        <?php 
603
    }
604
    
605
    protected function getPaginationURL(int $page, $params=array())
606
    {
607
        $params[$this->getVarName('page')] = $page;
608
        
609
        return $this->getURL($params);
610
    }
611
    
612
    protected function renderListEntry(Localization_Scanner_StringHash $string)
613
    {
614
        $hash = $string->getHash();
615
        
616
        $previewText = $string->getTranslatedText();
617
        if(empty($previewText)) {
618
            $previewText = $string->getText();
619
        }
620
        
621
        $shortText =  $this->renderText($previewText, 50);
622
        
623
        $files = $string->getFiles();
624
        
625
        ?>
626
        	<tr class="string-entry inactive" onclick="Editor.Toggle('<?php echo $hash ?>')" data-hash="<?php echo $hash ?>">
627
        		<td class="string-text"><?php echo $shortText ?></td>
628
        		<td class="align-center string-status"><?php echo $this->renderStatus($string) ?></td>
629
        		<td class="align-center"><?php echo $this->renderTypes($string) ?></td>
630
        		<td class="align-right"><?php echo $this->renderFileNames($string) ?></td>
631
        	</tr>
632
        	<tr class="string-form">
633
        		<td colspan="4">
634
        			<?php echo pt('Native text:') ?>
0 ignored issues
show
Bug introduced by
Are you sure the usage of pt('Native text:') is correct as it seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
635
        			<p class="native-text"><?php echo $this->renderText($string->getText()) ?></p>
636
        			<p>
637
        				<textarea rows="4" class="form-control" name="<?php echo $this->getVarName('strings') ?>[<?php echo $hash ?>]"><?php echo $string->getTranslatedText() ?></textarea>
638
        			</p>
639
        			<p>
640
	        			<button type="button" class="btn btn-outline-primary btn-sm" onclick="Editor.Confirm('<?php echo $hash ?>')">
641
	        				<?php pt('OK') ?>
642
	        			</button>
643
	        			<button type="button" class="btn btn-outline-secondary btn-sm" onclick="Editor.Toggle('<?php echo $hash ?>')">
644
	        				<?php pt('Cancel') ?>
645
	        			</button>
646
        			</p>
647
        			<div class="files-list">
648
            			<p>
649
            				<?php 
650
            				    $totalFiles = count($files);
651
            				    
652
            				    if($totalFiles == 1)
653
            				    {
654
            				        pt('Found in a single file:');
655
            				    }
656
            				    else
657
            				    {
658
            				        pt('Found in %1$s files:', $totalFiles);
659
            				    }
660
    				        ?>
661
            			</p>
662
        				<div class="files-scroller">
663
                			<ul>
664
                				<?php 
665
                				    $locations = $string->getStrings();
666
                				    
667
                    				foreach($locations as $location) 
668
                    				{
669
                    				    $file = $location->getSourceFile();
670
                    				    $line = $location->getLine();
671
                    				    
672
                    				    $icon = '';
673
                    				    
674
                    				    $ext = \AppUtils\FileHelper::getExtension($file);
675
                    				    
676
                    				    if($ext == 'php') {
677
                    				        $icon = 'fab fa-php';
678
                    				    } else if($ext == 'js') {
679
                    				        $icon = 'fab fa-js-square';
680
                    				    } else {
681
                    				        $icon = 'fas fa-file-code';
682
                    				    }
683
                    				    
684
                    				    ?>
685
                    				    	<li>
686
                    				    		<i class="<?php echo $icon ?>"></i>
687
                    				    		<?php echo $file ?><span class="line-number">:<?php echo $line ?></span>
688
                    				    	</li>
689
                    				    <?php 
690
                    				}
691
                				?>
692
                			</ul>
693
            			</div>
694
        			</div>
695
        		</td>
696
        	</tr>
697
        <?php 
698
        
699
    }
700
    
701
    protected function renderText(string $text, int $cutAt=0) : string
702
    {
703
        if(empty($text)) {
704
            return $text;
705
        }
706
        
707
        if($cutAt > 0) {
708
            $text = \AppUtils\ConvertHelper::text_cut($text, $cutAt);
709
        }
710
        
711
        $text = htmlspecialchars($text);
712
        
713
        $vars = $this->detectVariables($text);
714
        
715
        foreach($vars as $var) {
716
            $text = str_replace($var, '<span class="placeholder">'.$var.'</span>', $text);
717
        }
718
        
719
        return $text; 
720
    }
721
    
722
    protected function detectVariables(string $string) : array
723
    {
724
        $result = array();
725
        preg_match_all('/%[0-9]+d|%s|%[0-9]+\$s/i', $string, $result, PREG_PATTERN_ORDER);
726
727
        if(isset($result[0]) && !empty($result[0])) {
728
            return $result[0];
729
        }
730
        
731
        return array();
732
    }
733
    
734
    protected function renderFileNames(Localization_Scanner_StringHash $hash) : string
735
    {
736
        $max = 2;
737
        $total = $hash->countFiles();
738
        $keep = $hash->getFileNames();
739
        $keepTotal = count($keep); // with duplicate file names, this can be less than the file total
740
        
741
        // add a counter of the additional files if the total
742
        // is higher than the maximum to show
743
        if($total > $max) 
744
        {
745
            $length = $max;
746
            if($length > $keepTotal) {
747
                $length = $keepTotal; 
748
            }
749
            
750
            $keep = array_slice($keep, 0, $length);
751
            $keep[] = '+'.($total - $length); 
752
        }
753
        
754
        $result = implode(', ', $keep);
755
        
756
        return $result;
757
    }
758
    
759
    public function display()
760
    {
761
        echo $this->render();
762
    }
763
    
764
    protected function getJavascript() : string
765
    {
766
        return file_get_contents($this->installPath.'/js/editor.js');
767
    }
768
    
769
    protected function getCSS() : string
770
    {
771
        return file_get_contents($this->installPath.'/css/editor.css');
772
    }
773
    
774
    public function getSourceURL(Localization_Source $source, array $params=array())
775
    {
776
        $params[$this->getVarName('source')] = $source->getID();
777
        
778
        return $this->getURL($params);
779
    }
780
    
781
    public function getLocaleURL(Localization_Locale $locale, array $params=array())
782
    {
783
        $params[$this->getVarName('locale')] = $locale->getName();
784
        
785
        return $this->getURL($params);
786
    }
787
    
788
    public function getScanURL()
789
    {
790
        return $this->getSourceURL($this->activeSource, array($this->getVarName('scan') => 'yes'));
791
    }
792
    
793
    public function getWarningsURL()
794
    {
795
        return $this->getSourceURL($this->activeSource, array($this->getVarName('warnings') => 'yes'));
796
    }
797
    
798
    public function getURL(array $params=array())
799
    {
800
        $persist = $this->getRequestParams();
801
        
802
        foreach($persist as $name => $value) {
803
            if(!isset($params[$name])) {
804
                $params[$name] = $value;
805
            }
806
        }
807
        
808
        return '?'.http_build_query($params);
809
    }
810
    
811
    public function redirect($url)
812
    {
813
        header('Location:'.$url);
814
        exit;
815
    }
816
    
817
    protected function executeScan()
818
    {
819
        $this->scanner->scan();
820
821
        $this->addMessage(
822
            t('The source files have been analyzed successfully at %1$s.', date('H:i:s')),
823
            self::MESSAGE_SUCCESS
824
        );
825
        
826
        $this->redirect($this->getSourceURL($this->activeSource));
827
    }
828
    
829
    protected function executeSave()
830
    {
831
        $data = $_POST;
832
        
833
        $translator = Localization::getTranslator($this->activeAppLocale);
834
        
835
        $strings = $data[$this->getVarName('strings')];
836
        foreach($strings as $hash => $text) 
837
        {
838
            $text = trim($text);
839
            
840
            if(empty($text)) {
841
                continue;
842
            } 
843
            
844
            $translator->setTranslation($hash, $text);
845
        }
846
        
847
        $translator->save($this->activeSource, $this->scanner->getCollection());
848
        
849
        // refresh all the client files
850
        Localization::writeClientFiles(true);
851
        
852
        $this->addMessage(
853
            t('The texts have been updated successfully at %1$s.', date('H:i:s')),
854
            self::MESSAGE_SUCCESS
855
        );
856
        
857
        $this->redirect($this->getURL());
858
    }
859
    
860
    protected function renderStatus(Localization_Scanner_StringHash $hash)
861
    {
862
        if($hash->isTranslated()) {
863
            return '<i class="fa fa-check text-success"></i>';
864
        }        
865
        
866
        return '<i class="fa fa-ban text-danger"></i>';
867
    }
868
    
869
    protected function renderTypes(Localization_Scanner_StringHash $hash)
870
    {
871
        $types = array();
872
        
873
        if($hash->hasLanguageType('PHP')) {
874
            $types[] = t('Server');
875
        }
876
        
877
        if($hash->hasLanguageType('Javascript')) {
878
            $types[] = t('Client');
879
        }
880
        
881
        return implode(', ', $types);
882
    }
883
    
884
    protected function addMessage($message, $type=self::MESSAGE_INFO)
885
    {
886
        $_SESSION['localization_messages'][] = array(
887
            'text' => $message,
888
            'type' => $type
889
        );
890
    }
891
    
892
    public function getDefaultOptions() : array
893
    {
894
        return array(
895
            'appname' => '',
896
            'default-source' => '',
897
            'back-url' => '',
898
            'back-label' => ''
899
        );
900
    }
901
    
902
   /**
903
    * Sets the application name shown in the main navigation
904
    * in the user interface.
905
    * 
906
    * @param string $name
907
    * @return Localization_Editor
908
    */
909
    public function setAppName(string $name) : Localization_Editor
910
    {
911
        $this->setOption('appname', $name);
912
        return $this;
913
    }
914
    
915
    public function getAppName() : string
916
    {
917
        $name = $this->getOption('appname');
918
        if(!empty($name)) {
919
            return $name;
920
        }
921
        
922
        return t('Localization editor');
923
    }
924
925
   /**
926
    * Selects the default source to use if none has been 
927
    * explicitly selected.
928
    * 
929
    * @param string $sourceID
930
    */
931
    public function selectDefaultSource(string $sourceID) : Localization_Editor
932
    {
933
        $this->setOption('default-source', $sourceID);
934
        return $this;
935
    }
936
    
937
   /**
938
    * Sets an URL that the translators can use to go back to
939
    * the main application, for example if it is integrated into
940
    * an existing application.
941
    * 
942
    * @param string $url The URL to use for the link
943
    * @param string $label Label of the link
944
    * @return Localization_Editor
945
    */
946
    public function setBackURL(string $url, string $label) : Localization_Editor
947
    {
948
        $this->setOption('back-url', $url);
949
        $this->setOption('back-label', $label);
950
        return $this;
951
    }
952
}