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 */
26
27namespace PrestaShop\PrestaShop\Adapter\Module\Tab;
28
29use PrestaShop\PrestaShop\Adapter\Module\Module;
30use PrestaShopBundle\Entity\Repository\LangRepository;
31use PrestaShopBundle\Entity\Repository\TabRepository;
32use PrestaShopBundle\Entity\Tab;
33use Psr\Log\LoggerInterface;
34use Symfony\Component\Translation\TranslatorInterface;
35use Tab as TabClass;
36
37/**
38 * Class responsible of unregister existing tabs of Back Office's menu.
39 */
40class ModuleTabUnregister
41{
42    /**
43     * @var LangRepository
44     */
45    protected $langRepository;
46
47    /**
48     * @var TabRepository
49     */
50    protected $tabRepository;
51
52    /**
53     * @var LoggerInterface
54     */
55    private $logger;
56
57    /**
58     * @var TranslatorInterface
59     */
60    private $translator;
61
62    public function __construct(TabRepository $tabRepository, LangRepository $langRepository, LoggerInterface $logger, TranslatorInterface $translator)
63    {
64        $this->langRepository = $langRepository;
65        $this->tabRepository = $tabRepository;
66        $this->logger = $logger;
67        $this->translator = $translator;
68    }
69
70    /**
71     * Uninstall all module-defined tabs.
72     *
73     * This is done automatically as part of the module uninstallation.
74     *
75     * @return void
76     */
77    public function unregisterTabs(Module $module)
78    {
79        // We use the Tab repository to have only
80        // installed tabs related to the module
81        $tabs = $this->tabRepository->findByModule($module->get('name'));
82
83        foreach ($tabs as $tab) {
84            $this->unregisterTab($tab);
85            $this->removeDuplicatedParent($tab);
86        }
87    }
88
89    /**
90     * @param Module $module
91     */
92    public function disableTabs(Module $module)
93    {
94        $this->tabRepository->changeEnabledByModuleName($module->get('name'), false);
95    }
96
97    /**
98     * Uninstalls a tab given its defined structure.
99     *
100     * @param Tab $tab the instance of entity tab
101     */
102    private function unregisterTab(Tab $tab)
103    {
104        // We need to use the legacy class because of the right management
105        $tab_legacy = new TabClass($tab->getId());
106
107        if (!$tab_legacy->delete()) {
108            $this->logger->warning(
109                $this->translator->trans(
110                    'Failed to uninstall admin tab "%name%".',
111                    [
112                        '%name%' => $tab->getClassName(),
113                    ],
114                    'Admin.Modules.Notification'
115                )
116            );
117        }
118    }
119
120    /**
121     * When we add a level of children in the menu tabs, we created a dummy parent.
122     * We must delete it when it has no more children than the original tab.
123     *
124     * @param Tab $tab
125     */
126    private function removeDuplicatedParent(Tab $tab)
127    {
128        $remainingChildren = $this->tabRepository->findByParentId($tab->getIdParent());
129        // Or more than one children, the parent tab is still used.
130        // If there is no children, the deletion is likely to be done manually by the module.
131        if (count($remainingChildren) !== 1) {
132            return;
133        }
134
135        $parent = $this->tabRepository->find($tab->getIdParent());
136        $child = end($remainingChildren);
137
138        // We know we have a tab to delete if the parent name is the remaining child name+_MTR
139        if ($parent->getClassName() === $child->getClassName() . ModuleTabRegister::SUFFIX) {
140            $legacyTabParent = new TabClass($parent->getId());
141            // Setting a wrong id_parent will prevent the children to move
142            $legacyTabParent->id_parent = -1;
143            $legacyTabParent->delete();
144
145            $legacyTab = new TabClass($child->getId());
146            $legacyTab->id_parent = $parent->getIdParent();
147            $legacyTab->save();
148            // Updating the id_parent will override the position, that's why we save 2 times
149            $legacyTab->position = $parent->getPosition();
150            $legacyTab->save();
151        }
152    }
153}
154