diff --git a/src/Kunstmaan/NodeBundle/AdminList/DeletedNodeAdminListConfigurator.php b/src/Kunstmaan/NodeBundle/AdminList/DeletedNodeAdminListConfigurator.php new file mode 100644 index 0000000000..5bf6ccdbfa --- /dev/null +++ b/src/Kunstmaan/NodeBundle/AdminList/DeletedNodeAdminListConfigurator.php @@ -0,0 +1,35 @@ +addFilter('title', new StringFilterType('title'), 'kuma_node.admin.list.filter.title') + ->addFilter('created', new DateFilterType('created'), 'kuma_node.admin.list.filter.created_at') + ->addFilter('updated', new DateFilterType('updated'), 'kuma_node.admin.list.filter.updated_at') + ; + } + + public function buildFields() + { + $this + ->addField('title', 'kuma_node.admin.list.header.title', true, '@KunstmaanNode/Admin/title.html.twig') + ->addField('created', 'kuma_node.admin.list.header.created_at', true) + ->addField('updated', 'kuma_node.admin.list.header.updated_at', true) + ; + } + + public function adaptQueryBuilder(QueryBuilder $queryBuilder) + { + parent::adaptQueryBuilder($queryBuilder); + + $queryBuilder->setParameter('deleted', 1); + } +} diff --git a/src/Kunstmaan/NodeBundle/AdminList/NodeAdminListConfigurator.php b/src/Kunstmaan/NodeBundle/AdminList/NodeAdminListConfigurator.php index b5d3c35c07..5d0e880809 100644 --- a/src/Kunstmaan/NodeBundle/AdminList/NodeAdminListConfigurator.php +++ b/src/Kunstmaan/NodeBundle/AdminList/NodeAdminListConfigurator.php @@ -2,7 +2,9 @@ namespace Kunstmaan\NodeBundle\AdminList; +use Doctrine\Common\Collections\Criteria; use Doctrine\ORM\EntityManager; +use Doctrine\ORM\Query\Expr\Join; use Doctrine\ORM\QueryBuilder; use Kunstmaan\AdminBundle\Helper\DomainConfigurationInterface; use Kunstmaan\AdminBundle\Helper\Security\Acl\AclHelper; @@ -62,14 +64,14 @@ public function __construct(EntityManager $em, AclHelper $aclHelper, $locale, $p $this->setPermissionDefinition( new PermissionDefinition( array($permission), - 'Kunstmaan\NodeBundle\Entity\Node', + Node::class, 'n' ) ); } /** - * @param \Kunstmaan\AdminBundle\Helper\DomainConfigurationInterface $domainConfiguration + * @param DomainConfigurationInterface $domainConfiguration */ public function setDomainConfiguration(DomainConfigurationInterface $domainConfiguration) { @@ -241,11 +243,13 @@ public function adaptQueryBuilder(QueryBuilder $queryBuilder) $queryBuilder ->select('b,n') - ->innerJoin('b.node', 'n', 'WITH', 'b.node = n.id') + ->innerJoin('b.node', 'n', Join::WITH, 'b.node = n.id') ->andWhere('b.lang = :lang') - ->andWhere('n.deleted = 0') - ->addOrderBy('b.updated', 'DESC') - ->setParameter('lang', $this->locale); + ->andWhere('n.deleted = :deleted') + ->addOrderBy('b.updated', Criteria::DESC) + ->setParameter('lang', $this->locale) + ->setParameter('deleted', 0) + ; if (!$this->domainConfiguration) { return; diff --git a/src/Kunstmaan/NodeBundle/Controller/NodeAdminController.php b/src/Kunstmaan/NodeBundle/Controller/NodeAdminController.php index 9886989d55..3c067e0af8 100644 --- a/src/Kunstmaan/NodeBundle/Controller/NodeAdminController.php +++ b/src/Kunstmaan/NodeBundle/Controller/NodeAdminController.php @@ -16,6 +16,8 @@ use Kunstmaan\AdminBundle\Helper\Security\Acl\Permission\PermissionMap; use Kunstmaan\AdminBundle\Service\AclManager; use Kunstmaan\AdminListBundle\AdminList\AdminList; +use Kunstmaan\AdminListBundle\AdminList\ListAction\SimpleListAction; +use Kunstmaan\NodeBundle\AdminList\DeletedNodeAdminListConfigurator; use Kunstmaan\NodeBundle\AdminList\NodeAdminListConfigurator; use Kunstmaan\NodeBundle\Entity\HasNodeInterface; use Kunstmaan\NodeBundle\Entity\Node; @@ -91,9 +93,16 @@ class NodeAdminController extends Controller */ protected $translator; - /** @var PageCloningHelper */ + /** + * @var PageCloningHelper + */ private $pageCloningHelper; + /** + * @var bool + */ + private $isUndoDeletingNodesEnabled; + /** * init * @@ -110,6 +119,9 @@ protected function init(Request $request) $this->nodePublisher = $this->container->get('kunstmaan_node.admin_node.publisher'); $this->translator = $this->container->get('translator'); $this->pageCloningHelper = $this->container->get(PageCloningHelper::class); + $this->isUndoDeletingNodesEnabled = $this->container->getParameter( + 'kunstmaan_node.kunstmaan_node.enable_undo_deleting_nodes' + ); } /** @@ -141,17 +153,37 @@ public function indexAction(Request $request) 'params' => ['_locale' => $locale, 'url' => $item->getUrl()], ); } + + return null; }; + $nodeAdminListConfigurator->addSimpleItemAction('action.preview', $itemRoute, 'eye'); $nodeAdminListConfigurator->setDomainConfiguration($this->get('kunstmaan_admin.domain_configuration')); - $nodeAdminListConfigurator->setShowAddHomepage($this->getParameter('kunstmaan_node.show_add_homepage') && $this->isGranted('ROLE_SUPER_ADMIN')); + $nodeAdminListConfigurator->setShowAddHomepage( + $this->getParameter('kunstmaan_node.show_add_homepage') && $this->isGranted('ROLE_SUPER_ADMIN') + ); + + $this->addViewDeletedNodesAction($nodeAdminListConfigurator); + + return $this->renderAdminList($request, $nodeAdminListConfigurator); + } - /** @var AdminList $adminlist */ - $adminlist = $this->get('kunstmaan_adminlist.factory')->createList($nodeAdminListConfigurator); - $adminlist->bindRequest($request); + private function addViewDeletedNodesAction(NodeAdminListConfigurator $nodeAdminListConfigurator): void + { + if (!$this->isUndoDeletingNodesEnabled) { + return; + } - return array( - 'adminlist' => $adminlist, + $nodeAdminListConfigurator->addListAction( + new SimpleListAction( + [ + 'path' => 'KunstmaanNodeBundle_deleted_nodes', + 'params' => [], + ], + 'deleted_pages.view_action', + null, + '@KunstmaanAdmin/Settings/button_resolve_all.html.twig' + ) ); } @@ -348,7 +380,6 @@ public function unPublishAction(Request $request, $id) $node = $this->em->getRepository(Node::class)->find($id); $nodeTranslation = $node->getNodeTranslation($this->locale, true); - $request = $this->get('request_stack')->getCurrentRequest(); $this->nodePublisher->chooseHowToUnpublish($request, $nodeTranslation, $this->translator); return $this->redirect($this->generateUrl('KunstmaanNodeBundle_nodes_edit', array('id' => $node->getId()))); @@ -452,6 +483,101 @@ public function deleteAction(Request $request, $id) return $response; } + /** + * @Route("/deleted", name="KunstmaanNodeBundle_deleted_nodes") + * @Template("@KunstmaanNode/Admin/deleted_list.html.twig") + */ + public function deletedNodesAction(Request $request): array + { + $this->init($request); + + if (!$this->isUndoDeletingNodesEnabled) { + throw $this->createAccessDeniedException(); + } + + $nodeAdminListConfigurator = new DeletedNodeAdminListConfigurator( + $this->em, + $this->aclHelper, + $this->locale, + PermissionMap::PERMISSION_DELETE, + $this->authorizationChecker + ); + $nodeAdminListConfigurator->addListAction( + new SimpleListAction( + [ + 'path' => 'KunstmaanNodeBundle_nodes', + 'params' => [], + ], + 'pages.view_action', + null, + '@KunstmaanAdmin/Settings/button_resolve_all.html.twig' + ) + ); + $nodeAdminListConfigurator->setDomainConfiguration($this->get('kunstmaan_admin.domain_configuration')); + $locale = $this->locale; + $acl = $this->authorizationChecker; + + $nodeAdminListConfigurator->addSimpleItemAction( + 'action.undo_delete', + function (EntityInterface $item) use ($locale, $acl) { + if ($acl->isGranted(PermissionMap::PERMISSION_DELETE, $item->getNode())) { + return [ + 'path' => 'KunstmaanNodeBundle_nodes_delete_undo', + 'params' => [ + '_locale' => $locale, + 'id' => $item->getNode()->getId(), + ], + ]; + } + + return null; + }, + 'fa fa-undo', + '@KunstmaanNode\Admin\undo_delete_button.html.twig' + ); + + return $this->renderAdminList($request, $nodeAdminListConfigurator); + } + + /** + * @Route( + * "/{id}/delete/undo", + * requirements={"id" = "\d+"}, + * name="KunstmaanNodeBundle_nodes_delete_undo", + * ) + */ + public function undoDeleteAction(Request $request, int $id): RedirectResponse + { + $this->init($request); + + if (!$this->isUndoDeletingNodesEnabled) { + throw $this->createAccessDeniedException(); + } + + /* @var Node $node */ + $node = $this->em->getRepository(Node::class)->find($id); + + try { + $this->undoDeleteNode($node); + + $this->em->flush(); + + $this->addFlash( + FlashTypes::SUCCESS, + $this->get('translator')->trans('kuma_node.admin.undo_delete.flash.success') + ); + } catch (AccessDeniedException $exception) { + $this->addFlash( + FlashTypes::SUCCESS, + $this->get('translator')->trans('kuma_node.admin.undo_delete.flash.error') + ); + } + + return $this->redirectToRoute( + 'KunstmaanNodeBundle_deleted_nodes' + ); + } + /** * @Route( * "/{id}/duplicate", @@ -882,6 +1008,13 @@ public function editAction(Request $request, $id, $subaction) $nodeVersion = $draftNodeVersion; $page = $nodeVersion->getRef($this->em); } else { + if (!empty($request->request->get('undo_delete'))) { + $node->setDeleted(false); + + $this->em->persist($node); + $this->em->flush(); + } + if ($request->getMethod() == 'POST') { $nodeVersionIsLocked = $this->isNodeVersionLocked($nodeTranslation, true); @@ -1220,6 +1353,34 @@ private function deleteNodeChildren( } } + private function undoDeleteNode(Node $node): void + { + if (!$node->isDeleted()) { + return; + } + + $this->denyAccessUnlessGranted( + PermissionMap::PERMISSION_DELETE, + $node + ); + + $node->setDeleted(false); + + foreach ($node->getNodeTranslations() as $nodeTranslation) { + if (!$nodeTranslation instanceof NodeTranslation) { + continue; + } + + $this->nodePublisher->unPublish($nodeTranslation); + } + + $this->em->persist($node); + + foreach ($node->getChildren() as $child) { + $this->undoDeleteNode($child); + } + } + /** * @param Request $request * @param string $type @@ -1321,4 +1482,17 @@ private function dispatch($event, string $eventName) return $eventDispatcher->dispatch($eventName, $event); } + + private function renderAdminList( + Request $request, + NodeAdminListConfigurator $nodeAdminListConfigurator + ): array { + /** @var AdminList $adminList */ + $adminList = $this->get('kunstmaan_adminlist.factory')->createList($nodeAdminListConfigurator); + $adminList->bindRequest($request); + + return [ + 'adminlist' => $adminList, + ]; + } } diff --git a/src/Kunstmaan/NodeBundle/DependencyInjection/Configuration.php b/src/Kunstmaan/NodeBundle/DependencyInjection/Configuration.php index 6014dc3f7f..a6476dd2a1 100644 --- a/src/Kunstmaan/NodeBundle/DependencyInjection/Configuration.php +++ b/src/Kunstmaan/NodeBundle/DependencyInjection/Configuration.php @@ -61,6 +61,7 @@ public function getConfigTreeBuilder() ->booleanNode('show_add_homepage')->defaultTrue()->end() ->booleanNode('show_duplicate_with_children')->defaultFalse()->end() ->booleanNode('enable_export_page_template')->defaultFalse()->end() + ->booleanNode('enable_undo_deleting_nodes')->defaultTrue()->end() ->arrayNode('lock') ->addDefaultsIfNotSet() ->canBeEnabled() diff --git a/src/Kunstmaan/NodeBundle/DependencyInjection/KunstmaanNodeExtension.php b/src/Kunstmaan/NodeBundle/DependencyInjection/KunstmaanNodeExtension.php index fff347a8eb..f65c3d1089 100644 --- a/src/Kunstmaan/NodeBundle/DependencyInjection/KunstmaanNodeExtension.php +++ b/src/Kunstmaan/NodeBundle/DependencyInjection/KunstmaanNodeExtension.php @@ -39,6 +39,7 @@ public function load(array $configs, ContainerBuilder $container) $container->setParameter('kunstmaan_node.show_add_homepage', $config['show_add_homepage']); $container->setParameter('kunstmaan_node.show_duplicate_with_children', $config['show_duplicate_with_children']); $container->setParameter('kunstmaan_node.enable_export_page_template', $config['enable_export_page_template']); + $container->setParameter('kunstmaan_node.enable_undo_deleting_nodes', $config['enable_undo_deleting_nodes']); $container->setParameter('kunstmaan_node.lock_check_interval', $config['lock']['check_interval']); $container->setParameter('kunstmaan_node.lock_threshold', $config['lock']['threshold']); $container->setParameter('kunstmaan_node.lock_enabled', $config['lock']['enabled']); diff --git a/src/Kunstmaan/NodeBundle/Helper/Menu/ActionsMenuBuilder.php b/src/Kunstmaan/NodeBundle/Helper/Menu/ActionsMenuBuilder.php index b3dcff1578..92f1b54e90 100644 --- a/src/Kunstmaan/NodeBundle/Helper/Menu/ActionsMenuBuilder.php +++ b/src/Kunstmaan/NodeBundle/Helper/Menu/ActionsMenuBuilder.php @@ -65,9 +65,16 @@ class ActionsMenuBuilder */ private $enableExportPageTemplate; - /** @var bool */ + /** + * @var bool + */ private $showDuplicateWithChildren; + /** + * @var bool + */ + private $enableUndoDeletingNodes; + /** * @param FactoryInterface $factory * @param EntityManager $em @@ -77,6 +84,7 @@ class ActionsMenuBuilder * @param PagesConfiguration $pagesConfiguration * @param bool $enableExportPageTemplate * @param bool $showDuplicateWithChildren + * @param bool $enableUndoDeletingNodes */ public function __construct( FactoryInterface $factory, @@ -86,7 +94,8 @@ public function __construct( AuthorizationCheckerInterface $authorizationChecker, PagesConfiguration $pagesConfiguration, $enableExportPageTemplate = true, - bool $showDuplicateWithChildren = false + bool $showDuplicateWithChildren = false, + bool $enableUndoDeletingNodes = true ) { $this->factory = $factory; $this->em = $em; @@ -96,6 +105,7 @@ public function __construct( $this->pagesConfiguration = $pagesConfiguration; $this->enableExportPageTemplate = $enableExportPageTemplate; $this->showDuplicateWithChildren = $showDuplicateWithChildren; + $this->enableUndoDeletingNodes = $enableUndoDeletingNodes; } /** @@ -418,20 +428,36 @@ public function createActionsMenu() $node ) ) { - $menu->addChild( - 'action.delete', - [ - 'linkAttributes' => [ - 'type' => 'button', - 'class' => 'btn btn-default btn--raise-on-hover', - 'onClick' => 'oldEdited = isEdited; isEdited=false', - 'data-toggle' => 'modal', - 'data-keyboard' => 'true', - 'data-target' => '#delete-page-modal', - ], - 'extras' => ['renderType' => 'button'], - ] - ); + if (!$node->isDeleted()) { + $menu->addChild( + 'action.delete', + [ + 'linkAttributes' => [ + 'type' => 'button', + 'class' => 'btn btn-default btn--raise-on-hover', + 'onClick' => 'oldEdited = isEdited; isEdited=false', + 'data-toggle' => 'modal', + 'data-keyboard' => 'true', + 'data-target' => '#delete-page-modal', + ], + 'extras' => ['renderType' => 'button'], + ] + ); + } elseif ($this->enableUndoDeletingNodes) { + $menu->addChild( + 'action.undo_delete', + [ + 'linkAttributes' => [ + 'type' => 'button', + 'class' => 'btn btn-default btn--raise-on-hover', + 'data-toggle' => 'modal', + 'data-keyboard' => 'true', + 'data-target' => '#undo-delete-page-modal', + ], + 'extras' => ['renderType' => 'button'], + ] + ); + } } $this->dispatch( diff --git a/src/Kunstmaan/NodeBundle/Resources/config/services.yml b/src/Kunstmaan/NodeBundle/Resources/config/services.yml index 13c3bdbbb1..2e70f714e9 100644 --- a/src/Kunstmaan/NodeBundle/Resources/config/services.yml +++ b/src/Kunstmaan/NodeBundle/Resources/config/services.yml @@ -100,6 +100,7 @@ services: - '@kunstmaan_node.pages_configuration' - '%kunstmaan_node.enable_export_page_template%' - '%kunstmaan_node.show_duplicate_with_children%' + - '%kunstmaan_node.enable_undo_deleting_nodes%' public: true kunstmaan_node.menu.sub_actions: diff --git a/src/Kunstmaan/NodeBundle/Resources/translations/messages.en.yml b/src/Kunstmaan/NodeBundle/Resources/translations/messages.en.yml index 7dca63cd67..0b6add6aca 100644 --- a/src/Kunstmaan/NodeBundle/Resources/translations/messages.en.yml +++ b/src/Kunstmaan/NodeBundle/Resources/translations/messages.en.yml @@ -13,11 +13,13 @@ action: addsubpage: "Add subpage" duplicate: "Duplicate" duplicate_with_children: "Duplicate with sub pages" + undo_delete: "Undo deleting page" modal: sourcelanguage: "Source language" pages: + view_action: 'View pages' copynotavailable: "Please translate the parent page first" kuma_node: @@ -82,6 +84,10 @@ kuma_node: success: The page has been edited locked: The page is currently being edited by %users% locked_success: An automatic backup has been created because the page is being edited by another user + undo_delete: + flash: + success: Deleting the page has been undone + error: There was an error while undoing deleting the page new_page: title: default: New page @@ -166,6 +172,9 @@ kuma_node: button: delete: Delete cancel: Cancel + undo_delete_page: + 'title.%page%': Undo deleting page '%page%' + 'body.%page%': This will undo deleting this page in all languages! Are you really sure about this? duplicate_page: 'title.%page%': Duplicate page '%page%' label: @@ -189,3 +198,8 @@ toolbar: node: title: 'Page title' edit: 'Edit page' + +deleted_pages: + title: 'Deleted pages' + additional_title: 'This list contains all the deleted pages, chronologically ordered by updated date.' + view_action: 'View deleted pages' diff --git a/src/Kunstmaan/NodeBundle/Resources/translations/messages.nl.yml b/src/Kunstmaan/NodeBundle/Resources/translations/messages.nl.yml index d12ad9816b..8d995db9d1 100644 --- a/src/Kunstmaan/NodeBundle/Resources/translations/messages.nl.yml +++ b/src/Kunstmaan/NodeBundle/Resources/translations/messages.nl.yml @@ -12,9 +12,11 @@ action: delete: "Verwijder" addsubpage: "Subpagina toevoegen" duplicate: "Dupliceer" + undo_delete: "Verwijderen ongedaan maken" modal: sourcelanguage: "Bron taal" pages: + view_action: "Bekijk pagina's" copynotavailable: "Gelieve de bovenliggende pagina eerst te vertalen" kuma_node: form: @@ -78,6 +80,10 @@ kuma_node: success: De pagina is bewerkt locked: De pagina wordt momenteel bewerkt door %users% locked_success: Een automatische back-up is aangemaakt, omdat de pagina is bewerkt door een andere gebruiker + undo_delete: + flash: + success: Het ongedaan maken van het verwijderen van de pagina is gelukt + error: Er is een probleem opgetreden tijdens het ongedaan maken van het verwijderen van de pagina new_page: title: default: Nieuwe pagina @@ -160,6 +166,9 @@ kuma_node: button: delete: Verwijder cancel: Annuleer + undo_delete_page: + 'title.%page%': Verwijderen van de pagina '%page% ongedaan maken' + 'body.%page%': Hiermee maakt u het verwijderen van deze pagina ongedaan in alle talen! Bent u echt zeker over dit? duplicate_page: 'title.%page%': Dupliceer pagina '%page%' label: @@ -177,3 +186,7 @@ kuma_node: widget: choose: Kies url_chooser: URL-keuze +deleted_pages: + title: "Verwijderde pagina's" + additional_title: "Deze lijst bevat alle verwijderde pagina's chronologisch gesorteerd op laatste wijziging." + view_action: "Bekijk verwijderde pagina's" diff --git a/src/Kunstmaan/NodeBundle/Resources/views/Admin/deleted_list.html.twig b/src/Kunstmaan/NodeBundle/Resources/views/Admin/deleted_list.html.twig new file mode 100644 index 0000000000..a2ab23e155 --- /dev/null +++ b/src/Kunstmaan/NodeBundle/Resources/views/Admin/deleted_list.html.twig @@ -0,0 +1,17 @@ +{% extends '@KunstmaanAdminList/Default/list.html.twig' %} + +{% block admin_page_title %} +