From 68cf9171689f70a8e345bb4bf6c80ffff658b669 Mon Sep 17 00:00:00 2001 From: Simon Vieille Date: Thu, 27 May 2021 16:48:03 +0200 Subject: [PATCH] backports murph-skeleton --- assets/css/admin.scss | 6 +++ .../js/{admin => addons}/checkbox-checker.js | 0 assets/js/{admin => addons}/choices.js | 0 assets/js/{admin => addons}/datepicker.js | 0 assets/js/{admin => addons}/dbclick.js | 0 .../js/{admin => addons}/document-selector.js | 0 assets/js/{admin => addons}/editor.js | 0 .../js/{admin => addons}/form-collection.js | 0 assets/js/{admin => addons}/form-confirm.js | 0 assets/js/{admin => addons}/form.js | 0 assets/js/{admin => addons}/modal.js | 0 assets/js/{admin => addons}/panel.js | 0 assets/js/{admin => addons}/password.js | 0 assets/js/{admin => addons}/push-state.js | 0 assets/js/{admin => addons}/rest-choices.js | 0 assets/js/{admin => addons}/simplemde.js | 0 assets/js/addons/sortable.js | 31 +++++++++++ assets/js/{admin => addons}/table-fixed.js | 0 .../js/{admin => addons}/table-selectable.js | 0 assets/js/{admin => addons}/toast.js | 0 assets/js/{admin => addons}/tooltip.js | 0 assets/js/admin.js | 43 +++++++--------- config/packages/app.yaml | 14 ++--- core/Controller/Admin/Crud/CrudController.php | 51 +++++++++++++++++-- core/Crud/CrudConfiguration.php | 26 ++++++++++ .../crud-controller/CrudController.tpl.php | 8 +++ core/Resources/translations/messages.fr.yaml | 1 + .../views/admin/crud/index.html.twig | 38 ++++++++++++-- package.json | 1 + yarn.lock | 5 ++ 30 files changed, 185 insertions(+), 39 deletions(-) rename assets/js/{admin => addons}/checkbox-checker.js (100%) rename assets/js/{admin => addons}/choices.js (100%) rename assets/js/{admin => addons}/datepicker.js (100%) rename assets/js/{admin => addons}/dbclick.js (100%) rename assets/js/{admin => addons}/document-selector.js (100%) rename assets/js/{admin => addons}/editor.js (100%) rename assets/js/{admin => addons}/form-collection.js (100%) rename assets/js/{admin => addons}/form-confirm.js (100%) rename assets/js/{admin => addons}/form.js (100%) rename assets/js/{admin => addons}/modal.js (100%) rename assets/js/{admin => addons}/panel.js (100%) rename assets/js/{admin => addons}/password.js (100%) rename assets/js/{admin => addons}/push-state.js (100%) rename assets/js/{admin => addons}/rest-choices.js (100%) rename assets/js/{admin => addons}/simplemde.js (100%) create mode 100644 assets/js/addons/sortable.js rename assets/js/{admin => addons}/table-fixed.js (100%) rename assets/js/{admin => addons}/table-selectable.js (100%) rename assets/js/{admin => addons}/toast.js (100%) rename assets/js/{admin => addons}/tooltip.js (100%) diff --git a/assets/css/admin.scss b/assets/css/admin.scss index 49c5041..9d729e4 100644 --- a/assets/css/admin.scss +++ b/assets/css/admin.scss @@ -189,6 +189,12 @@ tr.table-primary-light { } } +*[data-sortable-item] { + &:hover { + cursor: pointer; + } +} + .footer { position: absolute; bottom: 0; diff --git a/assets/js/admin/checkbox-checker.js b/assets/js/addons/checkbox-checker.js similarity index 100% rename from assets/js/admin/checkbox-checker.js rename to assets/js/addons/checkbox-checker.js diff --git a/assets/js/admin/choices.js b/assets/js/addons/choices.js similarity index 100% rename from assets/js/admin/choices.js rename to assets/js/addons/choices.js diff --git a/assets/js/admin/datepicker.js b/assets/js/addons/datepicker.js similarity index 100% rename from assets/js/admin/datepicker.js rename to assets/js/addons/datepicker.js diff --git a/assets/js/admin/dbclick.js b/assets/js/addons/dbclick.js similarity index 100% rename from assets/js/admin/dbclick.js rename to assets/js/addons/dbclick.js diff --git a/assets/js/admin/document-selector.js b/assets/js/addons/document-selector.js similarity index 100% rename from assets/js/admin/document-selector.js rename to assets/js/addons/document-selector.js diff --git a/assets/js/admin/editor.js b/assets/js/addons/editor.js similarity index 100% rename from assets/js/admin/editor.js rename to assets/js/addons/editor.js diff --git a/assets/js/admin/form-collection.js b/assets/js/addons/form-collection.js similarity index 100% rename from assets/js/admin/form-collection.js rename to assets/js/addons/form-collection.js diff --git a/assets/js/admin/form-confirm.js b/assets/js/addons/form-confirm.js similarity index 100% rename from assets/js/admin/form-confirm.js rename to assets/js/addons/form-confirm.js diff --git a/assets/js/admin/form.js b/assets/js/addons/form.js similarity index 100% rename from assets/js/admin/form.js rename to assets/js/addons/form.js diff --git a/assets/js/admin/modal.js b/assets/js/addons/modal.js similarity index 100% rename from assets/js/admin/modal.js rename to assets/js/addons/modal.js diff --git a/assets/js/admin/panel.js b/assets/js/addons/panel.js similarity index 100% rename from assets/js/admin/panel.js rename to assets/js/addons/panel.js diff --git a/assets/js/admin/password.js b/assets/js/addons/password.js similarity index 100% rename from assets/js/admin/password.js rename to assets/js/addons/password.js diff --git a/assets/js/admin/push-state.js b/assets/js/addons/push-state.js similarity index 100% rename from assets/js/admin/push-state.js rename to assets/js/addons/push-state.js diff --git a/assets/js/admin/rest-choices.js b/assets/js/addons/rest-choices.js similarity index 100% rename from assets/js/admin/rest-choices.js rename to assets/js/addons/rest-choices.js diff --git a/assets/js/admin/simplemde.js b/assets/js/addons/simplemde.js similarity index 100% rename from assets/js/admin/simplemde.js rename to assets/js/addons/simplemde.js diff --git a/assets/js/addons/sortable.js b/assets/js/addons/sortable.js new file mode 100644 index 0000000..dd47c8e --- /dev/null +++ b/assets/js/addons/sortable.js @@ -0,0 +1,31 @@ +const $ = require('jquery') +const Sortable = require('sortablejs').Sortable + +module.exports = () => { + $('*[data-sortable]').each((i, list) => { + const element = $(list) + const route = element.attr('data-sortable-route') + + new Sortable(list, { + handle: '*[data-sortable-item]', + sort: true, + onEnd: (e) => { + if (!route) { + return; + } + + const items = element.find('*[data-sortable-item]') + let datas = {items: []} + + items.each((order, v) => { + datas.items[$(v).attr('data-sortable-item')] = order + 1; + }) + + $.post(route, datas) + .always((data) => { + document.location.href = document.location.href + }) + } + }); + }); +} diff --git a/assets/js/admin/table-fixed.js b/assets/js/addons/table-fixed.js similarity index 100% rename from assets/js/admin/table-fixed.js rename to assets/js/addons/table-fixed.js diff --git a/assets/js/admin/table-selectable.js b/assets/js/addons/table-selectable.js similarity index 100% rename from assets/js/admin/table-selectable.js rename to assets/js/addons/table-selectable.js diff --git a/assets/js/admin/toast.js b/assets/js/addons/toast.js similarity index 100% rename from assets/js/admin/toast.js rename to assets/js/addons/toast.js diff --git a/assets/js/admin/tooltip.js b/assets/js/addons/tooltip.js similarity index 100% rename from assets/js/admin/tooltip.js rename to assets/js/addons/tooltip.js diff --git a/assets/js/admin.js b/assets/js/admin.js index 163eec0..2052312 100644 --- a/assets/js/admin.js +++ b/assets/js/admin.js @@ -1,29 +1,20 @@ -/*const imagesContext = require.context( - '../images', - true, /\.(png|jpg|jpeg|gif|ico|svg|webp)$/ -); - -imagesContext.keys().forEach(imagesContext);*/ - import '../css/admin.scss'; require('../../node_modules/bootstrap/dist/js/bootstrap.min.js'); -// require('./admin/table-selectable.js')(); -require('./admin/table-fixed.js')(); -// require('./admin/document-selector.js')(); -require('./admin/form-confirm.js')(); -require('./admin/form.js')(); -require('./admin/dbclick.js')(); -require('./admin/toast.js')(); -require('./admin/modal.js')(); -require('./admin/push-state.js')(); -require('./admin/password.js')(); -require('./admin/tooltip.js')(); -require('./admin/editor.js')(); -require('./admin/panel.js')(); -require('./admin/choices.js')(); -require('./admin/checkbox-checker.js')(); -require('./admin/rest-choices.js')(); -require('./admin/form-collection.js')(); -require('./admin/datepicker.js')(); -require('./admin/simplemde.js')(); +require('./addons/table-fixed.js')(); +require('./addons/form-confirm.js')(); +require('./addons/form.js')(); +require('./addons/dbclick.js')(); +require('./addons/toast.js')(); +require('./addons/modal.js')(); +require('./addons/push-state.js')(); +require('./addons/password.js')(); +require('./addons/tooltip.js')(); +require('./addons/editor.js')(); +require('./addons/panel.js')(); +require('./addons/choices.js')(); +require('./addons/checkbox-checker.js')(); +require('./addons/rest-choices.js')(); +require('./addons/form-collection.js')(); +require('./addons/datepicker.js')(); +require('./addons/sortable.js')(); diff --git a/config/packages/app.yaml b/config/packages/app.yaml index dd0caab..9485b08 100644 --- a/config/packages/app.yaml +++ b/config/packages/app.yaml @@ -3,18 +3,18 @@ core: name: "Blog" logo: "build/images/core/logo.svg" controllers: - - {name: 'PostController::posts', action: 'App\Controller\Blog\PostController::posts'} - - {name: 'CategoryController::categories', action: 'App\Controller\Blog\CategoryController::categories'} - - {name: 'PostController::category', action: 'App\Controller\Blog\PostController::category'} - - {name: 'PostController::search', action: 'App\Controller\Blog\PostController::search'} - {name: 'LinkController:links', action: 'App\Controller\LinkController:links'} - {name: 'ContactController::contact', action: 'App\Controller\ContactController::contact'} - - {name: 'PostController::post', action: 'App\Controller\Blog\PostController::post'} - - {name: '\Blog\PostController::rss', action: 'App\Controller\Blog\PostController::rss'} - {name: 'BotController::formWithoutJavascript', action: 'App\Controller\BotController::formWithoutJavascript'} - {name: 'LinkController:rss', action: 'App\Controller\LinkController:rss'} - - {name: 'PostController::jsonApi', action: 'App\Controller\Blog\PostController::jsonApi'} - {name: 'TextController:text', action: 'App\Controller\TextController:text'} + - {name: 'Blog\PostController::search', action: 'App\Controller\Blog\PostController::search'} + - {name: 'Blog\PostController::posts', action: 'App\Controller\Blog\PostController::posts'} + - {name: 'Blog\PostController::category', action: 'App\Controller\Blog\PostController::category'} + - {name: 'Blog\PostController::post', action: 'App\Controller\Blog\PostController::post'} + - {name: 'Blog\PostController::rss', action: 'App\Controller\Blog\PostController::rss'} + - {name: 'Blog\PostController::jsonApi', action: 'App\Controller\Blog\PostController::jsonApi'} + - {name: 'Blog\CategoryController::categories', action: 'App\Controller\Blog\CategoryController::categories'} pages: App\Entity\Page\SimplePage: name: 'Page de contenu' diff --git a/core/Controller/Admin/Crud/CrudController.php b/core/Controller/Admin/Crud/CrudController.php index d435d88..2b2cd42 100644 --- a/core/Controller/Admin/Crud/CrudController.php +++ b/core/Controller/Admin/Crud/CrudController.php @@ -31,11 +31,11 @@ abstract class CrudController extends AdminController $configuration = $this->getConfiguration(); $this->applySort('index', $query, $request); - $this->updateFilters($request, $session); + $this->updatefilters($request, $session); $pager = $query - ->useFilters($this->filters) - ->paginate($page, $configuration->getMaxPerPage('index')) + ->usefilters($this->filters) + ->paginate($page, $configuration->getmaxperpage('index')) ; return $this->render($this->getConfiguration()->getView('index'), [ @@ -125,6 +125,44 @@ abstract class CrudController extends AdminController ]); } + protected function doSort(int $page = 1, RepositoryQuery $query, EntityManager $entityManager, Request $request, Session $session): Response + { + $configuration = $this->getConfiguration(); + $context = $request->query->get('context', 'index'); + + if (!$configuration->getIsSortableCollection($context)) { + throw $this->createNotFoundException(); + } + + $this->applySort($context, $query, $request); + $this->updateFilters($request, $session); + + $pager = $query + ->useFilters($this->filters) + ->paginate($page, $configuration->getMaxPerPage($context)) + ; + + if ($this->isCsrfTokenValid('sort', $request->query->get('_token'))) { + $items = $request->request->get('items', []); + $setter = 'set'.$configuration->getSortableCollectionProperty(); + $orderStart = ($page - 1) * $configuration->getMaxPerPage($context); + + foreach ($pager as $key => $entity) { + if (isset($items[$key + 1])) { + $entity->$setter($items[$key + 1] + $orderStart); + + $entityManager->update($entity); + } + } + + $this->addFlash('success', 'The data has been saved.'); + } else { + $this->addFlash('warning', 'The form is not valid.'); + } + + return $this->json([]); + } + protected function doDelete(EntityInterface $entity, EntityManager $entityManager, Request $request, callable $beforeDelete = null): Response { $configuration = $this->getConfiguration(); @@ -208,6 +246,13 @@ abstract class CrudController extends AdminController protected function applySort(string $context, RepositoryQuery $query, Request $request) { $configuration = $this->getConfiguration(); + + if ($configuration->getIsSortableCollection($context)) { + $query->orderBy(sprintf('.%s', $configuration->getSortableCollectionProperty())); + + return; + } + $defaultSort = $configuration->getDefaultSort($context); $name = $request->query->get('_sort', $defaultSort['label'] ?? null); diff --git a/core/Crud/CrudConfiguration.php b/core/Crud/CrudConfiguration.php index 38cc8cd..45bdb03 100644 --- a/core/Crud/CrudConfiguration.php +++ b/core/Crud/CrudConfiguration.php @@ -23,6 +23,8 @@ class CrudConfiguration protected array $maxPerPage = []; protected array $locales = []; protected array $defaultSort = []; + protected array $isSortableCollection = []; + protected string $sortableCollectionProperty = 'sortOrder'; protected ?string $defaultLocale = null; protected static $self; @@ -245,4 +247,28 @@ class CrudConfiguration { return $this->defaultSort[$context] ?? null; } + + public function setIsSortableCollection(string $page, bool $isSortableCollection): self + { + $this->isSortableCollection[$page] = $isSortableCollection; + + return $this; + } + + public function getIsSortableCollection(string $page): bool + { + return $this->isSortableCollection[$page] ?? false; + } + + public function setSortableCollectionProperty(string $sortableCollectionProperty): self + { + $this->sortableCollectionProperty = $sortableCollectionProperty; + + return $this; + } + + public function getSortableCollectionProperty(): string + { + return $this->sortableCollectionProperty; + } } diff --git a/core/Resources/maker/crud-controller/CrudController.tpl.php b/core/Resources/maker/crud-controller/CrudController.tpl.php index 78c80c5..e4cab02 100644 --- a/core/Resources/maker/crud-controller/CrudController.tpl.php +++ b/core/Resources/maker/crud-controller/CrudController.tpl.php @@ -58,6 +58,14 @@ class extends CrudController return $this->doEdit($entity, $entityManager, $request); } + /** + * @Route("/admin//sort/{page}", name="admin__sort", methods={"POST"}) + */ + public function sort(int $page = 1, RepositoryQuery $query, EntityManager $entityManager, Request $request, Session $session): Response + { + return $this->doSort($page, $query, $entityManager, $request, $session); + } + /** * @Route("/admin//delete/{entity}", name="admin__delete", methods={"DELETE"}) */ diff --git a/core/Resources/translations/messages.fr.yaml b/core/Resources/translations/messages.fr.yaml index b6fe635..ec47569 100644 --- a/core/Resources/translations/messages.fr.yaml +++ b/core/Resources/translations/messages.fr.yaml @@ -157,3 +157,4 @@ "Tasks": "Tâches" "Results": "Résultats" "Clean all cache": "Nettoyer tout le cache" +"You can sort items by drag & drop": "Vous pouvez trier les élements via un glisser/déposer" diff --git a/core/Resources/views/admin/crud/index.html.twig b/core/Resources/views/admin/crud/index.html.twig index 8405953..4674200 100644 --- a/core/Resources/views/admin/crud/index.html.twig +++ b/core/Resources/views/admin/crud/index.html.twig @@ -62,6 +62,8 @@ {% endblock %} {% block list %} + {% set isSortable = configuration.isSortableCollection('index') %} +
{% block list_header %} @@ -119,8 +121,29 @@ {% endblock %} {% block list_items %} - + {% if isSortable %} + {% set dataSortable = 'data-sortable' %} + {% set dataSortableRoute = 'data-sortable-route="' ~ path( + configuration.pageRoute('sort'), + { + _token: csrf_token('sort'), + page: pager.currentPageNumber, + context: 'index', + } + ) ~ '"' %} + {% else %} + {% set dataSortable = '' %} + {% set dataSortableRoute = '' %} + {% endif %} + + {% for item in pager %} + {% if isSortable %} + {% set dataSortableItem = 'data-sortable-item="' ~ loop.index ~ '"' %} + {% else %} + {% set dataSortableItem = '' %} + {% endif %} + {% block list_item %} {%- set dbClick %} {% if configuration.action('index', 'show', true) %} @@ -130,7 +153,7 @@ {% endif %} {% endset -%} - + {% for config in configuration.fields('index') %} {% set attr = config.options.attr is defined ? config.options.attr : [] %} {% set action = config.options.action is defined ? config.options.action : null %} @@ -180,6 +203,15 @@ {% endblock %} + + {% if loop.last and isSortable %} + + + + {% endif %} {% else %} diff --git a/package.json b/package.json index 54fd29f..5cb5929 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "prismjs": "^1.23.0", "qrcodejs": "^1.0.0", "simplemde": "^1.11.2", + "sortablejs": "^1.13.0", "tinymce": "^5.7.1", "vanillajs-datepicker": "^1.1.4", "wire.css": "^1.2.5", diff --git a/yarn.lock b/yarn.lock index f19d0b2..6697753 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5481,6 +5481,11 @@ sockjs@0.3.21: uuid "^3.4.0" websocket-driver "^0.7.4" +sortablejs@^1.13.0: + version "1.13.0" + resolved "https://registry.yarnpkg.com/sortablejs/-/sortablejs-1.13.0.tgz#3ab2473f8c69ca63569e80b1cd1b5669b51269e9" + integrity sha512-RBJirPY0spWCrU5yCmWM1eFs/XgX2J5c6b275/YyxFRgnzPhKl/TDeU2hNR8Dt7ITq66NRPM4UlOt+e5O4CFHg== + source-list-map@^2.0.0, source-list-map@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34"
+ + {{ 'You can sort items by drag & drop'|trans }} +
@@ -187,7 +219,7 @@
- Aucun résultat + {{ 'No result'|trans }}