1<?php
2/**
3 * Copyright since 2007 PrestaShop SA and Contributors
4 * PrestaShop is an International Registered Trademark & Property of PrestaShop SA
5 *
6 * NOTICE OF LICENSE
7 *
8 * This source file is subject to the Open Software License (OSL 3.0)
9 * that is bundled with this package in the file LICENSE.md.
10 * It is also available through the world-wide-web at this URL:
11 * https://opensource.org/licenses/OSL-3.0
12 * If you did not receive a copy of the license and are unable to
13 * obtain it through the world-wide-web, please send an email
14 * to license@prestashop.com so we can send you a copy immediately.
15 *
16 * DISCLAIMER
17 *
18 * Do not edit or add to this file if you wish to upgrade PrestaShop to newer
19 * versions in the future. If you wish to customize PrestaShop for your
20 * needs please refer to https://devdocs.prestashop.com/ for more information.
21 *
22 * @author    PrestaShop SA and Contributors <contact@prestashop.com>
23 * @copyright Since 2007 PrestaShop SA and Contributors
24 * @license   https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
25 */
26class TreeCore
27{
28    const DEFAULT_TEMPLATE_DIRECTORY = 'helpers/tree';
29    const DEFAULT_TEMPLATE = 'tree.tpl';
30    const DEFAULT_HEADER_TEMPLATE = 'tree_header.tpl';
31    const DEFAULT_NODE_FOLDER_TEMPLATE = 'tree_node_folder.tpl';
32    const DEFAULT_NODE_ITEM_TEMPLATE = 'tree_node_item.tpl';
33
34    protected $_attributes;
35    private $_context;
36    protected $_data;
37    protected $_data_search;
38    protected $_headerTemplate;
39    protected $_id_tree;
40    private $_id;
41    protected $_node_folder_template;
42    protected $_node_item_template;
43    protected $_template;
44
45    /** @var string */
46    private $_template_directory;
47    private $_title;
48    private $_no_js;
49
50    /** @var TreeToolbar|ITreeToolbar */
51    private $_toolbar;
52
53    /** @var Translator */
54    public $translator;
55
56    public function __construct($id, $data = null)
57    {
58        $this->translator = Context::getContext()->getTranslator();
59        $this->setId($id);
60
61        if (isset($data)) {
62            $this->setData($data);
63        }
64    }
65
66    public function __toString()
67    {
68        return $this->render();
69    }
70
71    public function setActions($value)
72    {
73        if (!isset($this->_toolbar)) {
74            $this->setToolbar(new TreeToolbarCore());
75        }
76
77        $this->getToolbar()->setTemplateDirectory($this->getTemplateDirectory())->setActions($value);
78
79        return $this;
80    }
81
82    public function getActions()
83    {
84        if (!isset($this->_toolbar)) {
85            $this->setToolbar(new TreeToolbarCore());
86        }
87
88        return $this->getToolbar()->setTemplateDirectory($this->getTemplateDirectory())->getActions();
89    }
90
91    public function setAttribute($name, $value)
92    {
93        if (!isset($this->_attributes)) {
94            $this->_attributes = [];
95        }
96
97        $this->_attributes[$name] = $value;
98
99        return $this;
100    }
101
102    public function getAttribute($name)
103    {
104        return $this->hasAttribute($name) ? $this->_attributes[$name] : null;
105    }
106
107    public function setAttributes($value)
108    {
109        if (!is_array($value) && !$value instanceof Traversable) {
110            throw new PrestaShopException('Data value must be an traversable array');
111        }
112
113        $this->_attributes = $value;
114
115        return $this;
116    }
117
118    public function setIdTree($id_tree)
119    {
120        $this->_id_tree = $id_tree;
121
122        return $this;
123    }
124
125    public function getIdTree()
126    {
127        return $this->_id_tree;
128    }
129
130    public function getAttributes()
131    {
132        if (!isset($this->_attributes)) {
133            $this->_attributes = [];
134        }
135
136        return $this->_attributes;
137    }
138
139    public function setContext($value)
140    {
141        $this->_context = $value;
142
143        return $this;
144    }
145
146    public function getContext()
147    {
148        if (!isset($this->_context)) {
149            $this->_context = Context::getContext();
150        }
151
152        return $this->_context;
153    }
154
155    public function setDataSearch($value)
156    {
157        if (!is_array($value) && !$value instanceof Traversable) {
158            throw new PrestaShopException('Data value must be an traversable array');
159        }
160
161        $this->_data_search = $value;
162
163        return $this;
164    }
165
166    public function getDataSearch()
167    {
168        if (!isset($this->_data_search)) {
169            $this->_data_search = [];
170        }
171
172        return $this->_data_search;
173    }
174
175    public function setData($value)
176    {
177        if (!is_array($value) && !$value instanceof Traversable) {
178            throw new PrestaShopException('Data value must be an traversable array');
179        }
180
181        $this->_data = $value;
182
183        return $this;
184    }
185
186    public function getData()
187    {
188        if (!isset($this->_data)) {
189            $this->_data = [];
190        }
191
192        return $this->_data;
193    }
194
195    public function setHeaderTemplate($value)
196    {
197        $this->_headerTemplate = $value;
198
199        return $this;
200    }
201
202    public function getHeaderTemplate()
203    {
204        if (!isset($this->_headerTemplate)) {
205            $this->setHeaderTemplate(self::DEFAULT_HEADER_TEMPLATE);
206        }
207
208        return $this->_headerTemplate;
209    }
210
211    public function setId($value)
212    {
213        $this->_id = $value;
214
215        return $this;
216    }
217
218    public function getId()
219    {
220        return $this->_id;
221    }
222
223    public function setNodeFolderTemplate($value)
224    {
225        $this->_node_folder_template = $value;
226
227        return $this;
228    }
229
230    public function getNodeFolderTemplate()
231    {
232        if (!isset($this->_node_folder_template)) {
233            $this->setNodeFolderTemplate(self::DEFAULT_NODE_FOLDER_TEMPLATE);
234        }
235
236        return $this->_node_folder_template;
237    }
238
239    public function setNodeItemTemplate($value)
240    {
241        $this->_node_item_template = $value;
242
243        return $this;
244    }
245
246    public function getNodeItemTemplate()
247    {
248        if (!isset($this->_node_item_template)) {
249            $this->setNodeItemTemplate(self::DEFAULT_NODE_ITEM_TEMPLATE);
250        }
251
252        return $this->_node_item_template;
253    }
254
255    public function setTemplate($value)
256    {
257        $this->_template = $value;
258
259        return $this;
260    }
261
262    public function getTemplate()
263    {
264        if (!isset($this->_template)) {
265            $this->setTemplate(self::DEFAULT_TEMPLATE);
266        }
267
268        return $this->_template;
269    }
270
271    /**
272     * @param $value
273     *
274     * @return Tree
275     */
276    public function setTemplateDirectory($value)
277    {
278        $this->_template_directory = $this->_normalizeDirectory($value);
279
280        return $this;
281    }
282
283    /**
284     * @return string
285     */
286    public function getTemplateDirectory()
287    {
288        if (!isset($this->_template_directory)) {
289            $this->_template_directory = $this->_normalizeDirectory(
290                self::DEFAULT_TEMPLATE_DIRECTORY
291            );
292        }
293
294        return $this->_template_directory;
295    }
296
297    public function getTemplateFile($template)
298    {
299        if (preg_match_all('/((?:^|[A-Z])[a-z]+)/', get_class($this->getContext()->controller), $matches) !== false) {
300            $controller_name = strtolower($matches[0][1]);
301        }
302
303        if ($this->getContext()->controller instanceof ModuleAdminController && isset($controller_name) && file_exists($this->_normalizeDirectory(
304                $this->getContext()->controller->getTemplatePath()
305        ) . $controller_name . DIRECTORY_SEPARATOR . $this->getTemplateDirectory() . $template)) {
306            return $this->_normalizeDirectory($this->getContext()->controller->getTemplatePath()) .
307                $controller_name . DIRECTORY_SEPARATOR . $this->getTemplateDirectory() . $template;
308        } elseif ($this->getContext()->controller instanceof ModuleAdminController && file_exists($this->_normalizeDirectory(
309                $this->getContext()->controller->getTemplatePath()
310        ) . $this->getTemplateDirectory() . $template)) {
311            return $this->_normalizeDirectory($this->getContext()->controller->getTemplatePath())
312                . $this->getTemplateDirectory() . $template;
313        } elseif ($this->getContext()->controller instanceof AdminController && isset($controller_name)
314            && file_exists($this->_normalizeDirectory($this->getContext()->smarty->getTemplateDir(0)) . 'controllers'
315                . DIRECTORY_SEPARATOR . $controller_name . DIRECTORY_SEPARATOR . $this->getTemplateDirectory() . $template)) {
316            return $this->_normalizeDirectory($this->getContext()->smarty->getTemplateDir(0)) . 'controllers'
317                . DIRECTORY_SEPARATOR . $controller_name . DIRECTORY_SEPARATOR . $this->getTemplateDirectory() . $template;
318        } elseif (file_exists($this->_normalizeDirectory($this->getContext()->smarty->getTemplateDir(1))
319                . $this->getTemplateDirectory() . $template)) {
320            return $this->_normalizeDirectory($this->getContext()->smarty->getTemplateDir(1))
321                    . $this->getTemplateDirectory() . $template;
322        } elseif (file_exists($this->_normalizeDirectory($this->getContext()->smarty->getTemplateDir(0))
323                . $this->getTemplateDirectory() . $template)) {
324            return $this->_normalizeDirectory($this->getContext()->smarty->getTemplateDir(0))
325                . $this->getTemplateDirectory() . $template;
326        } else {
327            return $this->getTemplateDirectory() . $template;
328        }
329    }
330
331    public function setNoJS($value)
332    {
333        $this->_no_js = $value;
334
335        return $this;
336    }
337
338    public function setTitle($value)
339    {
340        $this->_title = $value;
341
342        return $this;
343    }
344
345    public function getTitle()
346    {
347        return $this->_title;
348    }
349
350    public function setToolbar($value)
351    {
352        if (!is_object($value)) {
353            throw new PrestaShopException('Toolbar must be a class object');
354        }
355
356        $reflection = new ReflectionClass($value);
357
358        if (!$reflection->implementsInterface('ITreeToolbarCore')) {
359            throw new PrestaShopException('Toolbar class must implements ITreeToolbarCore interface');
360        }
361
362        $this->_toolbar = $value;
363
364        return $this;
365    }
366
367    public function getToolbar()
368    {
369        if (isset($this->_toolbar)) {
370            if ($this->getDataSearch()) {
371                $this->_toolbar->setData($this->getDataSearch());
372            } else {
373                $this->_toolbar->setData($this->getData());
374            }
375        }
376
377        return $this->_toolbar;
378    }
379
380    public function addAction($action)
381    {
382        if (!isset($this->_toolbar)) {
383            $this->setToolbar(new TreeToolbarCore());
384        }
385
386        $this->getToolbar()->setTemplateDirectory($this->getTemplateDirectory())->addAction($action);
387
388        return $this;
389    }
390
391    public function removeActions()
392    {
393        if (!isset($this->_toolbar)) {
394            $this->setToolbar(new TreeToolbarCore());
395        }
396
397        $this->getToolbar()->setTemplateDirectory($this->getTemplateDirectory())->removeActions();
398
399        return $this;
400    }
401
402    public function render($data = null)
403    {
404        //Adding tree.js
405        $admin_webpath = str_ireplace(_PS_CORE_DIR_, '', _PS_ADMIN_DIR_);
406        $admin_webpath = preg_replace('/^' . preg_quote(DIRECTORY_SEPARATOR, '/') . '/', '', $admin_webpath);
407        $bo_theme = ((Validate::isLoadedObject($this->getContext()->employee)
408            && $this->getContext()->employee->bo_theme) ? $this->getContext()->employee->bo_theme : 'default');
409
410        if (!file_exists(_PS_BO_ALL_THEMES_DIR_ . $bo_theme . DIRECTORY_SEPARATOR . 'template')) {
411            $bo_theme = 'default';
412        }
413
414        $js_path = __PS_BASE_URI__ . $admin_webpath . '/themes/' . $bo_theme . '/js/tree.js';
415        if ($this->getContext()->controller->ajax) {
416            if (!$this->_no_js) {
417                $html = '<script type="text/javascript" src="' . $js_path . '"></script>';
418            }
419        } else {
420            $this->getContext()->controller->addJs($js_path);
421        }
422
423        //Create Tree Template
424        $template = $this->getContext()->smarty->createTemplate(
425            $this->getTemplateFile($this->getTemplate()),
426            $this->getContext()->smarty
427        );
428
429        if (trim($this->getTitle()) != '' || $this->useToolbar()) {
430            //Create Tree Header Template
431            $headerTemplate = $this->getContext()->smarty->createTemplate(
432                $this->getTemplateFile($this->getHeaderTemplate()),
433                $this->getContext()->smarty
434            );
435            $headerTemplate->assign($this->getAttributes())
436                ->assign(
437                    [
438                        'title' => $this->getTitle(),
439                        'toolbar' => $this->useToolbar() ? $this->renderToolbar() : null,
440                    ]
441            );
442            $template->assign('header', $headerTemplate->fetch());
443        }
444
445        //Assign Tree nodes
446        $template->assign($this->getAttributes())->assign([
447            'id' => $this->getId(),
448            'nodes' => $this->renderNodes($data),
449            'id_tree' => $this->getIdTree(),
450        ]);
451
452        return (isset($html) ? $html : '') . $template->fetch();
453    }
454
455    public function renderNodes($data = null)
456    {
457        if (!isset($data)) {
458            $data = $this->getData();
459        }
460
461        if (!is_array($data) && !$data instanceof Traversable) {
462            throw new PrestaShopException('Data value must be an traversable array');
463        }
464
465        $html = '';
466
467        foreach ($data as $item) {
468            if (array_key_exists('children', $item)
469                && !empty($item['children'])) {
470                $html .= $this->getContext()->smarty->createTemplate(
471                    $this->getTemplateFile($this->getNodeFolderTemplate()),
472                    $this->getContext()->smarty
473                )->assign([
474                    'children' => $this->renderNodes($item['children']),
475                    'node' => $item,
476                ])->fetch();
477            } else {
478                $html .= $this->getContext()->smarty->createTemplate(
479                    $this->getTemplateFile($this->getNodeItemTemplate()),
480                    $this->getContext()->smarty
481                )->assign([
482                    'node' => $item,
483                ])->fetch();
484            }
485        }
486
487        return $html;
488    }
489
490    public function renderToolbar()
491    {
492        return $this->getToolbar()->render();
493    }
494
495    public function useInput()
496    {
497        return isset($this->_input_type);
498    }
499
500    public function useToolbar()
501    {
502        return isset($this->_toolbar);
503    }
504
505    private function _normalizeDirectory($directory)
506    {
507        $last = $directory[strlen($directory) - 1];
508
509        if (in_array($last, ['/', '\\'])) {
510            $directory[strlen($directory) - 1] = DIRECTORY_SEPARATOR;
511
512            return $directory;
513        }
514
515        $directory .= DIRECTORY_SEPARATOR;
516
517        return $directory;
518    }
519}
520