Completed
Push — master ( 53827a...dc1ed5 )
by Iurii
03:58
created

Translator::getLanguagesTranslator()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 4
nc 1
nop 0
1
<?php
2
3
/**
4
 * @package Translator
5
 * @author Iurii Makukh <[email protected]>
6
 * @copyright Copyright (c) 2017, Iurii Makukh <[email protected]>
7
 * @license https://www.gnu.org/licenses/gpl-3.0.en.html GPL-3.0+
8
 */
9
10
namespace gplcart\modules\translator\controllers;
11
12
use gplcart\core\models\File as FileModel;
13
use gplcart\core\controllers\backend\Controller as BackendController;
14
use gplcart\modules\translator\models\Translator as TranslatorModuleModel;
15
16
/**
17
 * Handles incoming requests and outputs data related to Translator module
18
 */
19
class Translator extends BackendController
20
{
21
22
    /**
23
     * File model class instance
24
     * @var \gplcart\core\models\File $file
25
     */
26
    protected $file;
27
28
    /**
29
     * Translator model class instance
30
     * @var \gplcart\modules\translator\models\Translator $translator
31
     */
32
    protected $translator;
33
34
    /**
35
     * The current translation file
36
     * @var string
37
     */
38
    protected $data_file;
39
40
    /**
41
     * An array of translation strings
42
     * @var array
43
     */
44
    protected $data_content;
45
46
    /**
47
     * The current language
48
     * @var array
49
     */
50
    protected $data_language;
51
52
    /**
53
     * @param FileModel $file
54
     * @param TranslatorModuleModel $translator
55
     */
56
    public function __construct(FileModel $file,
57
            TranslatorModuleModel $translator)
58
    {
59
        parent::__construct();
60
61
        $this->file = $file;
62
        $this->translator = $translator;
63
    }
64
65
    /**
66
     * Displays the language list page
67
     */
68
    public function languageTranslator()
69
    {
70
        $this->setTitleLanguageTranslator();
71
        $this->setBreadcrumbLanguageTranslator();
72
73
        $this->setData('languages', $this->getLanguagesTranslator());
74
75
        $this->outputLanguageTranslator();
76
    }
77
78
    /**
79
     * Returns an array of sorted languages
80
     * @return array
81
     */
82
    protected function getLanguagesTranslator()
83
    {
84
        $languages = $this->language->getList();
85
        gplcart_array_sort($languages, 'name');
86
        return gplcart_array_split($languages, 6);
87
    }
88
89
    /**
90
     * Set titles on the language list page
91
     */
92
    protected function setTitleLanguageTranslator()
93
    {
94
        $this->setTitle($this->text('Translator'));
95
    }
96
97
    /**
98
     * Sets breadcrumbs on the language list page
99
     */
100
    protected function setBreadcrumbLanguageTranslator()
101
    {
102
        $this->setBreadcrumbHome();
103
    }
104
105
    /**
106
     * Render and output the language list page
107
     */
108
    protected function outputLanguageTranslator()
109
    {
110
        $this->output('translator|languages');
111
    }
112
113
    /**
114
     * Displays the file overview page
115
     * @param string $langcode
116
     */
117
    public function filesTranslator($langcode)
118
    {
119
        $this->setLanguageTranslator($langcode);
120
121
        $this->downloadFileTranslator();
122
        $this->actionFilesTranslator();
123
124
        $this->setTitleFilesTranslator();
125
        $this->setBreadcrumbFilesTranslator();
126
127
        $this->setData('language', $this->data_language);
128
        $this->setData('files', $this->getFilesTranslator());
129
130
        $this->outputFilesTranslator();
131
    }
132
133
    /**
134
     * Download a translation file
135
     */
136
    protected function downloadFileTranslator()
137
    {
138
        $hash = $this->getQuery('download');
139
        if (!empty($hash) && $this->access('module_translator_download')) {
140
            $info = $this->getDownloadFileTranslator($hash);
141
            if (!empty($info)) {
142
                $this->download($info[0], $info[1], array('text' => !empty($info[2])));
143
            }
144
        }
145
    }
146
147
    /**
148
     * Returns an array of file
149
     * @param string $hash
150
     * @return array
151
     */
152
    protected function getDownloadFileTranslator($hash)
153
    {
154
        $parts = $this->parseHashTranslator($hash);
155
156
        if (empty($parts)) {
157
            return array();
158
        }
159
160
        list($module_id, $file) = $parts;
161
162
        if (gplcart_path_is_absolute($file)) {
163
            return array($file, "$module_id-" . basename($file));
164
        }
165
166
        $csv_string = $this->translator->readZip($module_id, $file, $this->data_language['code']);
167
        return empty($csv_string) ? array() : array($csv_string, $hash, true);
168
    }
169
170
    /**
171
     * Parses a hash string containing module ID and translation file
172
     * @param string $hash
173
     * @return array
174
     */
175
    protected function parseHashTranslator($hash)
176
    {
177
        if (pathinfo($hash, PATHINFO_EXTENSION) === 'csv') {
178
            $parts = explode('-', $hash, 2);
179
            if (empty($parts[1])) {
180
                return array();
181
            }
182
183
            list($module_id, $file) = $parts;
184
        } else {
185
186
            $file = gplcart_path_absolute(gplcart_string_decode($hash));
187
            if (!$this->translator->isTranslationFile($file, $this->data_language['code'])) {
188
                return array();
189
            }
190
191
            $module_id = $this->translator->getModuleIdFromPath($file);
192
193
            if (empty($module_id)) {
194
                $module_id = 'core';
195
            }
196
        }
197
198
        if ($module_id !== 'core' && !$this->config->getModule($module_id)) {
199
            return array();
200
        }
201
202
        return array($module_id, $file);
203
    }
204
205
    /**
206
     * Applies an action to the selected translation files
207
     */
208
    protected function actionFilesTranslator()
209
    {
210
        list($selected, $action) = $this->getPostedAction();
211
212
        $deleted = 0;
213
        foreach ($selected as $hash) {
214
            if ($action === 'delete' && $this->access('module_translator_delete')) {
215
                $deleted += (int) $this->deleteFileTranslator($hash);
216
            }
217
        }
218
219
        if ($deleted > 0) {
220
            $message = $this->text('Deleted %num item(s)', array('%num' => $deleted));
221
            $this->setMessage($message, 'success');
222
        }
223
    }
224
225
    /**
226
     * Deletes a translation file
227
     * @param string $id
228
     * @return boolean
229
     */
230
    protected function deleteFileTranslator($id)
231
    {
232
        $info = $this->getDownloadFileTranslator($id);
233
234
        if (isset($info[0])) {
235
            return $this->translator->delete($info[0], $this->data_language['code']);
236
        }
237
238
        return false;
239
    }
240
241
    /**
242
     * Returns an array of files for the language
243
     * @return array
244
     */
245
    protected function getFilesTranslator()
246
    {
247
        if ($this->getQuery('tab') === 'compiled') {
248
            return $this->getCompiledFilesTranslator();
249
        }
250
251
        return $this->getPrimaryFilesTranslator();
252
    }
253
254
    /**
255
     * Sets titles on the file overview page
256
     */
257
    protected function setTitleFilesTranslator()
258
    {
259
        $vars = array('%name' => $this->data_language['name']);
260
        $this->setTitle($this->text('Translations for %name', $vars));
261
    }
262
263
    /**
264
     * Sets breadcrumbs on the files overview page
265
     */
266
    protected function setBreadcrumbFilesTranslator()
267
    {
268
        $breadcrumbs = array();
269
270
        $breadcrumbs[] = array(
271
            'url' => $this->url('admin'),
272
            'text' => $this->text('Dashboard')
273
        );
274
275
        $breadcrumbs[] = array(
276
            'url' => $this->url('admin/tool/translator'),
277
            'text' => $this->text('Languages')
278
        );
279
280
        $this->setBreadcrumbs($breadcrumbs);
281
    }
282
283
    /**
284
     * Displays the upload translation page
285
     * @param string $langcode
286
     */
287
    public function uploadTranslator($langcode)
288
    {
289
        $this->controlAccessUploadTranslator();
290
291
        $this->setLanguageTranslator($langcode);
292
        $this->setTitleUploadTranslator();
293
        $this->setBreadcrumbUploadTranslator();
294
295
        $this->setData('language', $this->data_language);
296
        $this->setData('modules', $this->config->getModules());
297
298
        $this->submitUploadTranslator();
299
        $this->outputUploadTranslator();
300
    }
301
302
    /**
303
     * Controls access to upload a translation file
304
     */
305
    protected function controlAccessUploadTranslator()
306
    {
307
        $this->controlAccess('file_upload');
308
        $this->controlAccess('module_translator_upload');
309
    }
310
311
    /**
312
     * Sets titles on the upload translation page
313
     */
314
    protected function setTitleUploadTranslator()
315
    {
316
        $vars = array('%name' => $this->data_language['name']);
317
        $this->setTitle($this->text('Upload translation for %name', $vars));
318
    }
319
320
    /**
321
     * Sets breadcrumbs on the upload translation page
322
     */
323
    protected function setBreadcrumbUploadTranslator()
324
    {
325
        $breadcrumbs = array();
326
327
        $breadcrumbs[] = array(
328
            'url' => $this->url('admin'),
329
            'text' => $this->text('Dashboard')
330
        );
331
332
        $breadcrumbs[] = array(
333
            'url' => $this->url('admin/tool/translator'),
334
            'text' => $this->text('Languages')
335
        );
336
337
        $breadcrumbs[] = array(
338
            'url' => $this->url("admin/tool/translator/{$this->data_language['code']}"),
339
            'text' => $this->text('Translations for %name', array('%name' => $this->data_language['name']))
340
        );
341
342
        $this->setBreadcrumbs($breadcrumbs);
343
    }
344
345
    /**
346
     * Handles submission of translation file
347
     */
348
    protected function submitUploadTranslator()
349
    {
350
        if ($this->isPosted('save') && $this->validateUploadTranslator()) {
351
            $this->copyFileTranslator();
352
        }
353
    }
354
355
    /**
356
     * Validates a uploaded translation file
357
     * @return boolean
358
     */
359
    protected function validateUploadTranslator()
360
    {
361
        $this->setSubmitted('translation');
362
363
        $this->validateUploadScopeTranslator();
364
        $this->validateUploadFileTranslator();
365
366
        return !$this->hasErrors();
367
    }
368
369
    /**
370
     * Validates scope of uploaded file
371
     * @return boolean
372
     */
373
    protected function validateUploadScopeTranslator()
374
    {
375
        $scope = $this->getSubmitted('scope');
376
377
        if (!empty($scope) && !$this->config->getModule($scope)) {
378
            $this->setError('scope', $this->text('@field has invalid value', array('@field' => $scope)));
379
            return false;
380
        }
381
382
        $this->setSubmitted('destination', $this->language->getFile($this->data_language['code'], $scope));
383
        return true;
384
    }
385
386
    /**
387
     * Validates uploaded translation file
388
     * @return boolean|null
389
     */
390
    protected function validateUploadFileTranslator()
391
    {
392
        if ($this->isError()) {
393
            return null;
394
        }
395
396
        $file = $this->request->file('file');
397
398
        if (empty($file)) {
399
            $this->setError('file', $this->text('File is required'));
400
            return false;
401
        }
402
403
        $result = $this->file->upload($file, 'csv');
404
405
        if ($result !== true) {
406
            $this->setError('file', $result);
407
            return false;
408
        }
409
410
        $this->setSubmitted('file', $this->file->getTransferred());
411
        return true;
412
    }
413
414
    /**
415
     * Copy a uploaded translation
416
     */
417
    protected function copyFileTranslator()
418
    {
419
        $this->controlAccessUploadTranslator();
420
421
        $source = $this->getSubmitted('file');
422
        $destination = $this->getSubmitted('destination');
423
        $result = $this->translator->copy($source, $destination);
424
425
        if ($result) {
426
            $this->redirect('', $this->text('Translation has been saved. Now you can <a href="@url">refresh language</a>', array('@url' => $this->url('admin/settings/language'))), 'success');
427
        }
428
429
        $this->redirect('', $this->text('Translation has not been saved'), 'warning');
430
    }
431
432
    /**
433
     * Render and output the upload translation page
434
     */
435
    protected function outputUploadTranslator()
436
    {
437
        $this->output('translator|upload');
438
    }
439
440
    /**
441
     * Render and output the file overview page
442
     */
443
    protected function outputFilesTranslator()
444
    {
445
        $this->output('translator|files');
446
    }
447
448
    /**
449
     * Returns an array of primary translations
450
     * @return array
451
     */
452
    protected function getPrimaryFilesTranslator()
453
    {
454
        $files = array($this->language->getFile($this->data_language['code']));
455
456
        foreach (array_keys($this->config->getModules()) as $module_id) {
457
            $files[$module_id] = $this->language->getFile($this->data_language['code'], $module_id);
458
        }
459
460
        foreach ($files as $id => $file) {
461
            if (is_file($file)) {
462
                $files[$id] = $this->buildFileInfoTranslator($file);
463
            } else {
464
                unset($files[$id]);
465
            }
466
        }
467
468
        return $files;
469
    }
470
471
    /**
472
     * Returns an array of compiled translation files
473
     * @return array
474
     */
475
    protected function getCompiledFilesTranslator()
476
    {
477
        $directory = $this->language->getCompiledDirectory($this->data_language['code']);
478
479
        $files = array();
480
        if (is_dir($directory)) {
481
            foreach (gplcart_file_scan($directory, array('csv')) as $file) {
482
                $files[] = $this->buildFileInfoTranslator($file);
483
            }
484
        }
485
486
        return $files;
487
    }
488
489
    /**
490
     * Build translation file info
491
     * @param string $file
492
     * @return array
493
     */
494
    protected function buildFileInfoTranslator($file)
495
    {
496
        $path = gplcart_path_relative($file);
497
        $context = str_replace(array('-', '_'), array('/', '/*/'), pathinfo(basename($path), PATHINFO_FILENAME));
498
499
        $langcode = $this->data_language['code'];
500
        $js_file = $this->language->getContextJsFile($langcode);
501
        $common_file = $this->language->getCommonFile($langcode);
502
503
        if (substr($js_file, -strlen($path)) === $path) {
504
            $context = $this->text('No context');
505
        }
506
507
        if (substr($common_file, -strlen($path)) === $path) {
508
            $context = $this->text('No context');
509
        }
510
511
        return array(
512
            'path' => $path,
513
            'modified' => filemtime($file),
514
            'hash' => gplcart_string_encode($path),
515
            'filesize' => gplcart_file_size(filesize($file)),
516
            'context' => preg_replace('/\*\/$/', '*', $context),
517
            'progress' => $this->translator->getFileInfo($file)
518
        );
519
    }
520
521
    /**
522
     * Sets the current language
523
     * @param string $langcode
524
     */
525
    protected function setLanguageTranslator($langcode)
526
    {
527
        $this->data_language = $this->language->get($langcode);
528
529
        if (empty($this->data_language)) {
530
            $this->outputHttpStatus(404);
531
        }
532
    }
533
534
    /**
535
     * Displays list of translation files available for import
536
     * @param string $langcode
537
     */
538
    public function listImportTranslator($langcode)
539
    {
540
        $this->setLanguageTranslator($langcode);
541
542
        $this->actionImportTranslator();
543
        $this->downloadFileTranslator();
544
545
        $this->setTitleListImportTranslator();
546
        $this->setBreadcrumbListImportTranslator();
547
548
        $this->setData('language', $this->data_language);
549
        $this->setData('modules', $this->config->getModules());
550
        $this->setData('list', $this->translator->getImportList($langcode));
551
        $this->setData('time', $this->config->get('module_translator_saved', 0));
552
        $this->setData('download_url', $this->translator->getImportDownloadUrl());
553
554
        $this->submitImportTranslator();
555
        $this->outputListImportTranslator();
556
    }
557
558
    /**
559
     * Handles submitted import
560
     */
561
    protected function submitImportTranslator()
562
    {
563
        if ($this->isPosted('update')) {
564
            if ($this->translator->clearImport()) {
565
                $this->redirect('', $this->text('File has been updated'), 'success');
566
            }
567
            $this->redirect('', $this->text('File has not been updated'), 'warning');
568
        }
569
    }
570
571
    /**
572
     * Bulk actions for selected translations
573
     */
574
    protected function actionImportTranslator()
575
    {
576
        list($selected, $action) = $this->getPostedAction();
577
578
        $submitted = array();
579
        foreach ($selected as $id) {
580
            if ($action === 'import') {
581
                list($module_id, $file) = explode('-', $id, 2);
582
                if (isset($submitted[$module_id])) {
583
                    $this->setMessage($this->text('Please select only one file per module'), 'warning');
584
                    return null;
585
                }
586
587
                $submitted[$module_id] = $file;
588
            }
589
        }
590
591
        $imported = 0;
592
        foreach ($submitted as $module_id => $file) {
593
            $imported += (int) $this->translator->importContent($module_id, $file, $this->data_language['code']);
594
        }
595
596
        if ($imported > 0) {
597
            $this->setMessage($this->text('Imported @num translation(s). Now you can <a href="@url">refresh language</a>', array('@num' => $imported, '@url' => $this->url('admin/settings/language'))), 'success');
598
        }
599
    }
600
601
    /**
602
     * Sets titles on the translation list page
603
     */
604
    protected function setTitleListImportTranslator()
605
    {
606
        $vars = array('%name' => $this->data_language['name']);
607
        $this->setTitle($this->text('Import translations for %name', $vars));
608
    }
609
610
    /**
611
     * Sets breadcrumbs on the translation list page
612
     */
613
    protected function setBreadcrumbListImportTranslator()
614
    {
615
        $breadcrumbs = array();
616
617
        $breadcrumbs[] = array(
618
            'url' => $this->url('admin'),
619
            'text' => $this->text('Dashboard')
620
        );
621
622
        $breadcrumbs[] = array(
623
            'url' => $this->url('admin/tool/translator'),
624
            'text' => $this->text('Languages')
625
        );
626
627
        $this->setBreadcrumbs($breadcrumbs);
628
    }
629
630
    /**
631
     * Render and output the translation list page
632
     */
633
    protected function outputListImportTranslator()
634
    {
635
        $this->output('translator|import');
636
    }
637
638
    /**
639
     * Displays the translation view page
640
     * @param string $langcode
641
     * @param string $id
642
     */
643
    public function viewTranslator($langcode, $id)
644
    {
645
        $this->setLanguageTranslator($langcode);
646
        $this->setContentTranslator($id);
647
648
        $this->setTitleViewTranslator();
649
        $this->setBreadcrumbViewTranslator();
650
651
        $this->setData('strings', $this->data_content);
652
        $this->setData('language', $this->data_language);
653
        $this->outputViewTranslator();
654
    }
655
656
    /**
657
     * Read content from ZIP file
658
     * @param string $hash
659
     */
660
    protected function setContentTranslator($hash)
661
    {
662
        $parsed = $this->parseHashTranslator($hash);
663
664
        if (empty($parsed)) {
665
            $this->outputHttpStatus(403);
666
        }
667
668
        list($module_id, $file) = $parsed;
669
670
        if (gplcart_path_is_absolute($file)) {
671
            $this->data_content = $this->language->parseCsv($file);
672
        } else {
673
            $list = $this->translator->getImportList();
674
            if (!isset($list[$module_id][$this->data_language['code']][$file]['content'])) {
675
                $this->outputHttpStatus(403);
676
            }
677
678
            $this->data_content = $list[$module_id][$this->data_language['code']][$file]['content'];
679
        }
680
681
        // Untranslated strings go first
682
        usort($this->data_content, function($a) {
683
            return isset($a[1]) && $a[1] !== '' ? 1 : -1;
684
        });
685
    }
686
687
    /**
688
     * Sets titles on the translation view page
689
     */
690
    protected function setTitleViewTranslator()
691
    {
692
        $vars = array('%name' => $this->data_language['name']);
693
        $this->setTitle($this->text('Translation for %name', $vars));
694
    }
695
696
    /**
697
     * Sets breadcrumbs on the translation view page
698
     */
699
    protected function setBreadcrumbViewTranslator()
700
    {
701
        $breadcrumbs = array();
702
703
        $breadcrumbs[] = array(
704
            'url' => $this->url('admin'),
705
            'text' => $this->text('Dashboard')
706
        );
707
708
        $breadcrumbs[] = array(
709
            'url' => $this->url('admin/tool/translator'),
710
            'text' => $this->text('Languages')
711
        );
712
713
        $breadcrumbs[] = array(
714
            'url' => $this->url("admin/tool/translator/{$this->data_language['code']}/import"),
715
            'text' => $this->text('Import translations for %name', array('%name' => $this->data_language['name']))
716
        );
717
718
        $this->setBreadcrumbs($breadcrumbs);
719
    }
720
721
    /**
722
     * Render and output the translation view page
723
     */
724
    protected function outputViewTranslator()
725
    {
726
        $this->output('translator|view');
727
    }
728
729
}
730