Completed
Push — master ( 84d990...7c1130 )
by Iurii
01:32
created

Translator::submitImportTranslator()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

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