Completed
Push — dev ( 578824...b5d7bb )
by Iurii
04:24
created

Controller::setDefaultData()   C

Complexity

Conditions 7
Paths 20

Size

Total Lines 34
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 19
nc 20
nop 0
dl 0
loc 34
rs 6.7272
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * @package GPL Cart core
5
 * @author Iurii Makukh <[email protected]>
6
 * @copyright Copyright (c) 2015, Iurii Makukh
7
 * @license https://www.gnu.org/licenses/gpl.html GNU/GPLv3
8
 */
9
10
namespace gplcart\core;
11
12
use InvalidArgumentException;
13
14
/**
15
 * Base controller class
16
 */
17
abstract class Controller
18
{
19
20
    /**
21
     * Whether the current path is an installation area
22
     * @var boolean
23
     */
24
    protected $is_install = false;
25
26
    /**
27
     * Whether the site in maintenance mode
28
     * @var boolean
29
     */
30
    protected $is_maintenance = false;
31
32
    /**
33
     * Whether the current view is backend
34
     * @var boolean
35
     */
36
    protected $is_backend;
37
38
    /**
39
     * Whether the current request is AJAX
40
     * @var boolean
41
     */
42
    protected $is_ajax;
43
44
    /**
45
     * The current HTTP status code
46
     * @var string
47
     */
48
    protected $http_status;
49
50
    /**
51
     * Current theme name
52
     * @var string
53
     */
54
    protected $theme;
55
56
    /**
57
     * Frontend theme module name
58
     * @var string
59
     */
60
    protected $theme_frontend;
61
62
    /**
63
     * Backend theme module name
64
     * @var string
65
     */
66
    protected $theme_backend;
67
68
    /**
69
     * An numeric ID of the user currently visiting the site
70
     * @var integer
71
     */
72
    protected $uid;
73
74
    /**
75
     * Current user cart ID
76
     * @var integer|string
77
     */
78
    protected $cart_uid;
79
80
    /**
81
     * A random string generated from the session
82
     * @var string
83
     */
84
    protected $token;
85
86
    /**
87
     * Base URL
88
     * @var string
89
     */
90
    protected $base;
91
92
    /**
93
     * Current URL path without query
94
     * @var string
95
     */
96
    protected $path;
97
98
    /**
99
     * The request URI from $_SERVER['REQUEST_URI'] variable
100
     * @var string
101
     */
102
    protected $uri_path;
103
104
    /**
105
     * Full current URI including query and schema
106
     * @var string
107
     */
108
    protected $uri;
109
110
    /**
111
     * Current host
112
     * @var string
113
     */
114
    protected $host;
115
116
    /**
117
     * Current HTTP scheme
118
     * @var string
119
     */
120
    protected $scheme;
121
122
    /**
123
     * Current query
124
     * @var array
125
     */
126
    protected $query = array();
127
128
    /**
129
     * An array of filter parameters
130
     * @var array
131
     */
132
    protected $query_filter = array();
133
134
    /**
135
     * Array of template variables
136
     * @var array
137
     */
138
    protected $data = array();
139
140
    /**
141
     * Array of templates keyed by region for the current theme
142
     * @var array
143
     */
144
    protected $templates = array();
145
146
    /**
147
     * Current store ID
148
     * @var integer
149
     */
150
    protected $store_id;
151
152
    /**
153
     * Current route data
154
     * @var array
155
     */
156
    protected $current_route = array();
157
158
    /**
159
     * Current user data
160
     * @var array
161
     */
162
    protected $current_user = array();
163
164
    /**
165
     * Array of the current store
166
     * @var array
167
     */
168
    protected $current_store = array();
169
170
    /**
171
     * Array of the current theme
172
     * @var array
173
     */
174
    protected $current_theme = array();
175
176
    /**
177
     * The current HTML filter
178
     * @var array|false
179
     */
180
    protected $current_filter;
181
182
    /**
183
     * Current language data
184
     * @var array
185
     */
186
    protected $current_language;
187
188
    /**
189
     * Array of the current theme module info
190
     * @var array
191
     */
192
    protected $theme_settings = array();
193
194
    /**
195
     * Submitted form values
196
     * @var array
197
     */
198
    protected $submitted = array();
199
200
    /**
201
     * A key to get submitted data for $this->submitted
202
     * @var string
203
     */
204
    protected $post_key;
205
206
    /**
207
     * Array of validation errors
208
     * @var array
209
     */
210
    protected $errors = array();
211
212
    /**
213
     * User model instance
214
     * @var \gplcart\core\models\User $user
215
     */
216
    protected $user;
217
218
    /**
219
     * Store model instance
220
     * @var \gplcart\core\models\Store $store
221
     */
222
    protected $store;
223
224
    /**
225
     * Cart model instance
226
     * @var \gplcart\core\models\Cart $cart
227
     */
228
    protected $cart;
229
230
    /**
231
     * Language model instance
232
     * @var \gplcart\core\models\Language $language
233
     */
234
    protected $language;
235
236
    /**
237
     * Translation model instance
238
     * @var \gplcart\core\models\Translation $translation
239
     */
240
    protected $translation;
241
242
    /**
243
     * Validator model instance
244
     * @var \gplcart\core\models\Validator $validator
245
     */
246
    protected $validator;
247
248
    /**
249
     * Image model instance
250
     * @var \gplcart\core\models\Image $image
251
     */
252
    protected $image;
253
254
    /**
255
     * Library instance
256
     * @var \gplcart\core\Library $library
257
     */
258
    protected $library;
259
260
    /**
261
     * Current language code
262
     * @var string
263
     */
264
    protected $langcode;
265
266
    /**
267
     * URL class instance
268
     * @var \gplcart\core\helpers\Url $url
269
     */
270
    protected $url;
271
272
    /**
273
     * Asset class instance
274
     * @var \gplcart\core\helpers\Asset $asset
275
     */
276
    protected $asset;
277
278
    /**
279
     * Request class instance
280
     * @var \gplcart\core\helpers\Request $request
281
     */
282
    protected $request;
283
284
    /**
285
     * Server class instance
286
     * @var \gplcart\core\helpers\Server $server
287
     */
288
    protected $server;
289
290
    /**
291
     * Response class instance
292
     * @var \gplcart\core\helpers\Response $response
293
     */
294
    protected $response;
295
296
    /**
297
     * Route class instance
298
     * @var \gplcart\core\Route $route
299
     */
300
    protected $route;
301
302
    /**
303
     * Session class instance
304
     * @var \gplcart\core\helpers\Session $session
305
     */
306
    protected $session;
307
308
    /**
309
     * Hook class instance
310
     * @var \gplcart\core\Hook $hook
311
     */
312
    protected $hook;
313
314
    /**
315
     * Filter model instance
316
     * @var \gplcart\core\models\Filter $filter
317
     */
318
    protected $filter;
319
320
    /**
321
     * Pager class instance
322
     * @var \gplcart\core\helpers\Pager $pager
323
     */
324
    protected $pager;
325
326
    /**
327
     * Config class instance
328
     * @var \gplcart\core\Config $config
329
     */
330
    protected $config;
331
332
    /**
333
     * Module class instance
334
     * @var \gplcart\core\Module $module
335
     */
336
    protected $module;
337
338
    /**
339
     * Constructor
340
     */
341
    public function __construct()
342
    {
343
        $this->setInstanceProperties();
344
        $this->setToken();
345
        $this->setRouteProperties();
346
        $this->setLanguageProperties();
347
        $this->setUserProperties();
348
        $this->setStoreProperties();
349
        $this->setDefaultAssets();
350
        $this->setThemeProperties();
351
        $this->setDefaultData();
352
        $this->controlCommonAccess();
353
        $this->controlMaintenanceMode();
354
355
        $this->hook->attach('construct.controller', $this);
356
    }
357
358
    /**
359
     * Destructor
360
     */
361
    public function __destruct()
362
    {
363
        $this->hook->attach('destruct.controller', $this);
364
    }
365
366
    /**
367
     * Returns a property value
368
     * @param string $name
369
     * @return mixed
370
     * @throws InvalidArgumentException
371
     */
372
    public function getProperty($name)
373
    {
374
        if (property_exists($this, $name)) {
375
            return $this->{$name};
376
        }
377
378
        throw new InvalidArgumentException("Property $name does not exist");
379
    }
380
381
    /**
382
     * Set a property
383
     * @param string $property
384
     * @param object $value
385
     */
386
    public function setProperty($property, $value)
387
    {
388
        $this->{$property} = $value;
389
    }
390
391
    /**
392
     * Sets a token
393
     * @param string|null $token
394
     * @return string
395
     */
396
    public function setToken($token = null)
397
    {
398
        if (isset($token)) {
399
            return $this->token = $token;
400
        }
401
402
        return $this->token = gplcart_string_encode(crypt(session_id(), $this->config->getKey()));
403
    }
404
405
    /**
406
     * Returns an object instance
407
     * @param string $class
408
     * @return object
409
     */
410
    public function getInstance($class)
411
    {
412
        return Container::get($class);
413
    }
414
415
    /**
416
     * Sets instance properties
417
     */
418
    protected function setInstanceProperties()
419
    {
420
        $this->hook = $this->getInstance('gplcart\\core\\Hook');
421
        $this->route = $this->getInstance('gplcart\\core\\Route');
422
        $this->module = $this->getInstance('gplcart\\core\\Module');
423
        $this->config = $this->getInstance('gplcart\\core\\Config');
424
        $this->library = $this->getInstance('gplcart\\core\\Library');
425
426
        $this->cart = $this->getInstance('gplcart\\core\\models\\Cart');
427
        $this->user = $this->getInstance('gplcart\\core\\models\\User');
428
        $this->store = $this->getInstance('gplcart\\core\\models\\Store');
429
        $this->image = $this->getInstance('gplcart\\core\\models\\Image');
430
        $this->filter = $this->getInstance('gplcart\\core\\models\\Filter');
431
        $this->language = $this->getInstance('gplcart\\core\\models\\Language');
432
        $this->validator = $this->getInstance('gplcart\\core\\models\\Validator');
433
        $this->translation = $this->getInstance('gplcart\\core\\models\\Translation');
434
435
        $this->url = $this->getInstance('gplcart\\core\\helpers\\Url');
436
        $this->asset = $this->getInstance('gplcart\\core\\helpers\\Asset');
437
        $this->pager = $this->getInstance('gplcart\\core\\helpers\\Pager');
438
        $this->server = $this->getInstance('gplcart\\core\\helpers\\Server');
439
        $this->session = $this->getInstance('gplcart\\core\\helpers\\Session');
440
        $this->request = $this->getInstance('gplcart\\core\\helpers\\Request');
441
        $this->response = $this->getInstance('gplcart\\core\\helpers\\Response');
442
    }
443
444
    /**
445
     * Sets the current route data
446
     */
447
    protected function setRouteProperties()
448
    {
449
        $this->current_route = $this->route->get();
450
451
        $this->path = $this->url->path();
452
        $this->is_backend = $this->url->isBackend();
453
        $this->is_install = $this->url->isInstall();
454
455
        $this->host = $this->server->httpHost();
456
        $this->scheme = $this->server->httpScheme();
457
        $this->uri_path = $this->server->requestUri();
458
        $this->is_ajax = $this->server->isAjaxRequest();
459
        $this->uri = $this->scheme . $this->host . $this->uri_path;
460
461
        $this->base = $this->request->base();
462
        $this->query = (array) $this->request->get(null, array(), 'array');
463
    }
464
465
    /**
466
     * Sets the current language data
467
     */
468
    protected function setLanguageProperties()
469
    {
470
        if (!$this->isInternalRoute()) {
471
472
            $langcode = $this->route->getLangcode();
473
474
            if (!empty($langcode)) {
475
                $this->langcode = $langcode;
476
                $this->translation->set($langcode, $this->current_route['simple_pattern']);
477
                $this->current_language = $this->language->get($this->langcode);
478
            }
479
        }
480
    }
481
482
    /**
483
     * Returns the current route data
484
     * @return array
485
     */
486
    public function getRoute()
487
    {
488
        return $this->current_route;
489
    }
490
491
    /**
492
     * Sets user/access properties
493
     */
494
    protected function setUserProperties()
495
    {
496
        if (!$this->isInstall()) {
497
498
            $this->cart_uid = $this->cart->getUid();
499
            $this->uid = $this->user->getId();
500
501
            if (!empty($this->uid)) {
502
                $this->current_user = $this->user->get($this->uid);
503
            }
504
        }
505
506
        if (!empty($this->current_user['timezone'])) {
507
            date_default_timezone_set($this->current_user['timezone']);
508
        }
509
    }
510
511
    /**
512
     * Sets the current store data
513
     */
514
    protected function setStoreProperties()
515
    {
516
        $this->current_store = $this->store->get();
517
518
        if (isset($this->current_store['store_id'])) {
519
            $this->store_id = $this->current_store['store_id'];
520
        }
521
    }
522
523
    /**
524
     * Sets global system asset files
525
     */
526
    protected function setDefaultAssets()
527
    {
528
        if (!$this->isInternalRoute()) {
529
            $this->addAssetLibrary('jquery');
530
            $this->setJs('files/assets/system/js/common.js');
531
            $this->setCss('files/assets/system/css/common.css');
532
        }
533
    }
534
535
    /**
536
     * Whether the user has a given access
537
     * @param string $permission
538
     * @return boolean
539
     */
540
    public function access($permission)
541
    {
542
        return $this->user->access($permission);
543
    }
544
545
    /**
546
     * Returns a formatted URL
547
     * @param string $path
548
     * @param array $query
549
     * @param boolean $absolute
550
     * @param boolean $exclude_lang
551
     * @return string
552
     */
553
    public function url($path = '', array $query = array(), $absolute = false, $exclude_lang = false)
554
    {
555
        return $this->url->get($path, $query, $absolute, $exclude_lang);
556
    }
557
558
    /**
559
     * Returns a formatted URL with a language code
560
     * @param string $langcode
561
     * @param string $path
562
     * @param array $query
563
     * @return string
564
     */
565
    public function lurl($langcode, $path = '', array $query = array())
566
    {
567
        if ($langcode === $this->language->getDefault()) {
568
            $langcode = '';
569
        }
570
571
        return $this->url->language($langcode, $path, $query);
572
    }
573
574
    /**
575
     * Returns an image URL
576
     * @param string $path
577
     * @param integer|null $imagestyle_id
578
     * @param boolean $absolute
579
     * @return string
580
     */
581
    public function image($path, $imagestyle_id = null, $absolute = false)
582
    {
583
        return $this->image->getUrl($path, $imagestyle_id, $absolute);
584
    }
585
586
    /**
587
     * Download a file
588
     * @param string $file
589
     * @param string $filename
590
     * @param array $options
591
     */
592
    public function download($file, $filename = '', $options = array())
593
    {
594
        $this->response->download($file, $filename, $options);
595
    }
596
597
    /**
598
     * Translates a text
599
     * @param string $string
600
     * @param array $arguments
601
     * @return string
602
     */
603
    public function text($string, array $arguments = array())
604
    {
605
        return $this->translation->text($string, $arguments);
606
    }
607
608
    /**
609
     * Prints and/or returns JS array with translations
610
     * @param string|array $strings
611
     * @param bool $print
612
     * @return string
613
     */
614
    public function jstext($strings, $print = true)
615
    {
616
        $code = '';
617
618
        foreach ((array) $strings as $string) {
619
            $key = gplcart_json_encode($string);
620
            $text = $this->translation->text($string);
621
            $translation = gplcart_json_encode(array($text));
622
            $code .= "Gplcart.translations[$key]=$translation;\n";
623
        }
624
625
        if ($print && $code) {
626
            echo "<script>$code</script>";
627
        }
628
629
        return $code;
630
    }
631
632
    /**
633
     * Returns a value if an error occurred
634
     * @param string|array $key
635
     * @param mixed $return_error
636
     * @param mixed $return_no_error
637
     * @return mixed
638
     */
639
    public function error($key = null, $return_error = null, $return_no_error = '')
640
    {
641
        if (isset($key)) {
642
            $result = gplcart_array_get($this->errors, $key);
643
        } else {
644
            $result = empty($this->errors) ? null : $this->errors;
645
        }
646
647
        if (isset($result)) {
648
            return isset($return_error) ? $return_error : $result;
649
        }
650
651
        return $return_no_error;
652
    }
653
654
    /**
655
     * Returns a data of the current store
656
     * @param mixed $item
657
     * @return mixed
658
     */
659
    public function getStore($item = null)
660
    {
661
        if (isset($item)) {
662
            return gplcart_array_get($this->current_store, $item);
663
        }
664
665
        return $this->current_store;
666
    }
667
668
    /**
669
     * Returns the current store ID
670
     * @return int
671
     */
672
    public function getStoreId()
673
    {
674
        return $this->store_id;
675
    }
676
677
    /**
678
     * Returns the current cart user ID
679
     * @return int|string
680
     */
681
    public function getCartUid()
682
    {
683
        return $this->cart_uid;
684
    }
685
686
    /**
687
     * Returns base path
688
     * @return string
689
     */
690
    public function getBase()
691
    {
692
        return $this->base;
693
    }
694
695
    /**
696
     * Returns the request IP
697
     * @return string
698
     */
699
    public function getIp()
700
    {
701
        return $this->server->remoteAddr();
702
    }
703
704
    /**
705
     * Returns a data of the current user
706
     * @param mixed $item
707
     * @return mixed
708
     */
709
    public function getUser($item = null)
710
    {
711
        if (isset($item)) {
712
            return gplcart_array_get($this->current_user, $item);
713
        }
714
715
        return $this->current_user;
716
    }
717
718
    /**
719
     * Returns an array of CSS styles
720
     * @return array
721
     */
722
    public function getCss()
723
    {
724
        return $this->asset->get('css', 'top');
725
    }
726
727
    /**
728
     * Returns an array of JS scripts
729
     * @param string $position
730
     * @return array
731
     */
732
    public function getJs($position)
733
    {
734
        $js = $this->asset->get('js', $position);
735
736
        if (isset($js['js_settings']['asset'])) {
737
            $json = gplcart_json_encode($js['js_settings']['asset']);
738
            $js['js_settings']['asset'] = "Gplcart.settings=$json;";
739
        }
740
741
        return $js;
742
    }
743
744
    /**
745
     * Formats a local time/date
746
     * @param null|integer $timestamp
747
     * @param bool $full
748
     * @return string
749
     */
750
    public function date($timestamp = null, $full = true)
751
    {
752
        if (!isset($timestamp)) {
753
            $timestamp = GC_TIME;
754
        }
755
756
        if ($full) {
757
            $format = $this->config->get('date_full_format', 'd.m.Y H:i');
758
        } else {
759
            $format = $this->config->get('date_short_format', 'd.m.y');
760
        }
761
762
        return date($format, $timestamp);
763
    }
764
765
    /**
766
     * Return a formatted string
767
     * @param string|array $format
768
     * @param array $arguments
769
     * @param string $glue
770
     * @return string
771
     */
772
    public function format($format, array $arguments = array(), $glue = '<br>')
773
    {
774
        if (is_array($format)) {
775
            $format = implode($glue, gplcart_array_flatten($format));
776
        }
777
778
        return vsprintf($format, $arguments);
779
    }
780
781
    /**
782
     * Converts special characters to HTML entities
783
     * @param string $string
784
     * @return string
785
     */
786
    public function e($string)
787
    {
788
        return htmlspecialchars($string, ENT_QUOTES, 'UTF-8');
789
    }
790
791
    /**
792
     * Returns JSON escaped string
793
     * @param mixed $data
794
     * @return string
795
     */
796
    public function json($data)
797
    {
798
        return htmlspecialchars(gplcart_json_encode($data), ENT_QUOTES, 'UTF-8');
799
    }
800
801
    /**
802
     * Returns a truncated string
803
     * @param string $string
804
     * @param integer $length
805
     * @param string $trimmarker
806
     * @return string
807
     */
808
    public function truncate($string, $length = 100, $trimmarker = '...')
809
    {
810
        return mb_strimwidth($string, 0, $length, $trimmarker, 'UTF-8');
811
    }
812
813
    /**
814
     * Lower a string
815
     * @param string $string
816
     * @return string
817
     */
818
    public function lower($string)
819
    {
820
        return mb_strtolower($string, 'UTF-8');
821
    }
822
823
    /**
824
     * Returns a config item
825
     * @param string|null $key
826
     * @param mixed $default
827
     * @return mixed
828
     */
829
    public function config($key = null, $default = null)
830
    {
831
        return $this->config->get($key, $default);
832
    }
833
834
    /**
835
     * Returns a setting from the current theme settings
836
     * @param mixed $key
837
     * @param mixed $default
838
     * @return mixed
839
     */
840
    public function configTheme($key = null, $default = null)
841
    {
842
        if (!isset($key)) {
843
            return $this->theme_settings;
844
        }
845
846
        $value = gplcart_array_get($this->theme_settings, $key);
847
        return isset($value) ? $value : $default;
848
    }
849
850
    /**
851
     * Clean up HTML string using defined HTML filters
852
     * @param string $string
853
     * @param mixed $filter
854
     * @return string
855
     */
856
    public function filter($string, $filter = null)
857
    {
858
        if (!isset($filter)) {
859
            $filter = $this->current_filter;
860
        }
861
862
        $delimiter = $this->config('teaser_delimiter', '<!--teaser-->');
863
        $string = str_replace($delimiter, htmlspecialchars($delimiter), $string);
864
        return $this->filter->run($string, $filter);
865
    }
866
867
    /**
868
     * Explodes a text by teaser and full text
869
     * @param string $text
870
     * @return array
871
     */
872
    protected function explodeText($text)
873
    {
874
        $delimiter = $this->config('teaser_delimiter', '<!--teaser-->');
875
        $text = str_replace(htmlspecialchars($delimiter), $delimiter, $text);
876
        $parts = array_filter(array_map('trim', explode($delimiter, $text, 2)));
877
878
        return array_pad($parts, 2, '');
879
    }
880
881
    /**
882
     * Formats tag attributes
883
     * @param array $attributes
884
     * @return string
885
     */
886
    public function attributes(array $attributes)
887
    {
888
        foreach ($attributes as $attribute => &$data) {
889
            $data = implode(' ', (array) $data);
890
            $data = $attribute . '="' . htmlspecialchars($data, ENT_QUOTES, 'UTF-8') . '"';
891
        }
892
893
        return empty($attributes) ? '' : ' ' . implode(' ', $attributes);
894
    }
895
896
    /**
897
     * Returns a string from a text before the teaser delimiter
898
     * @param string $text
899
     * @param boolean $enable_filter
900
     * @param mixed $filter_id
901
     * @return string
902
     */
903
    public function teaser($text, $enable_filter = true, $filter_id = null)
904
    {
905
        $summary = '';
906
907
        if ($text !== '') {
908
            $parts = $this->explodeText($text);
909
            $summary = reset($parts);
910
        }
911
912
        if ($summary !== '' && $enable_filter) {
913
            $summary = $this->filter($summary, $filter_id);
914
        }
915
916
        return $summary;
917
    }
918
919
    /**
920
     * If $path is set - returns TRUE if the path pattern mathes the current URL path
921
     * If $path is not set or NULL - returns the current URL path
922
     * @param null|string $pattern
923
     * @return string|bool
924
     */
925
    public function path($pattern = null)
926
    {
927
        if (isset($pattern)) {
928
            return preg_match("~$pattern~i", $this->path) === 1;
929
        }
930
931
        return $this->path;
932
    }
933
934
    /**
935
     * Whether the current URL is an installing area
936
     * @return bool
937
     */
938
    public function isInstall()
939
    {
940
        return $this->is_install;
941
    }
942
943
    /**
944
     * Whether the current URL is an admin area
945
     * @return bool
946
     */
947
    public function isBackend()
948
    {
949
        return $this->is_backend;
950
    }
951
952
    /**
953
     * Whether the site in maintenance mode
954
     * @return boolean
955
     */
956
    public function isMaintenance()
957
    {
958
        return $this->is_maintenance;
959
    }
960
961
    /**
962
     * Whether the route is internal
963
     * @return bool
964
     */
965
    public function isInternalRoute()
966
    {
967
        return !empty($this->current_route['internal']);
968
    }
969
970
    /**
971
     * Whether the current user is logged in
972
     * @return bool
973
     */
974
    public function isLoggedIn()
975
    {
976
        return !empty($this->uid);
977
    }
978
979
    /**
980
     * Renders a template
981
     * @param string $file
982
     * @param array $data
983
     * @param boolean $merge
984
     * @param string $default
985
     * @return string
986
     */
987
    public function render($file, $data = array(), $merge = true, $default = '')
988
    {
989
        settype($data, 'array');
990
        $templates = $this->getTemplateFiles($file);
991
992
        if ($merge) {
993
            $data = array_merge($data, $this->getDefaultData());
994
        }
995
996
        $rendered = null;
997
        $this->hook->attach('template.render', $templates, $data, $rendered, $this);
998
999
        if (isset($rendered)) {
1000
            return trim($rendered);
1001
        }
1002
1003
        list($original, $overridden) = $templates;
1004
1005
        if (is_file("$overridden.php")) {
1006
            $template = "$overridden.php";
1007
        } else if (is_file("$original.php")) {
1008
            $template = "$original.php";
1009
        } else {
1010
            return $default;
1011
        }
1012
1013
        $rendered = $this->renderPhpTemplate($template, $data);
1014
        return trim($rendered);
1015
    }
1016
1017
    /**
1018
     * Returns an array of full template paths without file extension
1019
     * @param string $file
1020
     * @return array
1021
     */
1022
    protected function getTemplateFiles($file)
1023
    {
1024
        $module_id = $this->theme;
1025
1026
        if (strpos($file, '|') !== false) {
1027
            list($module_id, $file) = explode('|', $file, 2);
1028
        } else if (gplcart_path_is_absolute($file)) {
1029
            $template = substr($file, 0, (strrpos($file, '.')));
1030
            return array($template, $template);
1031
        }
1032
1033
        return array(
1034
            GC_DIR_MODULE . "/$module_id/templates/$file",
1035
            GC_DIR_MODULE . "/{$this->theme}/override/templates/$module_id/$file"
1036
        );
1037
    }
1038
1039
    /**
1040
     * Whether the user is super admin
1041
     * @param null|integer $user_id
1042
     * @return boolean
1043
     */
1044
    public function isSuperadmin($user_id = null)
1045
    {
1046
        return $this->user->isSuperadmin($user_id);
1047
    }
1048
1049
    /**
1050
     * Sets the current HTTP status code
1051
     * @param string $code
1052
     */
1053
    public function setHttpStatus($code)
1054
    {
1055
        $this->http_status = $code;
1056
    }
1057
1058
    /**
1059
     * Whether the key exists in POST query or current query is POST type
1060
     * @param string|null $key
1061
     * @return boolean
1062
     */
1063
    public function isPosted($key = null)
1064
    {
1065
        if (isset($key)) {
1066
            $value = $this->request->post($key, null);
1067
            return isset($value);
1068
        }
1069
1070
        return $this->server->requestMethod() === 'POST';
1071
    }
1072
1073
    /**
1074
     * Returns a data from POST query
1075
     * @param string $name
1076
     * @param mixed $default
1077
     * @param bool|string $filter
1078
     * @param string $type
1079
     * @return mixed
1080
     */
1081
    public function getPosted($name, $default = null, $filter = true, $type = 'string')
1082
    {
1083
        return $this->request->post($name, $default, $filter, $type);
1084
    }
1085
1086
    /**
1087
     * Whether a key is presented in the GET query
1088
     * @param string|null $key
1089
     * @return boolean
1090
     */
1091
    public function isQuery($key = null)
1092
    {
1093
        $value = $this->request->get($key);
1094
1095
        return !empty($value);
1096
    }
1097
1098
    /**
1099
     * Returns a GET query
1100
     * @param string|null $key
1101
     * @param mixed $default
1102
     * @param string|null $type
1103
     * @return mixed
1104
     */
1105
    public function getQuery($key = null, $default = null, $type = 'string')
1106
    {
1107
        return $this->request->get($key, $default, $type);
1108
    }
1109
1110
    /**
1111
     * Whether a key is presented in the submitted values array
1112
     * @param string|array $key
1113
     * @return boolean
1114
     */
1115
    public function isSubmitted($key)
1116
    {
1117
        $result = $this->getSubmitted($key);
1118
        return isset($result);
1119
    }
1120
1121
    /**
1122
     * Whether an error(s) exist
1123
     * @param string|array|null $key
1124
     * @return boolean
1125
     */
1126
    public function isError($key = null)
1127
    {
1128
        return $this->error($key, true, false);
1129
    }
1130
1131
    /**
1132
     * Sets theme properties
1133
     */
1134
    protected function setThemeProperties()
1135
    {
1136
        $this->theme_frontend = $this->config('theme', 'frontend');
1137
        $this->theme_backend = $this->config('theme_backend', 'backend');
1138
1139
        if ($this->is_backend) {
1140
            $this->theme = $this->theme_backend;
1141
        } elseif ($this->is_install) {
1142
            $this->theme = $this->theme_frontend;
1143
        } elseif (!empty($this->current_store)) {
1144
            $this->theme_frontend = $this->store->getConfig('theme');
1145
            $this->theme = $this->theme_frontend;
1146
        }
1147
1148
        $this->hook->attach('theme', $this);
1149
1150
        if (empty($this->theme)) {
1151
            $this->response->outputError404();
1152
        }
1153
1154
        $this->current_theme = $this->module->getInfo($this->theme);
1155
1156
        if (empty($this->current_theme)) {
1157
            $this->response->outputError404();
1158
        }
1159
1160
        $this->theme_settings = (array) $this->getModuleSettings($this->theme, null, array());
1161
        $this->templates = $this->getDefaultTemplates();
1162
1163
        if (!empty($this->current_theme['data']['templates'])) {
1164
            $this->templates = array_merge($this->templates, $this->current_theme['data']['templates']);
1165
        }
1166
    }
1167
1168
    /**
1169
     * Returns all or a single module setting
1170
     * @param string $module_id
1171
     * @param null|string $key
1172
     * @param mixed $default
1173
     * @return mixed
1174
     */
1175
    public function getModuleSettings($module_id, $key = null, $default = null)
1176
    {
1177
        return $this->module->getSettings($module_id, $key, $default);
1178
    }
1179
1180
    /**
1181
     * Set the current theme
1182
     * @param string $name
1183
     */
1184
    public function setCurrentTheme($name)
1185
    {
1186
        $this->theme = $name;
1187
    }
1188
1189
    /**
1190
     * Returns the current theme module ID
1191
     * @return string
1192
     */
1193
    public function getCurrentTheme()
1194
    {
1195
        return $this->theme;
1196
    }
1197
1198
    /**
1199
     * Whether a theme ID matches the current theme ID
1200
     * @param string $name
1201
     * @return boolean
1202
     */
1203
    public function isCurrentTheme($name)
1204
    {
1205
        return $this->theme === $name;
1206
    }
1207
1208
    /**
1209
     * Whether the current request is AJAX
1210
     * @return boolean
1211
     */
1212
    public function isAjax()
1213
    {
1214
        return $this->is_ajax;
1215
    }
1216
1217
    /**
1218
     * Sets a template variable
1219
     * @param string|array $key
1220
     * @param mixed $value
1221
     * @return array
1222
     */
1223
    public function setData($key, $value)
1224
    {
1225
        gplcart_array_set($this->data, $key, $value);
1226
1227
        return $this->data;
1228
    }
1229
1230
    /**
1231
     * Removes a value by a key from an array of template data
1232
     * @param string|array $key
1233
     * @return array
1234
     */
1235
    public function unsetData($key)
1236
    {
1237
        gplcart_array_unset($this->data, $key);
1238
1239
        return $this->data;
1240
    }
1241
1242
    /**
1243
     * Sets an error
1244
     * @param null|string|array $key
1245
     * @param mixed $value
1246
     * @return array
1247
     */
1248
    public function setError($key, $value)
1249
    {
1250
        if (isset($key)) {
1251
            gplcart_array_set($this->errors, $key, $value);
1252
        } else {
1253
            $this->errors = (array) $value;
1254
        }
1255
1256
        return $this->errors;
1257
    }
1258
1259
    /**
1260
     * Removes an error by a key from an array of errors
1261
     * @param string|array $key
1262
     * @return array
1263
     */
1264
    public function unsetError($key)
1265
    {
1266
        gplcart_array_unset($this->errors, $key);
1267
        return $this->errors;
1268
    }
1269
1270
    /**
1271
     * Sets an array of submitted data
1272
     * @param string|array|null $key
1273
     * @param mixed $value
1274
     * @param boolean|string $filter
1275
     * @return array
1276
     */
1277
    public function setSubmitted($key = null, $value = null, $filter = true)
1278
    {
1279
        if (!isset($key)) {
1280
1281
            if (isset($value)) {
1282
                return $this->submitted = (array) $value;
1283
            }
1284
1285
            $this->submitted = (array) $this->request->post(null, array(), $filter, 'array');
1286
            return $this->submitted;
1287
        }
1288
1289
        if (!isset($value) && empty($this->submitted)) {
1290
            $this->post_key = (string) $key;
1291
            $this->submitted = (array) $this->request->post($key, array(), $filter, 'array');
1292
            return $this->submitted;
1293
        }
1294
1295
        gplcart_array_set($this->submitted, $key, $value);
1296
        return $this->submitted;
1297
    }
1298
1299
    /**
1300
     * Removes a value(s) from an array of submitted data
1301
     * @param string|array $key
1302
     * @return array
1303
     */
1304
    public function unsetSubmitted($key)
1305
    {
1306
        gplcart_array_unset($this->submitted, $key);
1307
        return $this->submitted;
1308
    }
1309
1310
    /**
1311
     * Limit an array of submitted data to the allowed keys
1312
     * @param array $allowed
1313
     * @return array
1314
     */
1315
    public function filterSubmitted(array $allowed)
1316
    {
1317
        $this->submitted = array_intersect_key($this->submitted, array_flip($allowed));
1318
        return $this->submitted;
1319
    }
1320
1321
    /**
1322
     * Converts a submitted value to boolean
1323
     * @param string|array $key
1324
     * @return boolean
1325
     */
1326
    public function setSubmittedBool($key)
1327
    {
1328
        $bool = (bool) $this->getSubmitted($key);
1329
        $this->setSubmitted($key, $bool);
1330
        return $bool;
1331
    }
1332
1333
    /**
1334
     * Converts a submitted value to array using multiline delimiter
1335
     * @param string|array $key
1336
     * @return array
1337
     */
1338
    public function setSubmittedArray($key)
1339
    {
1340
        $value = $this->getSubmitted($key);
1341
1342
        if (isset($value) && is_string($value)) {
1343
            $array = gplcart_string_explode_multiline($value);
1344
            $this->setSubmitted($key, $array);
1345
            return $array;
1346
        }
1347
1348
        return array();
1349
    }
1350
1351
    /**
1352
     * Returns a submitted value
1353
     * @param string|array $key
1354
     * @param mixed $default
1355
     * @return mixed
1356
     */
1357
    public function getSubmitted($key = null, $default = null)
1358
    {
1359
        if (isset($key)) {
1360
            $result = gplcart_array_get($this->submitted, $key);
1361
            return isset($result) ? $result : $default;
1362
        }
1363
1364
        return $this->submitted;
1365
    }
1366
1367
    /**
1368
     * Returns a value from an array of template variables
1369
     * @param string|array|null $key
1370
     * @param mixed
1371
     * @return mixed
1372
     */
1373
    public function getData($key = null, $default = null)
1374
    {
1375
        if (!isset($key)) {
1376
            return $this->data;
1377
        }
1378
1379
        $result = gplcart_array_get($this->data, $key);
1380
        return isset($result) ? $result : $default;
1381
    }
1382
1383
    /**
1384
     * Controls user access to the current page
1385
     */
1386
    protected function controlCommonAccess()
1387
    {
1388
        if (!$this->isInstall()) {
1389
1390
            if (!empty($this->uid)) {
1391
                $this->controlAccessCredentials();
1392
            }
1393
1394
            $this->controlCsrf();
1395
            $this->controlAccessUpload();
1396
            $this->controlAccessRestrictedArea();
1397
            $this->controlAccessAdmin();
1398
            $this->controlAccessAccount();
1399
        }
1400
    }
1401
1402
    /**
1403
     * Output status page if status is set, e.g other than 200
1404
     */
1405
    protected function controlHttpStatus()
1406
    {
1407
        if (isset($this->http_status)) {
1408
            $this->outputHttpStatus($this->http_status);
1409
        }
1410
    }
1411
1412
    /**
1413
     * "Honey pot" submission protection
1414
     */
1415
    public function controlSpam()
1416
    {
1417
        if ($this->request->post('url', '', false, 'string') !== '') {
1418
            $this->response->outputError403(false);
1419
        }
1420
    }
1421
1422
    /**
1423
     * Controls the current user credentials, such as status, role, password hash...
1424
     */
1425
    protected function controlAccessCredentials()
1426
    {
1427
        if (empty($this->current_user['hash']) || empty($this->current_user['status'])) {
1428
            $this->session->delete();
1429
            $this->url->redirect('login');
1430
        }
1431
1432
        if (!gplcart_string_equals($this->current_user['hash'], $this->user->getSession('hash'))) {
1433
            $this->session->delete();
1434
            $this->url->redirect('login');
1435
        }
1436
1437
        if ($this->current_user['role_id'] != $this->user->getRoleId()) {
1438
            $this->session->delete();
1439
            $this->url->redirect('login');
1440
        }
1441
    }
1442
1443
    /**
1444
     * Controls access to upload a file
1445
     */
1446
    protected function controlAccessUpload()
1447
    {
1448
        if ($this->request->file() && !$this->access('file_upload')) {
1449
            $this->response->outputError403();
1450
        }
1451
    }
1452
1453
    /**
1454
     * Controls access to restricted areas
1455
     */
1456
    protected function controlAccessRestrictedArea()
1457
    {
1458
        if (($this->is_backend || $this->url->isAccount()) && empty($this->uid)) {
1459
            $this->url->redirect('login', array('target' => $this->path));
1460
        }
1461
    }
1462
1463
    /**
1464
     * Prevent Cross-Site Request Forgery (CSRF)
1465
     */
1466
    protected function controlCsrf()
1467
    {
1468
        if ($this->isPosted()
1469
            && !$this->isInternalRoute()
1470
            && (!isset($this->current_route['token']) || !empty($this->current_route['token']))) {
1471
1472
            $token = $this->request->post('token', '', false, 'string');
1473
1474
            if (!gplcart_string_equals($token, $this->token)) {
1475
                $this->response->outputError403();
1476
            }
1477
        }
1478
    }
1479
1480
    /**
1481
     * Controls token in the URL query
1482
     * @param null|string $key
1483
     */
1484
    protected function controlToken($key = null)
1485
    {
1486
        $control = isset($key) ? isset($this->query[$key]) : !empty($this->query);
1487
1488
        if ($control && (empty($this->query['token']) || !gplcart_string_equals($this->token, $this->query['token']))) {
1489
            $this->response->outputError403();
1490
        }
1491
    }
1492
1493
    /**
1494
     * Controls access to admin pages
1495
     */
1496
    protected function controlAccessAdmin()
1497
    {
1498
        if ($this->is_backend && !$this->isSuperadmin()) {
1499
1500
            if (empty($this->current_user['role_status']) || !$this->access('admin')) {
1501
                $this->redirect('/', $this->text('No access'), 'warning');
1502
            }
1503
1504
            if (isset($this->current_route['access']) && !$this->access($this->current_route['access'])) {
1505
                $this->setHttpStatus(403);
1506
            }
1507
        }
1508
    }
1509
1510
    /**
1511
     * Restrict access to only super-admin (UID 1)
1512
     */
1513
    protected function controlAccessSuperAdmin()
1514
    {
1515
        if (!$this->isSuperadmin()) {
1516
            $this->outputHttpStatus(403);
1517
        }
1518
    }
1519
1520
    /**
1521
     * Controls access to account pages
1522
     */
1523
    protected function controlAccessAccount()
1524
    {
1525
        $account_id = $this->url->getAccountId();
1526
1527
        if ($account_id === false || $this->uid === $account_id) {
1528
            return null;
1529
        }
1530
1531
        if ($this->isSuperadmin($account_id) && !$this->isSuperadmin()) {
1532
            $this->setHttpStatus(403);
1533
            return null;
1534
        }
1535
1536
        if (!$this->access('user')) {
1537
            $this->setHttpStatus(403);
1538
        }
1539
    }
1540
1541
    /**
1542
     * Switches the site to maintenance mode
1543
     */
1544
    protected function controlMaintenanceMode()
1545
    {
1546
        $allowed_path = $this->is_install
1547
            || $this->is_backend
1548
            || in_array($this->path, array('login', 'logout', 'forgot'));
1549
1550
        $this->is_maintenance = empty($this->current_store['status']) && !$allowed_path;
1551
1552
        if ($this->is_maintenance && !$this->access('maintenance')) {
1553
1554
            if (!$this->isFront()) {
1555
                $this->redirect('/');
1556
            }
1557
1558
            $this->outputMaintenance();
1559
        }
1560
    }
1561
1562
    /**
1563
     * Displays 403 access denied to unwanted users
1564
     * @param string $permission
1565
     */
1566
    public function controlAccess($permission)
1567
    {
1568
        if (!$this->access($permission)) {
1569
            $this->outputHttpStatus(403);
1570
        }
1571
    }
1572
1573
    /**
1574
     * Redirects to a new location
1575
     * @param string $url
1576
     * @param string $message
1577
     * @param string $severity
1578
     * @param boolean $exclude_lang
1579
     */
1580
    public function redirect($url = '', $message = '', $severity = 'info', $exclude_lang = false)
1581
    {
1582
        $this->setMessage($message, $severity, true);
1583
1584
        $parsed = parse_url($url);
1585
1586
        $query = array();
1587
        if (isset($parsed['query'])) {
1588
            parse_str($parsed['query'], $query);
1589
        }
1590
1591
        $full = strpos($url, $this->base) === 0;
1592
        $this->url->redirect($url, (array) $query, $full, $exclude_lang);
1593
    }
1594
1595
    /**
1596
     * Sets page <title> tag
1597
     * @param string $title
1598
     * @param boolean $both
1599
     */
1600
    public function setTitle($title, $both = true)
1601
    {
1602
        $this->data['_head_title'] = strip_tags($title);
1603
1604
        if ($both && !isset($this->data['_page_title'])) {
1605
            $this->setPageTitle($title);
1606
        }
1607
    }
1608
1609
    /**
1610
     * Sets page titles (H tag)
1611
     * @param string $title
1612
     */
1613
    public function setPageTitle($title)
1614
    {
1615
        $this->data['_page_title'] = $title;
1616
    }
1617
1618
    /**
1619
     * Extracts translatable strings from JS files and creates translation
1620
     * @param array $scripts
1621
     */
1622
    protected function setJsTranslation(array $scripts)
1623
    {
1624
        if (!empty($this->langcode) && !is_file($this->translation->getContextJsFile())) {
1625
            foreach ($scripts as $key => $script) {
1626
                if (strpos($key, 'system/modules/') === 0
1627
                    && strpos($key, '/vendor/') === false
1628
                    && !empty($script['file'])) {
1629
                    $string = file_get_contents($script['file']);
1630
                    $this->translation->createJsTranslation($string);
1631
                }
1632
            }
1633
        }
1634
    }
1635
1636
    /**
1637
     * Prepare output
1638
     * @param mixed $templates
1639
     * @param array $options
1640
     */
1641
    protected function prepareOutput(&$templates, array &$options)
1642
    {
1643
        if (!empty($this->http_status)) {
1644
            $title = (string) $this->response->getStatus($this->http_status);
1645
            $this->setTitle($title, false);
1646
            $templates = "common/status/{$this->http_status}";
1647
            $options['headers'] = $this->http_status;
1648
        }
1649
    }
1650
1651
    /**
1652
     * Prepare template data variables before output
1653
     */
1654
    protected function prepareDataOutput()
1655
    {
1656
        $this->data['_css'] = $this->getCss();
1657
1658
        foreach (array('top', 'bottom') as $position) {
1659
            $this->data["_js_$position"] = $this->getJs($position);
1660
            $this->setJsTranslation($this->data["_js_$position"]);
1661
        }
1662
1663
        $this->hook->attach('template.data', $this->data, $this);
1664
1665
        gplcart_array_sort($this->data['_css']);
1666
        gplcart_array_sort($this->data['_js_top']);
1667
        gplcart_array_sort($this->data['_js_bottom']);
1668
    }
1669
1670
    /**
1671
     * Renders all templates before sending them to a browser
1672
     * @param array|string $templates
1673
     * @return string
1674
     */
1675
    protected function renderOutput($templates)
1676
    {
1677
        $html = '';
1678
1679
        if (!$this->isInternalRoute()) {
1680
1681
            if (!is_array($templates)) {
1682
                $templates = array('region_content' => (string) $templates);
1683
            }
1684
1685
            $templates = array_merge($this->templates, $templates);
1686
1687
            $layout = $templates['layout'];
1688
            unset($templates['layout']);
1689
1690
            $body = $data = $this->data;
1691
1692
            foreach ($templates as $id => $template) {
1693
                if (strpos($id, 'region_') === 0) {
1694
                    $body[$id] = $this->renderRegion($id, $template);
1695
                }
1696
            }
1697
1698
            $data['_head'] = $data['_body'] = '';
1699
1700
            if (!empty($templates['head'])) {
1701
                $data['_head'] = $this->render($templates['head'], $this->data);
1702
            }
1703
1704
            if (!empty($templates['body'])) {
1705
                $data['_body'] = $this->render($templates['body'], $body);
1706
            }
1707
1708
            $html = $this->render($layout, $data, false);
1709
        }
1710
1711
        $this->hook->attach('template.output', $html, $this);
1712
        return $html;
1713
    }
1714
1715
    /**
1716
     * Renders a region
1717
     * @param string $region
1718
     * @param string $template
1719
     * @return string
1720
     */
1721
    protected function renderRegion($region, $template)
1722
    {
1723
        if (!isset($this->data[$region])) {
1724
            return $this->render($template, $this->data);
1725
        }
1726
1727
        $this->data[$region] = (array) $this->data[$region];
1728
        gplcart_array_sort($this->data[$region]);
1729
1730
        $items = array();
1731
1732
        foreach ($this->data[$region] as $item) {
1733
            $items[] = isset($item['rendered']) ? (string) $item['rendered'] : (string) $item;
1734
        }
1735
1736
        $this->data[$region] = $this->render($template, array($region => $items));
1737
        return $this->data[$region];
1738
    }
1739
1740
    /**
1741
     * Outputs rendered page
1742
     * @param null|array|string $templates
1743
     * @param array $options
1744
     */
1745
    final public function output($templates = null, array $options = array())
1746
    {
1747
        if (empty($templates)) {
1748
            $templates = $this->templates;
1749
        }
1750
1751
        $this->prepareDataOutput();
1752
        $this->prepareOutput($templates, $options);
1753
        $this->response->outputHtml($this->renderOutput($templates), $options);
1754
    }
1755
1756
    /**
1757
     * Output JSON string
1758
     * @param mixed $data
1759
     * @param array $options
1760
     */
1761
    public function outputJson($data, array $options = array())
1762
    {
1763
        $this->response->outputJson($data, $options);
1764
    }
1765
1766
    /**
1767
     * Displays an error page
1768
     * @param integer $code
1769
     */
1770
    final public function outputHttpStatus($code)
1771
    {
1772
        $this->setHttpStatus($code);
1773
        $this->output();
1774
    }
1775
1776
    /**
1777
     * Displays site maintenance page
1778
     */
1779
    public function outputMaintenance()
1780
    {
1781
        $this->setTitle('Site maintenance', false);
1782
        $this->output(array('body' => 'layout/maintenance'), array('headers' => 503));
1783
    }
1784
1785
    /**
1786
     * Adds an item to a region
1787
     * @param string $region
1788
     * @param string|array $item Expected array format:
1789
     * first item - template, second - variables for $this->render()
1790
     */
1791
    public function setRegion($region, $item)
1792
    {
1793
        if (is_array($item)) {
1794
            $template = array_shift($item);
1795
            $data = $item ? reset($item) : array();
1796
            $content = $this->render($template, $data);
1797
        } else {
1798
            $content = trim($item);
1799
        }
1800
1801
        if ($content !== '' && isset($this->templates["region_$region"])) {
1802
1803
            $weight = 1;
1804
1805
            if (isset($this->data["region_$region"])) {
1806
                $weight = count($this->data["region_$region"]) + 1;
1807
            }
1808
1809
            $this->data["region_$region"][] = array('rendered' => $content, 'weight' => $weight);
1810
        }
1811
    }
1812
1813
    /**
1814
     * Returns an array of default templates
1815
     * @return array
1816
     */
1817
    protected function getDefaultTemplates()
1818
    {
1819
        return array(
1820
            'head' => 'layout/head',
1821
            'body' => 'layout/body',
1822
            'layout' => 'layout/layout',
1823
            'region_bottom' => 'layout/region_bottom',
1824
            'region_content' => 'layout/region_content'
1825
        );
1826
    }
1827
1828
    /**
1829
     * Renders PHP templates
1830
     * @param string $template
1831
     * @param array $data
1832
     * @return string
1833
     */
1834
    public function renderPhpTemplate($template, array $data)
1835
    {
1836
        extract($data, EXTR_SKIP);
1837
1838
        unset($data); // Kill duplicates
1839
1840
        ob_start();
1841
        include $template;
1842
        return ob_get_clean();
1843
    }
1844
1845
    /**
1846
     * Adds default JS
1847
     */
1848
    protected function setDefaultJs()
1849
    {
1850
        foreach ($this->getDefaultData() as $key => $value) {
1851
            $this->setJsSettings(ltrim($key, '_'), $value, 60);
1852
        }
1853
1854
        $json = gplcart_json_encode($this->translation->loadJsTranslation());
1855
        $this->setJs("Gplcart.translations=$json;");
1856
    }
1857
1858
    /**
1859
     * Adds JSON string with JS settings
1860
     * @param string $key
1861
     * @param mixed $data
1862
     * @param integer|null $weight
1863
     */
1864
    public function setJsSettings($key, $data, $weight = null)
1865
    {
1866
        $asset = array(
1867
            'type' => 'js',
1868
            'weight' => $weight,
1869
            'key' => 'js_settings',
1870
            'merge' => 'js_settings',
1871
            'asset' => array($key => $data)
1872
        );
1873
1874
        $this->asset->set($asset);
1875
    }
1876
1877
    /**
1878
     * Returns global template variables
1879
     * @return array
1880
     */
1881
    protected function getDefaultData()
1882
    {
1883
        $data = array();
1884
1885
        $data['_uid'] = $this->uid;
1886
        $data['_uri'] = $this->uri;
1887
        $data['_path'] = $this->path;
1888
        $data['_base'] = $this->base;
1889
        $data['_host'] = $this->host;
1890
        $data['_token'] = $this->token;
1891
        $data['_query'] = $this->query;
1892
        $data['_scheme'] = $this->scheme;
1893
        $data['_cart_uid'] = $this->cart_uid;
1894
        $data['_is_front'] = $this->isFront();
1895
        $data['_is_logged_in'] = !empty($this->uid);
1896
        $data['_is_admin'] = $this->access('admin');
1897
        $data['_is_superadmin'] = $this->isSuperadmin();
1898
        $data['_langcode'] = empty($this->langcode) ? 'en' : $this->langcode;
1899
        $data['_url'] = $this->scheme . $this->host . $this->base . $this->path;
1900
1901
        return $data;
1902
    }
1903
1904
    /**
1905
     * Whether the current path is home page
1906
     * @return bool
1907
     */
1908
    public function isFront()
1909
    {
1910
        return $this->url->isFront();
1911
    }
1912
1913
    /**
1914
     * Sets default template variables
1915
     */
1916
    protected function setDefaultData()
1917
    {
1918
        $this->data = array_merge($this->data, $this->getDefaultData());
1919
1920
        $this->data['_version'] = gplcart_version();
1921
        $this->data['_user'] = $this->current_user;
1922
        $this->data['_store'] = $this->current_store;
1923
        $this->data['_language'] = $this->current_language;
1924
        $this->data['_messages'] = $this->session->getMessage();
1925
1926
        foreach($this->language->getList() as $code => $language){
1927
1928
            if(!empty($language['status'])){
1929
                $this->data['_languages'][$code] = $language;
1930
            }
1931
1932
            if(!empty($language['status']) || $code === 'en'){
1933
                $this->data['_language_switcher'][$code] = $language;
1934
            }
1935
        }
1936
1937
        $this->data['_store_title'] = $this->store->getTranslation('title', $this->langcode);
1938
1939
        if (!empty($this->current_store['data']['logo'])) {
1940
            $this->data['_store_logo'] = $this->image($this->current_store['data']['logo']);
1941
        }
1942
1943
        if (!empty($this->current_store['data']['favicon'])) {
1944
            $this->data['_store_favicon'] = $this->image($this->current_store['data']['favicon']);
1945
        }
1946
1947
        $this->setClasses();
1948
        $this->setDefaultJs();
1949
    }
1950
1951
    /**
1952
     * Sets an array of body CSS classes
1953
     */
1954
    protected function setClasses()
1955
    {
1956
        $classes = array();
1957
1958
        if (isset($this->current_route['pattern'])) {
1959
            $pattern = $this->current_route['pattern'] ? $this->current_route['pattern'] : 'front';
1960
            foreach (explode('/', $pattern) as $part) {
1961
                if (ctype_alpha($part)) {
1962
                    $classes[] = "gc-$part";
1963
                }
1964
            }
1965
        }
1966
1967
        $this->data['_classes'] = $classes;
1968
    }
1969
1970
    /**
1971
     * Adds a JS on the page
1972
     * @param string $script
1973
     * @param array $options
1974
     * @return bool|array
1975
     */
1976
    public function setJs($script, array $options = array())
1977
    {
1978
        $options['type'] = 'js';
1979
        $options['asset'] = $script;
1980
1981
        return $this->asset->set($options);
1982
    }
1983
1984
    /**
1985
     * Adds a CSS on the page
1986
     * @param string $css
1987
     * @param array $options
1988
     * @return bool|array
1989
     */
1990
    public function setCss($css, array $options = array())
1991
    {
1992
        $options['asset'] = $css;
1993
        $options['type'] = 'css';
1994
1995
        return $this->asset->set($options);
1996
    }
1997
1998
    /**
1999
     * Adds single or multiple asset libraries
2000
     * @param string|array $library_id
2001
     * @param array $options
2002
     * @return array
2003
     */
2004
    public function addAssetLibrary($library_id, array $options = array())
2005
    {
2006
        $added = array();
2007
2008
        foreach ($this->library->getFiles($library_id) as $file) {
2009
2010
            $extension = pathinfo($file, PATHINFO_EXTENSION);
2011
2012
            if ($extension === 'js') {
2013
                $result = $this->setJs($file, $options);
2014
            } else if ($extension === 'css') {
2015
                $result = $this->setCss($file, $options);
2016
            }
2017
2018
            if (!empty($result)) {
2019
                $added[] = $file;
2020
            }
2021
        }
2022
2023
        return $added;
2024
    }
2025
2026
    /**
2027
     * Sets a meta tag on the page
2028
     * @param array $content
2029
     */
2030
    public function setMeta($content)
2031
    {
2032
        $key = '_meta_tags';
2033
2034
        if (!isset($this->data[$key])) {
2035
            $this->data[$key] = array();
2036
        }
2037
2038
        $this->data[$key][] = $content;
2039
    }
2040
2041
    /**
2042
     * Sets a single page breadcrumb
2043
     * @param array $breadcrumb
2044
     */
2045
    public function setBreadcrumb(array $breadcrumb)
2046
    {
2047
        $key = '_breadcrumbs';
2048
2049
        if (!isset($this->data[$key])) {
2050
            $this->data[$key] = array();
2051
        }
2052
2053
        $this->data[$key][] = $breadcrumb;
2054
    }
2055
2056
    /**
2057
     * Set meta tags on entity page
2058
     * @param array $data
2059
     */
2060
    protected function setMetaEntity(array $data)
2061
    {
2062
        if (!empty($data['meta_title'])) {
2063
            $this->setTitle($data['meta_title'], false);
2064
        }
2065
2066
        if (!empty($data['meta_description'])) {
2067
            $this->setMeta(array('name' => 'description', 'content' => $data['meta_description']));
2068
        }
2069
2070
        $this->setMeta(array('rel' => 'canonical', 'href' => $this->path));
2071
    }
2072
2073
    /**
2074
     * Sets an array of page breadcrumbs
2075
     * @param array $breadcrumbs
2076
     */
2077
    public function setBreadcrumbs(array $breadcrumbs)
2078
    {
2079
        foreach ($breadcrumbs as $breadcrumb) {
2080
            $this->setBreadcrumb($breadcrumb);
2081
        }
2082
    }
2083
2084
    /**
2085
     * Sets HTML filter globally
2086
     * @param array $data
2087
     */
2088
    public function setHtmlFilter($data)
2089
    {
2090
        $role_id = isset($data['role_id']) ? $data['role_id'] : 0;
2091
        $this->current_filter = $this->filter->getByRole($role_id);
2092
    }
2093
2094
    /**
2095
     * Returns true if an error occurred and pass back to template the submitted data
2096
     * @param boolean $message
2097
     * @return boolean
2098
     */
2099
    public function hasErrors($message = true)
2100
    {
2101
        if (empty($this->errors)) {
2102
            return false;
2103
        }
2104
2105
        if ($message) {
2106
            $this->setMessage($this->text('One or more errors occurred'), 'danger');
2107
        }
2108
2109
        if (isset($this->post_key)) {
2110
            $this->setData($this->post_key, $this->submitted);
2111
        }
2112
2113
        return true;
2114
    }
2115
2116
    /**
2117
     * Validates a submitted set of elements
2118
     * @param string $handler_id
2119
     * @param array $options
2120
     * @return array
2121
     */
2122
    public function validateComponent($handler_id, array $options = array())
2123
    {
2124
        $result = $this->validator->run($handler_id, $this->submitted, $options);
2125
2126
        if ($result === true) {
2127
            return array();
2128
        }
2129
2130
        return $this->errors = (array) $result;
2131
    }
2132
2133
    /**
2134
     * Validates a single element
2135
     * @param string|array $field
2136
     * @param string $handler_id
2137
     * @param string|array $arguments
2138
     * @return boolean
2139
     */
2140
    protected function validateElement($field, $handler_id, $arguments = array())
2141
    {
2142
        if (is_array($field)) {
2143
            $label = reset($field);
2144
            $field = key($field);
2145
        }
2146
2147
        $options = array(
2148
            'field' => $field,
2149
            'arguments' => (array) $arguments,
2150
            'label' => empty($label) ? $this->text('Field') : $label
2151
        );
2152
2153
        $result = $this->validator->run($handler_id, $this->submitted, $options);
2154
2155
        if ($result === true) {
2156
            return true;
2157
        }
2158
2159
        settype($result, 'array');
2160
        $this->errors = gplcart_array_merge($this->errors, $result);
2161
        return false;
2162
    }
2163
2164
    /**
2165
     * Sets a message or an array of messages
2166
     * @param string|array $messages
2167
     * @param string $severity
2168
     * @param bool $once
2169
     */
2170
    public function setMessage($messages, $severity = 'info', $once = false)
2171
    {
2172
        if (!empty($messages)) {
2173
            foreach (gplcart_array_flatten((array) $messages) as $message) {
2174
                if ($once) {
2175
                    $this->session->setMessage($message, $severity);
2176
                } else {
2177
                    $this->data['_messages'][$severity][] = $message;
2178
                }
2179
            }
2180
        }
2181
    }
2182
2183
    /**
2184
     * Sets filter variables to the data array
2185
     * @param array $allowed
2186
     * @param array $query
2187
     */
2188
    public function setFilter(array $allowed = array(), $query = null)
2189
    {
2190
        if (!isset($query)) {
2191
            $query = $this->getFilterQuery();
2192
        }
2193
2194
        $this->setFilterData($allowed, $query);
2195
        $this->query_filter = array_filter($query, 'is_string');
2196
    }
2197
2198
    /**
2199
     * @param array $allowed
2200
     * @param array $query
2201
     */
2202
    protected function setFilterData(array $allowed, array $query)
2203
    {
2204
        $this->data['_filtering'] = false;
2205
        $order = isset($this->query['order']) ? $this->query['order'] : '';
2206
2207
        foreach ($allowed as $filter) {
2208
2209
            $this->data["filter_$filter"] = '';
2210
2211
            if (isset($this->query[$filter]) && is_string($this->query[$filter])) {
2212
                $this->data['_filtering'] = true;
2213
                $this->data["filter_$filter"] = $this->query[$filter];
2214
            }
2215
2216
            $sort = array('sort' => $filter, 'order' => $order == 'desc' ? 'asc' : 'desc');
2217
            $sort += $query;
2218
            $this->data["sort_$filter"] = $this->url('', $sort);
2219
        }
2220
    }
2221
2222
    /**
2223
     * Returns an array of prepared GET values used for filtering and sorting
2224
     * @param array $default
2225
     * @param array $allowed
2226
     * @return array
2227
     */
2228
    public function getFilterQuery(array $default = array(), $allowed = array())
2229
    {
2230
        $query = $this->query;
2231
        foreach ($query as $key => $value) {
2232
2233
            if (!is_string($value)) {
2234
                continue;
2235
            }
2236
2237
            if ($key === 'sort' && strpos($value, '-') !== false) {
2238
                list($sort, $order) = explode('-', $value, 2);
2239
                $query['sort'] = $sort;
2240
                $query['order'] = $order;
2241
            }
2242
2243
            if ($value === 'any') {
2244
                unset($query[$key]);
2245
            }
2246
        }
2247
2248
        $query += $default;
2249
2250
        if (empty($allowed)) {
2251
            return $query;
2252
        }
2253
2254
        return array_intersect_key($query, array_flip($allowed));
2255
    }
2256
2257
    /**
2258
     * Sets the pager
2259
     * @param array $options
2260
     * @return array
2261
     */
2262
    public function setPager(array $options)
2263
    {
2264
        $pager = $this->getPager($options);
2265
        $this->data['_pager'] = $pager['rendered'];
2266
        return $pager['limit'];
2267
    }
2268
2269
    /**
2270
     * Returns a rendered pager
2271
     * @param array $options
2272
     * @return array
2273
     */
2274
    public function getPager(array $options = array())
2275
    {
2276
        $options += array(
2277
            'query' => $this->getFilterQuery(),
2278
            'limit' => $this->config('list_limit', 20)
2279
        );
2280
2281
        return array(
2282
            'rendered' => $this->getWidgetPager($options),
2283
            'limit' => $this->pager->getLimit()
2284
        );
2285
    }
2286
2287
    /**
2288
     * Returns the rendered pager
2289
     * @param array $options
2290
     * @return string
2291
     */
2292
    public function getWidgetPager(array $options)
2293
    {
2294
        $options += array(
2295
            'key' => 'p',
2296
            'page' => 1,
2297
            'template' => 'common/pager',
2298
            'query' => $this->getQuery(null, array(), 'array')
2299
        );
2300
2301
        if (isset($options['query'][$options['key']])) {
2302
            $options['page'] = (int) $options['query'][$options['key']];
2303
        }
2304
2305
        $options['query'][$options['key']] = '%num';
2306
2307
        $data = array(
2308
            'options' => $options,
2309
            'pager' => $this->pager->build($options)->get()
2310
        );
2311
2312
        return $this->render($options['template'], $data);
2313
    }
2314
2315
}
2316