text: <?php
  
  declare(strict_types=1);
  
  /*
   * This file is part of the Zikula package.
   *
   * Copyright Zikula - https://ziku.la/
   *
   * For the full copyright and license information, please view the LICENSE
   * file that was distributed with this source code.
   */
  
  namespace Zikula\AdminModule\Controller;
  
  use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
  use Symfony\Component\HttpFoundation\RedirectResponse;
  use Symfony\Component\HttpFoundation\Request;
  use Symfony\Component\HttpFoundation\Response;
  use Symfony\Component\Routing\Annotation\Route;
  use Symfony\Component\Routing\Exception\RouteNotFoundException;
  use Symfony\Component\Routing\RouterInterface;
  use Symfony\Component\Security\Core\Exception\AccessDeniedException;
  use Zikula\AdminModule\Entity\AdminCategoryEntity;
  use Zikula\AdminModule\Entity\RepositoryInterface\AdminCategoryRepositoryInterface;
  use Zikula\AdminModule\Entity\RepositoryInterface\AdminModuleRepositoryInterface;
  use Zikula\AdminModule\Form\Type\AdminCategoryType;
  use Zikula\AdminModule\Helper\AdminLinksHelper;
  use Zikula\Bundle\CoreBundle\Controller\AbstractController;
  use Zikula\Bundle\CoreBundle\Doctrine\PaginatorInterface;
  use Zikula\Bundle\CoreBundle\HttpKernel\ZikulaHttpKernelInterface;
  use Zikula\Bundle\FormExtensionBundle\Form\Type\DeletionType;
  use Zikula\ExtensionsModule\Api\ApiInterface\CapabilityApiInterface;
  use Zikula\MenuModule\ExtensionMenu\ExtensionMenuCollector;
  use Zikula\MenuModule\ExtensionMenu\ExtensionMenuInterface;
  use Zikula\PermissionsModule\Annotation\PermissionCheck;
  use Zikula\ThemeModule\Engine\Annotation\Theme;
  
  /**
   * Administrative controllers for the admin module
   * NOTE: intentionally no class level route setting here
   */
  class AdminController extends AbstractController
  {
      /**
       * @Route("")
       *
       * The main administration function.
       */
      public function index(): RedirectResponse
      {
          // Security check will be done in view()
          return $this->redirectToRoute('zikulaadminmodule_admin_view');
      }
  
      /**
       * @Route("/categories/{page}", methods = {"GET"}, requirements={"page" = "\d+"})
       * @PermissionCheck("edit")
       * @Theme("admin")
       * @Template("@ZikulaAdminModule/Admin/view.html.twig")
       *
       * Views all admin categories.
       */
      public function view(AdminCategoryRepositoryInterface $repository, int $page = 1): array
      {
          $pageSize = $this->getVar('itemsperpage');
  
          /** @var PaginatorInterface $paginator */
          $paginator = $repository->getPagedCategories(['sortorder' => 'ASC'], $page, $pageSize);
          $paginator->setRoute('zikulaadminmodule_admin_view');
  
          return [
              'paginator' => $paginator
          ];
      }
  
      /**
       * @Route("/newcategory")
       * @PermissionCheck({"$_zkModule::Item", "::", "add"})
       * @Theme("admin")
       * @Template("@ZikulaAdminModule/Admin/editCategory.html.twig")
       *
       * Displays and handles a form for the user to input the details of the new category.
       *
       * @return array|RedirectResponse
       */
      public function create(Request $request)
      {
          $form = $this->createForm(AdminCategoryType::class, new AdminCategoryEntity());
          $form->handleRequest($request);
          if ($form->isSubmitted() && $form->isValid()) {
              if ($form->get('save')->isClicked()) {
                  $adminCategory = $form->getData();
                  $this->getDoctrine()->getManager()->persist($adminCategory);
                  $this->getDoctrine()->getManager()->flush();
                  $this->addFlash('status', 'Done! Created new category.');
              } elseif ($form->get('cancel')->isClicked()) {
                  $this->addFlash('status', 'Operation cancelled.');
              }
  
              return $this->redirectToRoute('zikulaadminmodule_admin_view');
          }
  
          return [
              'form' => $form->createView()
          ];
      }
  
      /**
       * @Route("/modifycategory/{cid}", requirements={"cid" = "^[1-9]\d*$"})
       * @Theme("admin")
       * @Template("@ZikulaAdminModule/Admin/editCategory.html.twig")
       *
       * Displays and handles a modify category form.
       *
       * @return array|RedirectResponse
       * @throws AccessDeniedException Thrown if the user doesn't have permission to edit a category
       */
      public function modify(Request $request, AdminCategoryEntity $category)
      {
          if (!$this->hasPermission('ZikulaAdminModule::Category', $category['name'] . '::' . $category->getCid(), ACCESS_EDIT)) {
              throw new AccessDeniedException();
          }
  
          $form = $this->createForm(AdminCategoryType::class, $category);
          $form->handleRequest($request);
          if ($form->isSubmitted() && $form->isValid()) {
              if ($form->get('save')->isClicked()) {
                  $category = $form->getData();
                  if (!$this->hasPermission('ZikulaAdminModule::Category', $category->getName() . '::' . $category->getCid(), ACCESS_EDIT)) {
                      throw new AccessDeniedException();
                  }
                  $this->getDoctrine()->getManager()->flush();
                  $this->addFlash('status', 'Done! Saved category.');
              } elseif ($form->get('cancel')->isClicked()) {
                  $this->addFlash('status', 'Operation cancelled.');
              }
  
              return $this->redirectToRoute('zikulaadminmodule_admin_view');
          }
  
          return [
              'form' => $form->createView()
          ];
      }
  
      /**
       * @Route("/deletecategory/{cid}", requirements={"cid" = "^[1-9]\d*$"})
       * @Theme("admin")
       * @Template("@ZikulaAdminModule/Admin/delete.html.twig")
       *
       * Deletes an admin category.
       *
       * @return array|RedirectResponse
       * @throws AccessDeniedException Thrown if the user doesn't have permission to edit a category
       */
      public function delete(Request $request, AdminCategoryEntity $category)
      {
          if (!$this->hasPermission('ZikulaAdminModule::Category', $category->getName() . '::' . $category->getCid(), ACCESS_DELETE)) {
              throw new AccessDeniedException();
          }
  
          $form = $this->createForm(DeletionType::class, $category);
          $form->handleRequest($request);
          if ($form->isSubmitted() && $form->isValid()) {
              if ($form->get('delete')->isClicked()) {
                  $category = $form->getData();
                  $this->getDoctrine()->getManager()->remove($category);
                  $this->getDoctrine()->getManager()->flush();
                  $this->addFlash('status', 'Done! Category deleted.');
              } elseif ($form->get('cancel')->isClicked()) {
                  $this->addFlash('status', 'Operation cancelled.');
              }
  
              return $this->redirectToRoute('zikulaadminmodule_admin_view');
          }
  
          return [
              'form' => $form->createView(),
              'category' => $category
          ];
      }
  
      /**
       * @Route("/panel/{acid}", methods = {"GET"}, requirements={"acid" = "^[1-9]\d*$"})
       * @PermissionCheck("edit")
       * @Theme("admin")
       * @Template("@ZikulaAdminModule/Admin/adminpanel.html.twig")
       *
       * Displays main admin panel for a category.
       *
       * @return array|Response
       * @throws AccessDeniedException Thrown if the user doesn't have edit permission for the module
       */
      public function adminpanel(
          ZikulaHttpKernelInterface $kernel,
          AdminCategoryRepositoryInterface $adminCategoryRepository,
          AdminModuleRepositoryInterface $adminModuleRepository,
          CapabilityApiInterface $capabilityApi,
          RouterInterface $router,
          AdminLinksHelper $adminLinksHelper,
          ExtensionMenuCollector $extensionMenuCollector,
          int $acid = null
      ) {
          if (empty($acid)) {
              $acid = $this->getVar('startcategory', 1);
          }
  
          // Check to see if we have access to the requested category.
          if (!$this->hasPermission('ZikulaAdminModule::', "::${acid}", ACCESS_ADMIN)) {
              $acid = -1;
          }
  
          // Get details for selected category
          $category = null;
          if ($acid > 0) {
              $category = $adminCategoryRepository->findOneBy(['cid' => $acid]);
          }
  
          if (!$category) {
              // get the default category
              $acid = $this->getVar('startcategory');
  
              // Check to see if we have access to the requested category.
              if (!$this->hasPermission('ZikulaAdminModule::', "::${acid}", ACCESS_ADMIN)) {
                  throw new AccessDeniedException();
              }
  
              $category = $adminCategoryRepository->findOneBy(['cid' => $acid]);
          }
  
          $templateParameters = [];
  
          // assign the category
          $templateParameters['category'] = $category;
  
          $displayNameType = $this->getVar('displaynametype', 1);
          $moduleEntities = $adminModuleRepository->findAll();
  
          // get admin capable modules
          $adminModules = $capabilityApi->getExtensionsCapableOf('admin');
          $adminLinks = [];
          foreach ($adminModules as $adminModule) {
              if (!$this->hasPermission($adminModule['name'] . '::', 'ANY', ACCESS_EDIT)) {
                  continue;
              }
  
              $moduleId = (int)$adminModule['id'];
              $moduleCategory = $adminCategoryRepository->getModuleCategory($moduleId);
              $catid = null !== $moduleCategory ? $moduleCategory['cid'] : 0;
  
              $sortOrder = -1;
              foreach ($moduleEntities as $association) {
                  if ($association['mid'] !== $adminModule['id']) {
                      continue;
                  }
  
                  $sortOrder = $association['sortorder'];
                  break;
              }
  
              if ($catid === $acid || (false === $catid && $acid === $this->getVar('defaultcategory'))) {
                  $menuText = '';
                  if (1 === $displayNameType) {
                      $menuText = $adminModule['displayname'];
                  } elseif (2 === $displayNameType) {
                      $menuText = $adminModule['name'];
                  } elseif (3 === $displayNameType) {
                      $menuText = $adminModule['displayname'] . ' (' . $adminModule['name'] . ')';
                  }
  
                  try {
                      $menuTextUrl = isset($adminModule['capabilities']['admin']['route'])
                          ? $router->generate($adminModule['capabilities']['admin']['route'])
                          : '';
                  } catch (RouteNotFoundException $routeNotFoundException) {
                      $menuTextUrl = 'javascript:void(0)';
                      $menuText .= ' (<i class="fas fa-exclamation-triangle"></i> ' . $this->trans('invalid route') . ')';
                  }
  
                  $moduleName = (string)$adminModule['name'];
                  /** @var \Knp\Menu\ItemInterface $extensionMenu */
                  $extensionMenu = $extensionMenuCollector->get($moduleName, ExtensionMenuInterface::TYPE_ADMIN);
                  if (isset($extensionMenu)) {
                      $extensionMenu->setChildrenAttribute('class', 'dropdown-menu');
                  }
  
                  $adminLinks[] = [
                      'menuTextUrl' => $menuTextUrl,
                      'menuText' => $menuText,
                      'menuTextTitle' => $adminModule['description'],
                      'moduleName' => $adminModule['name'],
                      'adminIcon' => $adminModule['icon'],
                      'id' => $adminModule['id'],
                      'order' => $sortOrder,
                      'extensionMenu' => $extensionMenu
                  ];
              }
          }
          $templateParameters['adminLinks'] = $adminLinksHelper->sortAdminModsByOrder($adminLinks);
  
          return $templateParameters;
      }
  
      /**
       * @Route("/categorymenu/{acid}", methods = {"GET"}, requirements={"acid" = "^[1-9]\d*$"})
       * @Theme("admin")
       *
       * Displays main category menu.
       */
      public function categorymenu(
          AdminCategoryRepositoryInterface $adminCategoryRepository,
          AdminModuleRepositoryInterface $adminModuleRepository,
          CapabilityApiInterface $capabilityApi,
          RouterInterface $router,
          AdminLinksHelper $adminLinksHelper,
          int $acid = null
      ): Response {
          $acid = empty($acid) ? $this->getVar('startcategory') : $acid;
  
          // Get all categories
          $categories = [];
          $items = $adminCategoryRepository->findBy([], ['sortorder' => 'ASC']);
          foreach ($items as $item) {
              if ($this->hasPermission('ZikulaAdminModule::', $item['name'] . '::' . $item['cid'], ACCESS_READ)) {
                  $categories[] = $item;
              }
          }
  
          $moduleEntities = $adminModuleRepository->findAll();
  
          // get admin capable modules
          $adminModules = $capabilityApi->getExtensionsCapableOf('admin');
          $adminLinks = [];
          foreach ($adminModules as $adminModule) {
              if (!$this->hasPermission($adminModule['name'] . '::', '::', ACCESS_EDIT)) {
                  continue;
              }
  
              $menuText = $adminModule['displayname'];
              try {
                  $menuTextUrl = isset($adminModule['capabilities']['admin']['route'])
                      ? $router->generate($adminModule['capabilities']['admin']['route'])
                      : '';
              } catch (RouteNotFoundException $routeNotFoundException) {
                  $menuTextUrl = 'javascript:void(0)';
                  $menuText .= ' (<i class="fas fa-exclamation-triangle"></i> ' . $this->trans('invalid route') . ')';
              }
  
              $moduleId = (int)$adminModule['id'];
              $moduleCategory = $adminCategoryRepository->getModuleCategory($moduleId);
              $catid = null !== $moduleCategory ? $moduleCategory['cid'] : 0;
  
              $sortOrder = -1;
              foreach ($moduleEntities as $association) {
                  if ($association['mid'] !== $adminModule['id']) {
                      continue;
                  }
  
                  $sortOrder = $association['sortorder'];
                  break;
              }
  
              $adminLinks[$catid][] = [
                  'menuTextUrl' => $menuTextUrl,
                  'menuText' => $menuText,
                  'menuTextTitle' => $adminModule['description'],
                  'moduleName' => $adminModule['name'],
                  'order' => $sortOrder,
                  'id' => $adminModule['id'],
                  'icon' => $adminModule['icon']
              ];
          }
  
          foreach ($adminLinks as $categoryId => $links) {
              $adminLinks[$categoryId] = $adminLinksHelper->sortAdminModsByOrder($links);
          }
  
          $menuOptions = [];
          $possibleCategoryIds = [];
          $permission = false;
  
          if (isset($categories) && is_array($categories)) {
              foreach ($categories as $category) {
                  // only categories containing modules where the current user has permissions will
                  // be shown, all others will be hidden
                  // admin will see all categories
                  if ($this->hasPermission('.*', '.*', ACCESS_ADMIN)
                      || (isset($adminLinks[$category['cid']]) && count($adminLinks[$category['cid']]))
                  ) {
                      $menuOption = [
                          'url' => $router->generate('zikulaadminmodule_admin_adminpanel', ['acid' => $category['cid']]),
                          'title' => $category['name'],
                          'description' => $category['description'],
                          'cid' => $category['cid'],
                          'items' => $adminLinks[$category['cid']] ?? []
                      ];
  
                      $menuOptions[$category['cid']] = $menuOption;
                      $possibleCategoryIds[] = $category['cid'];
  
                      if ($acid === $category['cid']) {
                          $permission = true;
                      }
                  }
              }
          }
  
          // if permission is false we are not allowed to see this category because its
          // empty and we are not admin
          if (false === $permission) {
              // show the first category
              $acid = !empty($possibleCategoryIds) ? (int)$possibleCategoryIds[0] : null;
          }
  
          return $this->render('@ZikulaAdminModule/Admin/categoryMenu.html.twig', [
              'currentCategory' => $acid,
              'menuOptions' => $menuOptions
          ]);
      }
  }
  