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 PrestaShopBundle\Controller\Admin\Improve;
28
29use DateTime;
30use Exception;
31use Module;
32use PrestaShop\PrestaShop\Adapter\Module\AdminModuleDataProvider;
33use PrestaShop\PrestaShop\Adapter\Module\Module as ModuleAdapter;
34use PrestaShop\PrestaShop\Core\Addon\AddonListFilter;
35use PrestaShop\PrestaShop\Core\Addon\AddonListFilterStatus;
36use PrestaShop\PrestaShop\Core\Addon\AddonListFilterType;
37use PrestaShop\PrestaShop\Core\Addon\AddonsCollection;
38use PrestaShop\PrestaShop\Core\Addon\Module\Exception\UnconfirmedModuleActionException;
39use PrestaShop\PrestaShop\Core\Addon\Module\ModuleRepository;
40use PrestaShopBundle\Controller\Admin\Improve\Modules\ModuleAbstractController;
41use PrestaShopBundle\Entity\ModuleHistory;
42use PrestaShopBundle\Security\Annotation\AdminSecurity;
43use PrestaShopBundle\Security\Voter\PageVoter;
44use PrestaShopBundle\Service\DataProvider\Admin\CategoriesProvider;
45use Profile;
46use Symfony\Component\Form\Util\ServerParams;
47use Symfony\Component\HttpFoundation\JsonResponse;
48use Symfony\Component\HttpFoundation\Request;
49use Symfony\Component\HttpFoundation\Response;
50use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
51use Symfony\Component\Validator\Constraints as Assert;
52
53/**
54 * Responsible of "Improve > Modules > Modules & Services > Catalog / Manage" page display.
55 */
56class ModuleController extends ModuleAbstractController
57{
58    public const CONTROLLER_NAME = 'ADMINMODULESSF';
59
60    public const MAX_MODULES_DISPLAYED = 6;
61
62    /**
63     * @AdminSecurity("is_granted(['read'], 'ADMINMODULESSF_')")
64     *
65     * @return Response
66     */
67    public function catalogAction()
68    {
69        return $this->render(
70            '@PrestaShop/Admin/Module/catalog.html.twig',
71            [
72                'layoutHeaderToolbarBtn' => $this->getToolbarButtons(),
73                'layoutTitle' => $this->trans('Modules catalog', 'Admin.Navigation.Menu'),
74                'requireAddonsSearch' => true,
75                'requireBulkActions' => false,
76                'showContentHeader' => true,
77                'enableSidebar' => true,
78                'help_link' => $this->generateSidebarLink('AdminModules'),
79                'requireFilterStatus' => false,
80                'level' => $this->authorizationLevel(self::CONTROLLER_NAME),
81                'errorMessage' => $this->trans(
82                    'You do not have permission to add this.',
83                    'Admin.Notifications.Error'
84                ),
85            ]
86        );
87    }
88
89    /**
90     * Controller responsible for displaying "Catalog Module Grid" section of Module management pages with ajax.
91     *
92     * @AdminSecurity("is_granted(['read'], 'ADMINMODULESSF_')")
93     *
94     * @return Response
95     */
96    public function manageAction()
97    {
98        $modulesProvider = $this->get('prestashop.core.admin.data_provider.module_interface');
99        $shopService = $this->get('prestashop.adapter.shop.context');
100        $moduleRepository = $this->get('prestashop.core.admin.module.repository');
101        $themeRepository = $this->get('prestashop.core.addon.theme.repository');
102
103        // Retrieve current shop
104        $shopId = $shopService->getContextShopId();
105        $shops = $shopService->getShops();
106        $modulesTheme = [];
107        if (isset($shops[$shopId]) && is_array($shops[$shopId])) {
108            $shop = $shops[$shopId];
109            $currentTheme = $themeRepository->getInstanceByName($shop['theme_name']);
110            $modulesTheme = $currentTheme->getModulesToEnable();
111        }
112
113        $filters = new AddonListFilter();
114        $filters->setType(AddonListFilterType::MODULE | AddonListFilterType::SERVICE)
115            ->removeStatus(AddonListFilterStatus::UNINSTALLED);
116        $installedProducts = $moduleRepository->getFilteredList($filters);
117
118        $categories = $this->getCategories($modulesProvider, $installedProducts);
119        $bulkActions = [
120            'bulk-uninstall' => $this->trans('Uninstall', 'Admin.Actions'),
121            'bulk-disable' => $this->trans('Disable', 'Admin.Actions'),
122            'bulk-enable' => $this->trans('Enable', 'Admin.Actions'),
123            'bulk-reset' => $this->trans('Reset', 'Admin.Actions'),
124            'bulk-enable-mobile' => $this->trans('Enable Mobile', 'Admin.Modules.Feature'),
125            'bulk-disable-mobile' => $this->trans('Disable Mobile', 'Admin.Modules.Feature'),
126        ];
127
128        return $this->render(
129            '@PrestaShop/Admin/Module/manage.html.twig',
130            [
131                'maxModulesDisplayed' => self::MAX_MODULES_DISPLAYED,
132                'bulkActions' => $bulkActions,
133                'layoutHeaderToolbarBtn' => $this->getToolbarButtons(),
134                'layoutTitle' => $this->trans('Module manager', 'Admin.Modules.Feature'),
135                'categories' => $categories['categories'],
136                'topMenuData' => $this->getTopMenuData($categories),
137                'requireAddonsSearch' => false,
138                'requireBulkActions' => true,
139                'enableSidebar' => true,
140                'help_link' => $this->generateSidebarLink('AdminModules'),
141                'requireFilterStatus' => true,
142                'level' => $this->authorizationLevel(self::CONTROLLER_NAME),
143                'errorMessage' => $this->trans('You do not have permission to add this.', 'Admin.Notifications.Error'),
144            ]
145        );
146    }
147
148    /**
149     * @AdminSecurity("is_granted(['read'], 'ADMINMODULESSF_')")
150     *
151     * @param Request $request
152     *
153     * @return Response
154     */
155    public function getPreferredModulesAction(Request $request)
156    {
157        $tabModulesList = $request->get('tab_modules_list');
158
159        if ($tabModulesList) {
160            $tabModulesList = explode(',', $tabModulesList);
161        }
162        $modulesListUnsorted = $this->getModulesByInstallation(
163            $tabModulesList
164        );
165
166        $installed = $uninstalled = [];
167
168        if (!empty($tabModulesList)) {
169            foreach ($tabModulesList as $key => $value) {
170                foreach ($modulesListUnsorted['installed'] as $moduleInstalled) {
171                    if ($moduleInstalled['attributes']['name'] == $value) {
172                        $installed[] = $moduleInstalled;
173
174                        continue 2;
175                    }
176                }
177
178                foreach ($modulesListUnsorted['not_installed'] as $moduleNotInstalled) {
179                    if ($moduleNotInstalled['attributes']['name'] == $value) {
180                        $uninstalled[] = $moduleNotInstalled;
181
182                        continue 2;
183                    }
184                }
185            }
186        }
187
188        $moduleListSorted = [
189            'installed' => $installed,
190            'notInstalled' => $uninstalled,
191        ];
192
193        $twigParams = [
194            'currentIndex' => '',
195            'modulesList' => $moduleListSorted,
196            'level' => $this->authorizationLevel(self::CONTROLLER_NAME),
197        ];
198
199        if ($request->request->has('admin_list_from_source')) {
200            $twigParams['adminListFromSource'] = $request->request->get('admin_list_from_source');
201        }
202
203        return $this->render(
204            '@PrestaShop/Admin/Module/tab-modules-list.html.twig',
205            $twigParams
206        );
207    }
208
209    /**
210     * @AdminSecurity("is_granted(['read', 'create', 'update', 'delete'], 'ADMINMODULESSF_')")
211     *
212     * @param Request $module_name
213     *
214     * @return Response
215     */
216    public function configureModuleAction($module_name)
217    {
218        /** @var UrlGeneratorInterface $legacyUrlGenerator */
219        $legacyUrlGenerator = $this->get('prestashop.core.admin.url_generator_legacy');
220        $legacyContextProvider = $this->get('prestashop.adapter.legacy.context');
221        $legacyContext = $legacyContextProvider->getContext();
222        $moduleRepository = $this->get('prestashop.core.admin.module.repository');
223        // Get accessed module object
224        $moduleAccessed = $moduleRepository->getModule($module_name);
225
226        // Get current employee Id
227        $currentEmployeeId = $legacyContext->employee->id;
228        // Get accessed module DB Id
229        $moduleAccessedId = (int) $moduleAccessed->database->get('id');
230
231        // Save history for this module
232        $moduleHistory = $this->getDoctrine()
233            ->getRepository('PrestaShopBundle:ModuleHistory')
234            ->findOneBy(
235                [
236                    'idEmployee' => $currentEmployeeId,
237                    'idModule' => $moduleAccessedId,
238                ]
239            );
240
241        if (null === $moduleHistory) {
242            $moduleHistory = new ModuleHistory();
243        }
244
245        $moduleHistory->setIdEmployee($currentEmployeeId);
246        $moduleHistory->setIdModule($moduleAccessedId);
247        $moduleHistory->setDateUpd(new DateTime());
248
249        $em = $this->getDoctrine()->getManager();
250        $em->persist($moduleHistory);
251        $em->flush();
252
253        return $this->redirect(
254            $legacyUrlGenerator->generate(
255                'admin_module_configure_action',
256                [
257                    // do not transmit limit & offset: go to the first page when redirecting
258                    'configure' => $module_name,
259                ]
260            )
261        );
262    }
263
264    /**
265     * @AdminSecurity("is_granted(['read'], 'ADMINMODULESSF_')")
266     *
267     * @param int $moduleId
268     *
269     * @return Response
270     */
271    public function getModuleCartAction($moduleId)
272    {
273        $moduleRepository = $this->get('prestashop.core.admin.module.repository');
274        $module = $moduleRepository->getModuleById($moduleId);
275
276        $addOnsAdminDataProvider = $this->get('prestashop.core.admin.data_provider.module_interface');
277        $addOnsAdminDataProvider->generateAddonsUrls(
278            AddonsCollection::createFrom([$module])
279        );
280
281        $modulePresenter = $this->get('prestashop.adapter.presenter.module');
282        $moduleToPresent = $modulePresenter->present($module);
283
284        return $this->render(
285            '@PrestaShop/Admin/Module/Includes/modal_read_more_content.html.twig',
286            [
287                'module' => $moduleToPresent,
288                'level' => $this->authorizationLevel(self::CONTROLLER_NAME),
289            ]
290        );
291    }
292
293    /**
294     * Controller responsible for displaying "Catalog Module Grid" section of Module management pages with ajax.
295     *
296     * @param Request $request
297     *
298     * @return JsonResponse
299     */
300    public function refreshCatalogAction(Request $request)
301    {
302        $deniedAccess = $this->checkPermissions(
303            [
304                PageVoter::LEVEL_READ,
305                PageVoter::LEVEL_CREATE,
306                PageVoter::LEVEL_DELETE,
307                PageVoter::LEVEL_UPDATE,
308            ]
309        );
310        if (null !== $deniedAccess) {
311            return $deniedAccess;
312        }
313
314        $modulesProvider = $this->get('prestashop.core.admin.data_provider.module_interface');
315        $moduleRepository = $this->get('prestashop.core.admin.module.repository');
316        $responseArray = [];
317
318        $filters = new AddonListFilter();
319        $filters->setType(AddonListFilterType::MODULE | AddonListFilterType::SERVICE)
320            ->setStatus(~AddonListFilterStatus::INSTALLED);
321
322        try {
323            $modulesFromRepository = AddonsCollection::createFrom($moduleRepository->getFilteredList($filters));
324            $modulesProvider->generateAddonsUrls($modulesFromRepository);
325
326            $modules = $modulesFromRepository->toArray();
327            shuffle($modules);
328            $categories = $this->getCategories($modulesProvider, $modules);
329
330            $responseArray['domElements'][] = $this->constructJsonCatalogCategoriesMenuResponse($categories);
331            $responseArray['domElements'][] = $this->constructJsonCatalogBodyResponse(
332                $categories,
333                $modules
334            );
335            $responseArray['status'] = true;
336        } catch (Exception $e) {
337            $responseArray['msg'] = $this->trans(
338                'Cannot get catalog data, please try again later. Reason: %error_details%',
339                'Admin.Modules.Notification',
340                ['%error_details%' => print_r($e->getMessage(), true)]
341            );
342            $responseArray['status'] = false;
343        }
344
345        return new JsonResponse($responseArray);
346    }
347
348    /**
349     * @param Request $request
350     *
351     * @return JsonResponse
352     */
353    public function moduleAction(Request $request)
354    {
355        $action = $request->get('action');
356
357        switch ($action) {
358            case ModuleAdapter::ACTION_UPGRADE:
359            case ModuleAdapter::ACTION_RESET:
360            case ModuleAdapter::ACTION_ENABLE:
361            case ModuleAdapter::ACTION_DISABLE:
362            case ModuleAdapter::ACTION_ENABLE_MOBILE:
363            case ModuleAdapter::ACTION_DISABLE_MOBILE:
364                $deniedAccess = $this->checkPermission(PageVoter::UPDATE);
365                break;
366            case ModuleAdapter::ACTION_INSTALL:
367                $deniedAccess = $this->checkPermission(PageVoter::CREATE);
368                break;
369            case ModuleAdapter::ACTION_UNINSTALL:
370                $deniedAccess = $this->checkPermission(PageVoter::DELETE);
371                break;
372
373            default:
374                $deniedAccess = null;
375        }
376
377        if (null !== $deniedAccess) {
378            return $deniedAccess;
379        }
380
381        if ($this->isDemoModeEnabled()) {
382            return $this->getDisabledFunctionalityResponse($request);
383        }
384
385        $module = $request->get('module_name');
386        $moduleManager = $this->container->get('prestashop.module.manager');
387        $moduleManager->setActionParams($request->request->get('actionParams', []));
388        $moduleRepository = $this->container->get('prestashop.core.admin.module.repository');
389        $modulesProvider = $this->container->get('prestashop.core.admin.data_provider.module_interface');
390        $response = [$module => []];
391
392        if (!method_exists($moduleManager, $action)) {
393            $response[$module]['status'] = false;
394            $response[$module]['msg'] = $this->trans('Invalid action', 'Admin.Notifications.Error');
395
396            return new JsonResponse($response);
397        }
398
399        $actionTitle = str_replace('_', ' ', $action);
400
401        try {
402            $response[$module]['status'] = $moduleManager->{$action}($module);
403
404            if ($response[$module]['status'] === true) {
405                $response[$module]['msg'] = $this->trans(
406                    '%action% action on module %module% succeeded.',
407                    'Admin.Modules.Notification',
408                    [
409                        '%action%' => ucfirst($actionTitle),
410                        '%module%' => $module,
411                    ]
412                );
413
414                if ($action !== 'uninstall') {
415                    $response[$module]['module_name'] = $module;
416                    $response[$module]['is_configurable'] = (bool) $this
417                        ->get('prestashop.core.admin.module.repository')
418                        ->getModule($module)
419                        ->attributes
420                        ->get('is_configurable');
421                }
422            } elseif ($response[$module]['status'] === false) {
423                $error = $moduleManager->getError($module);
424                $response[$module]['msg'] = $this->trans(
425                    'Cannot %action% module %module%. %error_details%',
426                    'Admin.Modules.Notification',
427                    [
428                        '%action%' => $actionTitle,
429                        '%module%' => $module,
430                        '%error_details%' => $error,
431                    ]
432                );
433            } else {
434                $response[$module]['status'] = false;
435                $response[$module]['msg'] = $this->trans(
436                    '%module% did not return a valid response on %action% action.',
437                    'Admin.Modules.Notification',
438                    [
439                        '%module%' => $module,
440                        '%action%' => $actionTitle,
441                    ]
442                );
443            }
444        } catch (UnconfirmedModuleActionException $e) {
445            $collection = AddonsCollection::createFrom([$e->getModule()]);
446            $modules = $modulesProvider->generateAddonsUrls($collection);
447            $response[$module] = array_replace(
448                $response[$module],
449                [
450                    'status' => false,
451                    'confirmation_subject' => $e->getSubject(),
452                    'module' => $this
453                        ->container
454                        ->get('prestashop.adapter.presenter.module')
455                        ->presentCollection($modules)[0],
456                    'msg' => $this->trans(
457                        'Confirmation needed by module %module% on %action% (%subject%).',
458                        'Admin.Modules.Notification',
459                        [
460                            '%subject%' => $e->getSubject(),
461                            '%action%' => $e->getAction(),
462                            '%module%' => $module,
463                        ]
464                    ),
465                ]
466            );
467        } catch (Exception $e) {
468            $response[$module]['status'] = false;
469            $response[$module]['msg'] = $this->trans(
470                'Exception thrown by module %module% on %action%. %error_details%',
471                'Admin.Modules.Notification',
472                [
473                    '%action%' => $actionTitle,
474                    '%module%' => $module,
475                    '%error_details%' => $e->getMessage(),
476                ]
477            );
478
479            // Get accessed module object
480            $moduleAccessed = $moduleRepository->getModule($module);
481            // Get accessed module DB Id
482            $moduleAccessedId = (int) $moduleAccessed->database->get('id');
483            $logger = $this->container->get('logger');
484            $logger->error($response[$module]['msg'], ['object_type' => 'Module', 'object_id' => $moduleAccessedId]);
485        }
486
487        if ($response[$module]['status'] === true && $action != 'uninstall') {
488            $moduleInstance = $moduleRepository->getModule($module);
489            $collection = AddonsCollection::createFrom([$moduleInstance]);
490            $response[$module]['action_menu_html'] = $this->container->get('templating')->render(
491                '@PrestaShop/Admin/Module/Includes/action_menu.html.twig',
492                [
493                    'module' => $this->container->get('prestashop.adapter.presenter.module')
494                        ->presentCollection($modulesProvider->generateAddonsUrls($collection))[0],
495                    'level' => $this->authorizationLevel(self::CONTROLLER_NAME),
496                ]
497            );
498        }
499
500        return new JsonResponse($response);
501    }
502
503    /**
504     * Controller responsible for importing new module from DropFile zone in BO.
505     *
506     * @param Request $request
507     *
508     * @return JsonResponse
509     */
510    public function importModuleAction(Request $request)
511    {
512        if ($this->isDemoModeEnabled()) {
513            return new JsonResponse(
514                [
515                    'status' => false,
516                    'msg' => $this->getDemoModeErrorMessage(),
517                ]
518            );
519        }
520
521        $deniedAccess = $this->checkPermissions(
522            [
523                PageVoter::LEVEL_CREATE,
524                PageVoter::LEVEL_DELETE,
525            ]
526        );
527        if (null !== $deniedAccess) {
528            return $deniedAccess;
529        }
530
531        $moduleManager = $this->get('prestashop.module.manager');
532        $moduleZipManager = $this->get('prestashop.module.zip.manager');
533        $serverParams = new ServerParams();
534        $moduleName = '';
535
536        try {
537            if ($serverParams->hasPostMaxSizeBeenExceeded()) {
538                throw new Exception($this->trans(
539                    'The uploaded file exceeds the post_max_size directive in php.ini',
540                    'Admin.Notifications.Error'
541                ));
542            }
543
544            $fileUploaded = $request->files->get('file_uploaded');
545            $constraints = [
546                new Assert\NotNull(
547                    [
548                        'message' => $this->trans(
549                            'The file is missing.',
550                            'Admin.Notifications.Error'
551                        ),
552                    ]
553                ),
554                new Assert\File(
555                    [
556                        'maxSize' => ini_get('upload_max_filesize'),
557                        'mimeTypes' => [
558                            'application/zip',
559                            'application/x-gzip',
560                            'application/gzip',
561                            'application/x-gtar',
562                            'application/x-tgz',
563                        ],
564                    ]
565                ),
566            ];
567
568            $violations = $this->get('validator')->validate($fileUploaded, $constraints);
569            if (0 !== count($violations)) {
570                $violationsMessages = [];
571                foreach ($violations as $violation) {
572                    $violationsMessages[] = $violation->getMessage();
573                }
574
575                throw new Exception(implode(PHP_EOL, $violationsMessages));
576            }
577
578            $moduleName = $moduleZipManager->getName($fileUploaded->getPathname());
579
580            // Install the module
581            $installationResponse = [
582                'status' => $moduleManager->install($fileUploaded->getPathname()),
583                'msg' => '',
584                'module_name' => $moduleName,
585            ];
586
587            if ($installationResponse['status'] === null) {
588                $installationResponse['status'] = false;
589                $installationResponse['msg'] = $this->trans(
590                    '%module% did not return a valid response on installation.',
591                    'Admin.Modules.Notification',
592                    ['%module%' => $moduleName]
593                );
594            } elseif ($installationResponse['status'] === true) {
595                $installationResponse['msg'] = $this->trans(
596                    'Installation of module %module% was successful.',
597                    'Admin.Modules.Notification',
598                    ['%module%' => $moduleName]
599                );
600                $installationResponse['is_configurable'] = (bool) $this->get('prestashop.core.admin.module.repository')
601                    ->getModule($moduleName)
602                    ->attributes
603                    ->get('is_configurable');
604            } else {
605                $error = $moduleManager->getError($moduleName);
606                $installationResponse['msg'] = $this->trans(
607                    'Installation of module %module% failed. %error%',
608                    'Admin.Modules.Notification',
609                    [
610                        '%module%' => $moduleName,
611                        '%error%' => $error,
612                    ]
613                );
614            }
615        } catch (UnconfirmedModuleActionException $e) {
616            $collection = AddonsCollection::createFrom([$e->getModule()]);
617            $modules = $this->get('prestashop.core.admin.data_provider.module_interface')
618                ->generateAddonsUrls($collection);
619            $installationResponse = [
620                'status' => false,
621                'confirmation_subject' => $e->getSubject(),
622                'module' => $this->get('prestashop.adapter.presenter.module')->presentCollection($modules)[0],
623                'msg' => $this->trans(
624                    'Confirmation needed by module %module% on %action% (%subject%).',
625                    'Admin.Modules.Notification',
626                    [
627                        '%subject%' => $e->getSubject(),
628                        '%action%' => $e->getAction(),
629                        '%module%' => $moduleName,
630                    ]
631                ),
632            ];
633        } catch (Exception $e) {
634            try {
635                if (isset($moduleName)) {
636                    $moduleManager->disable($moduleName);
637                }
638            } catch (Exception $subE) {
639            }
640
641            throw $e;
642        }
643
644        return new JsonResponse($installationResponse);
645    }
646
647    private function getModulesByInstallation($modulesSelectList = null)
648    {
649        $addonsProvider = $this->get('prestashop.core.admin.data_provider.module_interface');
650        $moduleRepository = $this->get('prestashop.core.admin.module.repository');
651        $modulePresenter = $this->get('prestashop.adapter.presenter.module');
652        $tabRepository = $this->get('prestashop.core.admin.tab.repository');
653
654        $modulesOnDisk = AddonsCollection::createFrom($moduleRepository->getList());
655
656        $modulesList = [
657            'installed' => [],
658            'not_installed' => [],
659        ];
660
661        $modulesOnDisk = $addonsProvider->generateAddonsUrls($modulesOnDisk);
662        foreach ($modulesOnDisk as $module) {
663            if (!isset($modulesSelectList) || in_array($module->get('name'), $modulesSelectList)) {
664                $perm = true;
665                if ($module->get('id')) {
666                    $perm &= Module::getPermissionStatic(
667                        $module->get('id'),
668                        'configure',
669                        $this->getContext()->employee
670                    );
671                } else {
672                    $id_admin_module = $tabRepository->findOneIdByClassName('AdminModules');
673                    $access = Profile::getProfileAccess(
674                        $this->getContext()->employee->id_profile,
675                        $id_admin_module
676                    );
677
678                    $perm &= !$access['edit'];
679                }
680
681                if ($module->get('author') === ModuleRepository::PARTNER_AUTHOR) {
682                    $module->set('type', 'addonsPartner');
683                }
684
685                if ($perm) {
686                    $module->fillLogo();
687                    if ($module->database->get('installed') == 1) {
688                        $modulesList['installed'][] = $modulePresenter->present($module);
689                    } else {
690                        $modulesList['not_installed'][] = $modulePresenter->present($module);
691                    }
692                }
693            }
694        }
695
696        return $modulesList;
697    }
698
699    private function getTopMenuData(array $topMenuData, $activeMenu = null)
700    {
701        if (isset($activeMenu)) {
702            if (!isset($topMenuData[$activeMenu])) {
703                throw new Exception(sprintf('Menu \'%s\' not found in Top Menu data', $activeMenu), 1);
704            }
705
706            $topMenuData[$activeMenu]->class = 'active';
707        }
708
709        return $topMenuData;
710    }
711
712    /**
713     * @param Request $request
714     *
715     * @return JsonResponse
716     */
717    private function getDisabledFunctionalityResponse(Request $request)
718    {
719        $content = [
720            $request->get('module_name') => [
721                'status' => false,
722                'msg' => $this->getDemoModeErrorMessage(),
723            ],
724        ];
725
726        return new JsonResponse($content);
727    }
728
729    /**
730     * Construct Json struct for catalog body response.
731     *
732     * @param array $categories
733     * @param array $modules
734     *
735     * @return array
736     */
737    private function constructJsonCatalogBodyResponse(
738        array $categories,
739        array $modules
740    ) {
741        $formattedContent = [];
742        $formattedContent['selector'] = '.module-catalog-page';
743        $formattedContent['content'] = $this->render(
744            '@PrestaShop/Admin/Module/Includes/sorting.html.twig',
745            [
746                'totalModules' => count($modules),
747            ]
748        )->getContent();
749
750        $errorMessage = $this->trans('You do not have permission to add this.', 'Admin.Notifications.Error');
751
752        $formattedContent['content'] .= $this->render(
753            '@PrestaShop/Admin/Module/catalog-refresh.html.twig',
754            [
755                'categories' => $categories['categories'],
756                'requireAddonsSearch' => true,
757                'level' => $this->authorizationLevel(self::CONTROLLER_NAME),
758                'errorMessage' => $errorMessage,
759            ]
760        )->getContent();
761
762        return $formattedContent;
763    }
764
765    /**
766     * Construct json struct from top menu.
767     *
768     * @param array $categories
769     *
770     * @return array
771     */
772    private function constructJsonCatalogCategoriesMenuResponse(array $categories)
773    {
774        $formattedContent = [];
775        $formattedContent['selector'] = '.module-menu-item';
776        $formattedContent['content'] = $this->render(
777            '@PrestaShop/Admin/Module/Includes/dropdown_categories_catalog.html.twig',
778            [
779                'topMenuData' => $this->getTopMenuData($categories),
780            ]
781        )->getContent();
782
783        return $formattedContent;
784    }
785
786    /**
787     * Check user permission.
788     *
789     * @param array $pageVoter
790     *
791     * @return JsonResponse|null
792     */
793    private function checkPermissions(array $pageVoter)
794    {
795        if (!in_array(
796            $this->authorizationLevel(self::CONTROLLER_NAME),
797            $pageVoter
798        )
799        ) {
800            return new JsonResponse(
801                [
802                    'status' => false,
803                    'msg' => $this->trans('You do not have permission to add this.', 'Admin.Notifications.Error'),
804                ]
805            );
806        }
807
808        return null;
809    }
810
811    /**
812     * @param string $pageVoter
813     *
814     * @return JsonResponse|null
815     */
816    private function checkPermission($pageVoter)
817    {
818        if (!$this->isGranted($pageVoter, self::CONTROLLER_NAME)) {
819            return new JsonResponse(
820                [
821                    'status' => false,
822                    'msg' => $this->trans('You do not have permission to add this.', 'Admin.Notifications.Error'),
823                ]
824            );
825        }
826
827        return null;
828    }
829
830    /**
831     * Get categories and its modules.
832     *
833     * @param array $modules List of installed modules
834     *
835     * @return array
836     */
837    private function getCategories(AdminModuleDataProvider $modulesProvider, array $modules)
838    {
839        /** @var CategoriesProvider $categoriesProvider */
840        $categoriesProvider = $this->get('prestashop.categories_provider');
841        $categories = $categoriesProvider->getCategoriesMenu($modules);
842
843        foreach ($categories['categories']->subMenu as $category) {
844            $collection = AddonsCollection::createFrom($category->modules);
845            $modulesProvider->generateAddonsUrls($collection);
846            $category->modules = $this->get('prestashop.adapter.presenter.module')
847                ->presentCollection($category->modules);
848        }
849
850        return $categories;
851    }
852}
853