diff --git a/Controller/Adminhtml/CategoryWidget.php b/Controller/Adminhtml/CategoryWidget.php
new file mode 100755
index 0000000000000000000000000000000000000000..704df39863fd684b2ce17d58b2f674a6a6d26af3
--- /dev/null
+++ b/Controller/Adminhtml/CategoryWidget.php
@@ -0,0 +1,57 @@
+<?php
+namespace NicolasBejean\CategoryWidget\Controller\Adminhtml;
+
+use \Magento\Backend\App\Action;
+use \Magento\Backend\App\Action\Context;
+use \Magento\Backend\Model\View\Result\Page;
+use \Magento\Framework\Registry;
+
+/**
+ * Class CategoryWidget
+ *
+ * @category PHP
+ * @package  NicolasBejean\CategoryWidget\Controller\Adminhtml
+ * @author   Nicolas Béjean <nicolas@bejean.eu>
+ * @license  https://github.com/nicolasbejean/category-widget/blob/master/licence.txt BSD Licence
+ * @link     https://www.bejean.eu
+ */
+abstract class CategoryWidget extends Action
+{
+    /**
+     * Authorization level of a basic admin session
+     *
+     * @see _isAllowed()
+     */
+    const ADMIN_RESOURCE = 'NicolasBejean_CategoryWidget::global';
+
+    /**
+     * Core registry
+     *
+     * @var Registry
+     */
+    protected $coreRegistry;
+
+    /**
+     * @param Context $context
+     * @param Registry $coreRegistry
+     */
+    public function __construct(Context $context, Registry $coreRegistry)
+    {
+        $this->coreRegistry = $coreRegistry;
+        parent::__construct($context);
+    }
+
+    /**
+     * Init page
+     *
+     * @param Page $resultPage
+     * @return Page
+     */
+    protected function initPage($resultPage)
+    {
+        $resultPage->setActiveMenu('NicolasBejean_CategoryWidget::categorywidget')
+            ->addBreadcrumb(__('Category Widget'), __('Category Widget'))
+            ->addBreadcrumb(__('Category Widget'), __('Category Widget'));
+        return $resultPage;
+    }
+}
diff --git a/Controller/Adminhtml/CategoryWidget/Delete.php b/Controller/Adminhtml/CategoryWidget/Delete.php
new file mode 100755
index 0000000000000000000000000000000000000000..5f70428296c9c123eed1cf2ff992dab516a3fa2a
--- /dev/null
+++ b/Controller/Adminhtml/CategoryWidget/Delete.php
@@ -0,0 +1,54 @@
+<?php
+namespace NicolasBejean\CategoryWidget\Controller\Adminhtml\CategoryWidget;
+
+use \Magento\Backend\Model\View\Result\Redirect;
+use \Magento\Framework\Controller\ResultInterface;
+use \NicolasBejean\CategoryWidget\Controller\Adminhtml\CategoryWidget;
+use \NicolasBejean\CategoryWidget\Model\CategoryWidget as CategoryWidgetModel;
+use \Exception;
+
+/**
+ * Class Delete
+ *
+ * @category PHP
+ * @package  NicolasBejean\CategoryWidget\Controller\Adminhtml\CategoryWidget
+ * @author   Nicolas Béjean <nicolas@bejean.eu>
+ * @license  https://github.com/nicolasbejean/category-widget/blob/master/licence.txt BSD Licence
+ * @link     https://www.bejean.eu
+ */
+class Delete extends CategoryWidget
+{
+    /**
+     * Delete action
+     *
+     * @return ResultInterface
+     */
+    public function execute()
+    {
+        /** @var Redirect $resultRedirect */
+        $resultRedirect = $this->resultRedirectFactory->create();
+        // check if we know what should be deleted
+        $id = $this->getRequest()->getParam('entity_id');
+        if ($id) {
+            try {
+                /** @var CategoryWidgetModel $model */
+                $model = $this->_objectManager->create(CategoryWidgetModel::class);
+                $model->load($id);
+                $model->delete();
+                // display success message
+                $this->messageManager->addSuccessMessage(__('You deleted the category widget.'));
+                // go to grid
+                return $resultRedirect->setPath('*/*/');
+            } catch (Exception $e) {
+                // display error message
+                $this->messageManager->addErrorMessage($e->getMessage());
+                // go back to edit form
+                return $resultRedirect->setPath('*/*/edit', ['entity_id' => $id]);
+            }
+        }
+        // display error message
+        $this->messageManager->addErrorMessage(__('We can\'t find an category widget to delete.'));
+        // go to grid
+        return $resultRedirect->setPath('*/*/');
+    }
+}
diff --git a/Controller/Adminhtml/CategoryWidget/Edit.php b/Controller/Adminhtml/CategoryWidget/Edit.php
new file mode 100755
index 0000000000000000000000000000000000000000..1fcc685a36fa4552f1fa3962527575448bb46561
--- /dev/null
+++ b/Controller/Adminhtml/CategoryWidget/Edit.php
@@ -0,0 +1,81 @@
+<?php
+namespace NicolasBejean\CategoryWidget\Controller\Adminhtml\CategoryWidget;
+
+use \Magento\Backend\App\Action\Context;
+use \Magento\Backend\Model\View\Result\Page;
+use \Magento\Backend\Model\View\Result\Redirect;
+use \Magento\Framework\Controller\ResultInterface;
+use \Magento\Framework\Registry;
+use \Magento\Framework\View\Result\PageFactory;
+use \NicolasBejean\CategoryWidget\Controller\Adminhtml\CategoryWidget;
+use \NicolasBejean\CategoryWidget\Model\CategoryWidget as CategoryWidgetModel;
+
+/**
+ * Class Edit
+ *
+ * @category PHP
+ * @package  NicolasBejean\CategoryWidget\Controller\Adminhtml\CategoryWidget
+ * @author   Nicolas Béjean <nicolas@bejean.eu>
+ * @license  https://github.com/nicolasbejean/category-widget/blob/master/licence.txt BSD Licence
+ * @link     https://www.bejean.eu
+ */
+class Edit extends CategoryWidget
+{
+    /**
+     * @var PageFactory
+     */
+    protected $resultPageFactory;
+
+    /**
+     * @param Context               $context
+     * @param Registry              $coreRegistry
+     * @param PageFactory           $resultPageFactory
+     */
+    public function __construct(
+        Context                     $context,
+        Registry                    $coreRegistry,
+        PageFactory                 $resultPageFactory
+    ) {
+        $this->resultPageFactory    = $resultPageFactory;
+
+        parent::__construct($context, $coreRegistry);
+    }
+
+    /**
+     * Edit category widget
+     *
+     * @return ResultInterface
+     * @SuppressWarnings(PHPMD.NPathComplexity)
+     */
+    public function execute()
+    {
+        // 1. Get ID and create model
+        $id = $this->getRequest()->getParam('entity_id');
+        /** @var CategoryWidgetModel $model */
+        $model = $this->_objectManager->create(CategoryWidgetModel::class);
+
+        // 2. Initial checking
+        if ($id) {
+            $model->load($id);
+            if (!$model->getId()) {
+                $this->messageManager->addErrorMessage(__('This category widget no longer exists.'));
+                /** @var Redirect $resultRedirect */
+                $resultRedirect = $this->resultRedirectFactory->create();
+                return $resultRedirect->setPath('*/*/');
+            }
+        }
+
+        $this->coreRegistry->register('nicolasbejean_categorywidget_item', $model);
+
+        // 5. Build edit form
+        /** @var Page $resultPage */
+        $resultPage = $this->resultPageFactory->create();
+        $this->initPage($resultPage)->addBreadcrumb(
+            $id ? __('Edit Image Slider') : __('New Image Slider'),
+            $id ? __('Edit Image Slider') : __('New Image Slider')
+        );
+        $resultPage->getConfig()->getTitle()->prepend(__('Image Slider'));
+        $resultPage->getConfig()->getTitle()->prepend($model->getId() ? $model->getName() . ' (' . $model->getIdentifier() . ')' : __('New Image Slider'));
+        return $resultPage;
+    }
+}
diff --git a/Controller/Adminhtml/CategoryWidget/Index.php b/Controller/Adminhtml/CategoryWidget/Index.php
new file mode 100755
index 0000000000000000000000000000000000000000..761ecab40ef56d142f37265025e07c31ddfd0849
--- /dev/null
+++ b/Controller/Adminhtml/CategoryWidget/Index.php
@@ -0,0 +1,58 @@
+<?php
+namespace NicolasBejean\CategoryWidget\Controller\Adminhtml\CategoryWidget;
+
+use \Magento\Backend\App\Action\Context;
+use \Magento\Backend\Model\View\Result\Page;
+use \Magento\Framework\Controller\ResultInterface;
+use \Magento\Framework\Registry;
+use \Magento\Framework\View\Result\PageFactory;
+use \NicolasBejean\CategoryWidget\Controller\Adminhtml\CategoryWidget;
+use \Magento\Framework\App\Request\DataPersistorInterface;
+
+/**
+ * Class Index
+ *
+ * @category PHP
+ * @package  NicolasBejean\CategoryWidget\Controller\Adminhtml\CategoryWidget
+ * @author   Nicolas Béjean <nicolas@bejean.eu>
+ * @license  https://github.com/nicolasbejean/category-widget/blob/master/licence.txt BSD Licence
+ * @link     https://www.bejean.eu
+ */class Index extends CategoryWidget
+{
+    /**
+     * @var PageFactory
+     */
+    protected $resultPageFactory;
+
+    /**
+     * @param Context               $context
+     * @param Registry              $coreRegistry
+     * @param PageFactory           $resultPageFactory
+     */
+    public function __construct(
+        Context                     $context,
+        Registry                    $coreRegistry,
+        PageFactory                 $resultPageFactory
+    ) {
+        $this->resultPageFactory    = $resultPageFactory;
+
+        parent::__construct($context, $coreRegistry);
+    }
+
+    /**
+     * Index action
+     *
+     * @return ResultInterface
+     */
+    public function execute()
+    {
+        /** @var Page $resultPage */
+        $resultPage = $this->resultPageFactory->create();
+        $this->initPage($resultPage)->getConfig()->getTitle()->prepend(__('Image Slider Manager'));
+
+        $dataPersistor = $this->_objectManager->get(DataPersistorInterface::class);
+        $dataPersistor->clear('nicolasbejean_categorywidget_item');
+
+        return $resultPage;
+    }
+}
diff --git a/Controller/Adminhtml/CategoryWidget/InlineEdit.php b/Controller/Adminhtml/CategoryWidget/InlineEdit.php
new file mode 100755
index 0000000000000000000000000000000000000000..067a5e69206dd4f455e04fa8733498bd465e15cc
--- /dev/null
+++ b/Controller/Adminhtml/CategoryWidget/InlineEdit.php
@@ -0,0 +1,108 @@
+<?php
+namespace NicolasBejean\CategoryWidget\Controller\Adminhtml\CategoryWidget;
+
+use \Magento\Backend\App\Action;
+use \Magento\Backend\App\Action\Context;
+use \Magento\Framework\Controller\Result\Json;
+use \Magento\Framework\Controller\ResultInterface;
+use \NicolasBejean\CategoryWidget\Api\CategoryWidgetRepositoryInterface as CategoryWidgetRepository;
+use \Magento\Framework\Controller\Result\JsonFactory;
+use \NicolasBejean\CategoryWidget\Api\Data\CategoryWidgetInterface;
+use \NicolasBejean\CategoryWidget\Model\CategoryWidget;
+use \Exception;
+
+/**
+ * Class InlineEdit
+ *
+ * @category PHP
+ * @package  NicolasBejean\CategoryWidget\Controller\Adminhtml\CategoryWidget
+ * @author   Nicolas Béjean <nicolas@bejean.eu>
+ * @license  https://github.com/nicolasbejean/category-widget/blob/master/licence.txt BSD Licence
+ * @link     https://www.bejean.eu
+ */
+class InlineEdit extends Action
+{
+    /**
+     * Authorization level of a basic admin session
+     *
+     * @see _isAllowed()
+     */
+    const ADMIN_RESOURCE = 'NicolasBejean_CategoryWidget::global';
+
+    /**
+     * @var CategoryWidgetRepository
+     */
+    protected $categoryWidgetRepository;
+
+    /**
+     * @var JsonFactory
+     */
+    protected $jsonFactory;
+
+    /**
+     * @param Context                   $context
+     * @param CategoryWidgetRepository     $categoryWidgetRepository
+     * @param JsonFactory               $jsonFactory
+     */
+    public function __construct(
+        Context                         $context,
+        CategoryWidgetRepository           $categoryWidgetRepository,
+        JsonFactory                     $jsonFactory
+    ) {
+        $this->categoryWidgetRepository    = $categoryWidgetRepository;
+        $this->jsonFactory              = $jsonFactory;
+
+        parent::__construct($context);
+    }
+
+    /**
+     * @return ResultInterface
+     */
+    public function execute()
+    {
+        /** @var Json $resultJson */
+        $resultJson = $this->jsonFactory->create();
+        $error = false;
+        $messages = [];
+
+        if ($this->getRequest()->getParam('isAjax')) {
+            $postItems = $this->getRequest()->getParam('items', []);
+            if (!count($postItems)) {
+                $messages[] = __('Please correct the data sent.');
+                $error = true;
+            } else {
+                foreach (array_keys($postItems) as $id) {
+                    /** @var CategoryWidget $categoryWidget */
+                    $categoryWidget = $this->categoryWidgetRepository->getById($id);
+                    try {
+                        $categoryWidget->setData(array_merge($categoryWidget->getData(), $postItems[$id]));
+                        $this->categoryWidgetRepository->save($categoryWidget);
+                    } catch (Exception $e) {
+                        $messages[] = $this->getErrorWithImageId(
+                            $categoryWidget,
+                            __($e->getMessage())
+                        );
+                        $error = true;
+                    }
+                }
+            }
+        }
+
+        return $resultJson->setData([
+            'messages' => $messages,
+            'error' => $error
+        ]);
+    }
+
+    /**
+     * Add Image Slider title to error message
+     *
+     * @param CategoryWidgetInterface $categoryWidget
+     * @param string $errorText
+     * @return string
+     */
+    protected function getErrorWithImageId(CategoryWidgetInterface $categoryWidget, $errorText)
+    {
+        return '[Image Slider ID: ' . $categoryWidget->getId() . '] ' . $errorText;
+    }
+}
diff --git a/Controller/Adminhtml/CategoryWidget/MassDelete.php b/Controller/Adminhtml/CategoryWidget/MassDelete.php
new file mode 100755
index 0000000000000000000000000000000000000000..3479dc72d8693fe0de79467dcb9f5251b91a5c73
--- /dev/null
+++ b/Controller/Adminhtml/CategoryWidget/MassDelete.php
@@ -0,0 +1,88 @@
+<?php
+namespace NicolasBejean\CategoryWidget\Controller\Adminhtml\CategoryWidget;
+
+use \Magento\Backend\App\Action;
+use \Magento\Backend\Model\View\Result\Redirect;
+use \Magento\Framework\App\ObjectManager;
+use \Magento\Framework\Controller\ResultFactory;
+use \Magento\Backend\App\Action\Context;
+use \Magento\Framework\Exception\LocalizedException;
+use \Magento\Ui\Component\MassAction\Filter;
+use \NicolasBejean\CategoryWidget\Model\CategoryWidget as Model;
+use \NicolasBejean\CategoryWidget\Model\ResourceModel\CategoryWidget as ResourceModel;
+use \NicolasBejean\CategoryWidget\Model\ResourceModel\CategoryWidget\Collection;
+use \NicolasBejean\CategoryWidget\Model\ResourceModel\CategoryWidget\CollectionFactory;
+use \Exception;
+
+/**
+ * Class MassDelete
+ *
+ * @category PHP
+ * @package  NicolasBejean\CategoryWidget\Controller\Adminhtml\CategoryWidget
+ * @author   Nicolas Béjean <nicolas@bejean.eu>
+ * @license  https://github.com/nicolasbejean/category-widget/blob/master/licence.txt BSD Licence
+ * @link     https://www.bejean.eu
+ */
+class MassDelete extends Action
+{
+    /**
+     * Authorization level of a basic admin session
+     *
+     * @see _isAllowed()
+     */
+    const ADMIN_RESOURCE = 'NicolasBejean_CategoryWidget::global';
+
+    /**
+     * @var Filter
+     */
+    protected $filter;
+
+    /**
+     * @var CollectionFactory
+     */
+    protected $collectionFactory;
+
+    /**
+     * @param Context               $context
+     * @param Filter                $filter
+     * @param CollectionFactory     $collectionFactory
+     */
+    public function __construct(
+        Context                     $context,
+        Filter                      $filter,
+        CollectionFactory           $collectionFactory
+    )
+    {
+        $this->filter               = $filter;
+        $this->collectionFactory    = $collectionFactory;
+
+        parent::__construct($context);
+    }
+
+    /**
+     * Execute action
+     *
+     * @return Redirect
+     * @throws LocalizedException|Exception
+     */
+    public function execute()
+    {
+        /** @var Collection $collection */
+        $collection = $this->filter->getCollection($this->collectionFactory->create());
+        $collectionSize = $collection->getSize();
+
+        /** @var ResourceModel $resourceModel */
+        $resourceModel = ObjectManager::getInstance()->get(ResourceModel::class);
+
+        /** @var Model $item */
+        foreach ($collection as $item) {
+            $resourceModel->delete($item);
+        }
+
+        $this->messageManager->addSuccessMessage(__('A total of %1 record(s) have been deleted.', $collectionSize));
+
+        /** @var Redirect $resultRedirect */
+        $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT);
+        return $resultRedirect->setPath('*/*/');
+    }
+}
diff --git a/Controller/Adminhtml/CategoryWidget/MassDisable.php b/Controller/Adminhtml/CategoryWidget/MassDisable.php
new file mode 100755
index 0000000000000000000000000000000000000000..abecdfbf8363c0efd0e98f62a09bc5c41b91a3a1
--- /dev/null
+++ b/Controller/Adminhtml/CategoryWidget/MassDisable.php
@@ -0,0 +1,91 @@
+<?php
+namespace NicolasBejean\CategoryWidget\Controller\Adminhtml\CategoryWidget;
+
+use \Magento\Backend\App\Action;
+use \Magento\Backend\Model\View\Result\Redirect;
+use \Magento\Framework\App\ObjectManager;
+use \Magento\Framework\Controller\ResultFactory;
+use \Magento\Backend\App\Action\Context;
+use \Magento\Framework\Exception\LocalizedException;
+use \Magento\Ui\Component\MassAction\Filter;
+use \NicolasBejean\CategoryWidget\Model\CategoryWidget as Model;
+use \NicolasBejean\CategoryWidget\Model\ResourceModel\CategoryWidget as ResourceModel;
+use \NicolasBejean\CategoryWidget\Model\ResourceModel\CategoryWidget\Collection;
+use \NicolasBejean\CategoryWidget\Model\ResourceModel\CategoryWidget\CollectionFactory;
+use \Exception;
+
+/**
+ * Class MassDisable
+ *
+ * @category PHP
+ * @package  NicolasBejean\CategoryWidget\Controller\Adminhtml\CategoryWidget
+ * @author   Nicolas Béjean <nicolas@bejean.eu>
+ * @license  https://github.com/nicolasbejean/category-widget/blob/master/licence.txt BSD Licence
+ * @link     https://www.bejean.eu
+ */
+class MassDisable extends Action
+{
+    /**
+     * Authorization level of a basic admin session
+     *
+     * @see _isAllowed()
+     */
+    const ADMIN_RESOURCE = 'NicolasBejean_CategoryWidget::global';
+
+    /**
+     * @var Filter
+     */
+    protected $filter;
+
+    /**
+     * @var CollectionFactory
+     */
+    protected $collectionFactory;
+
+    /**
+     * @param Context               $context
+     * @param Filter                $filter
+     * @param CollectionFactory     $collectionFactory
+     */
+    public function __construct(
+        Context                     $context,
+        Filter                      $filter,
+        CollectionFactory           $collectionFactory
+    )
+    {
+        $this->filter               = $filter;
+        $this->collectionFactory    = $collectionFactory;
+
+        parent::__construct($context);
+    }
+
+    /**
+     * Execute action
+     *
+     * @return Redirect
+     * @throws LocalizedException|Exception
+     */
+    public function execute()
+    {
+        /** @var Collection $collection */
+        $collection = $this->filter->getCollection($this->collectionFactory->create());
+        $collectionSize = $collection->getSize();
+
+        /** @var ResourceModel $resourceModel */
+        $resourceModel = ObjectManager::getInstance()->get(ResourceModel::class);
+
+        /** @var Model $item */
+        foreach ($collection as $item) {
+            $item->setIsActive(false);
+            $resourceModel->save($item);
+        }
+
+        $this->messageManager->addSuccessMessage(
+            __('A total of %1 record(s) have been disabled.', $collectionSize)
+        );
+
+        /** @var Redirect $resultRedirect */
+        $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT);
+        return $resultRedirect->setPath('*/*/');
+    }
+}
diff --git a/Controller/Adminhtml/CategoryWidget/MassEnable.php b/Controller/Adminhtml/CategoryWidget/MassEnable.php
new file mode 100755
index 0000000000000000000000000000000000000000..52fe3d31025e268c4e7468fd98196a96b5412668
--- /dev/null
+++ b/Controller/Adminhtml/CategoryWidget/MassEnable.php
@@ -0,0 +1,91 @@
+<?php
+namespace NicolasBejean\CategoryWidget\Controller\Adminhtml\CategoryWidget;
+
+use \Magento\Backend\App\Action;
+use \Magento\Backend\Model\View\Result\Redirect;
+use \Magento\Framework\App\ObjectManager;
+use \Magento\Framework\Controller\ResultFactory;
+use \Magento\Backend\App\Action\Context;
+use \Magento\Framework\Exception\LocalizedException;
+use \Magento\Ui\Component\MassAction\Filter;
+use \NicolasBejean\CategoryWidget\Model\CategoryWidget as Model;
+use \NicolasBejean\CategoryWidget\Model\ResourceModel\CategoryWidget as ResourceModel;
+use \NicolasBejean\CategoryWidget\Model\ResourceModel\CategoryWidget\Collection;
+use \NicolasBejean\CategoryWidget\Model\ResourceModel\CategoryWidget\CollectionFactory;
+use \Exception;
+
+/**
+ * Class MassEnable
+ *
+ * @category PHP
+ * @package  NicolasBejean\CategoryWidget\Controller\Adminhtml\Image
+ * @author   Nicolas Béjean <nicolas@bejean.eu>
+ * @license  https://github.com/nicolasbejean/category-widget/blob/master/licence.txt BSD Licence
+ * @link     https://www.bejean.eu
+ */
+class MassEnable extends Action
+{
+    /**
+     * Authorization level of a basic admin session
+     *
+     * @see _isAllowed()
+     */
+    const ADMIN_RESOURCE = 'NicolasBejean_CategoryWidget::global';
+
+    /**
+     * @var Filter
+     */
+    protected $filter;
+
+    /**
+     * @var CollectionFactory
+     */
+    protected $collectionFactory;
+
+    /**
+     * @param Context               $context
+     * @param Filter                $filter
+     * @param CollectionFactory     $collectionFactory
+     */
+    public function __construct(
+        Context                     $context,
+        Filter                      $filter,
+        CollectionFactory           $collectionFactory
+    )
+    {
+        $this->filter               = $filter;
+        $this->collectionFactory    = $collectionFactory;
+
+        parent::__construct($context);
+    }
+
+    /**
+     * Execute action
+     *
+     * @return Redirect
+     * @throws LocalizedException|Exception
+     */
+    public function execute()
+    {
+        /** @var Collection $collection */
+        $collection = $this->filter->getCollection($this->collectionFactory->create());
+        $collectionSize = $collection->getSize();
+
+        /** @var ResourceModel $resourceModel */
+        $resourceModel = ObjectManager::getInstance()->get(ResourceModel::class);
+
+        /** @var Model $item */
+        foreach ($collection as $item) {
+            $item->setIsActive(true);
+            $resourceModel->save($item);
+        }
+
+        $this->messageManager->addSuccessMessage(
+            __('A total of %1 record(s) have been enabled.', $collectionSize)
+        );
+
+        /** @var Redirect $resultRedirect */
+        $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT);
+        return $resultRedirect->setPath('*/*/');
+    }
+}
diff --git a/Controller/Adminhtml/CategoryWidget/NewAction.php b/Controller/Adminhtml/CategoryWidget/NewAction.php
new file mode 100755
index 0000000000000000000000000000000000000000..90d96c9219a7724473e9f2d4869aae89d3b975bb
--- /dev/null
+++ b/Controller/Adminhtml/CategoryWidget/NewAction.php
@@ -0,0 +1,53 @@
+<?php
+namespace NicolasBejean\CategoryWidget\Controller\Adminhtml\CategoryWidget;
+
+use \Magento\Backend\App\Action\Context;
+use \Magento\Backend\Model\View\Result\ForwardFactory;
+use \Magento\Framework\Controller\Result\Forward;
+use \Magento\Framework\Controller\ResultInterface;
+use \Magento\Framework\Registry;
+use \NicolasBejean\CategoryWidget\Controller\Adminhtml\CategoryWidget;
+
+/**
+ * Class NewAction
+ *
+ * @category PHP
+ * @package  NicolasBejean\CategoryWidget\Controller\Adminhtml\CategoryWidget
+ * @author   Nicolas Béjean <nicolas@bejean.eu>
+ * @license  https://github.com/nicolasbejean/category-widget/blob/master/licence.txt BSD Licence
+ * @link     https://www.bejean.eu
+ */
+class NewAction extends CategoryWidget
+{
+    /**
+     * @var ForwardFactory
+     */
+    protected $resultForwardFactory;
+
+    /**
+     * @param Context                   $context
+     * @param Registry                  $coreRegistry
+     * @param ForwardFactory            $resultForwardFactory
+     */
+    public function __construct(
+        Context                         $context,
+        Registry                        $coreRegistry,
+        ForwardFactory                  $resultForwardFactory
+    ) {
+        $this->resultForwardFactory     = $resultForwardFactory;
+
+        parent::__construct($context, $coreRegistry);
+    }
+
+    /**
+     * Create new category widget
+     *
+     * @return ResultInterface
+     */
+    public function execute()
+    {
+        /** @var Forward $resultForward */
+        $resultForward = $this->resultForwardFactory->create();
+        return $resultForward->forward('edit');
+    }
+}
diff --git a/Controller/Adminhtml/CategoryWidget/Save.php b/Controller/Adminhtml/CategoryWidget/Save.php
new file mode 100755
index 0000000000000000000000000000000000000000..177c3a41b068a28c92b33f688327212b7a50efe6
--- /dev/null
+++ b/Controller/Adminhtml/CategoryWidget/Save.php
@@ -0,0 +1,147 @@
+<?php
+namespace NicolasBejean\CategoryWidget\Controller\Adminhtml\CategoryWidget;
+
+use \Magento\Backend\Model\View\Result\Redirect;
+use \Magento\Framework\Controller\ResultInterface;
+use \NicolasBejean\CategoryWidget\Controller\Adminhtml\CategoryWidget as CategoryWidgetController;
+use \Magento\Backend\App\Action\Context;
+use \NicolasBejean\CategoryWidget\Api\CategoryWidgetRepositoryInterface;
+use \NicolasBejean\CategoryWidget\Model\CategoryWidget;
+use \NicolasBejean\CategoryWidget\Model\CategoryWidgetFactory;
+use \Magento\Framework\App\Request\DataPersistorInterface;
+use \Magento\Framework\Registry;
+use \Magento\Framework\App\ObjectManager;
+use \Exception;
+
+/**
+ * Class Save
+ *
+ * @category PHP
+ * @package  NicolasBejean\CategoryWidget\Controller\Adminhtml\CategoryWidget
+ * @author   Nicolas Béjean <nicolas@bejean.eu>
+ * @license  https://github.com/nicolasbejean/category-widget/blob/master/licence.txt BSD Licence
+ * @link     https://www.bejean.eu
+ */
+class Save extends CategoryWidgetController
+{
+    /**
+     * @var DataPersistorInterface
+     */
+    protected $dataPersistor;
+
+    /**
+     * @var CategoryWidgetFactory
+     */
+    private $categoryWidgetFactory;
+
+    /**
+     * @var CategoryWidgetRepositoryInterface
+     */
+    private $categoryWidgetRepository;
+
+    /**
+     * @param Context $context
+     * @param Registry $coreRegistry
+     * @param DataPersistorInterface $dataPersistor
+     * @param CategoryWidgetFactory|null $categoryWidgetFactory
+     * @param CategoryWidgetRepositoryInterface|null $categoryWidgetRepository
+     */
+    public function __construct(
+        Context $context,
+        Registry $coreRegistry,
+        DataPersistorInterface $dataPersistor,
+        CategoryWidgetFactory $categoryWidgetFactory = null,
+        CategoryWidgetRepositoryInterface $categoryWidgetRepository = null
+    ) {
+        $this->dataPersistor = $dataPersistor;
+        $this->categoryWidgetFactory = $categoryWidgetFactory
+            ?: ObjectManager::getInstance()->get(CategoryWidgetFactory::class);
+        $this->categoryWidgetRepository = $categoryWidgetRepository
+            ?: ObjectManager::getInstance()->get(CategoryWidgetRepositoryInterface::class);
+        parent::__construct($context, $coreRegistry);
+    }
+
+    /**
+     * Save action
+     *
+     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
+     * @return ResultInterface
+     */
+    public function execute()
+    {
+        /** @var Redirect $resultRedirect */
+        $resultRedirect = $this->resultRedirectFactory->create();
+        $data = $this->getRequest()->getPostValue();
+        if ($data) {
+
+            if (isset($data['is_active']) && $data['is_active'] === 'true') {
+                $data['is_active'] = CategoryWidget::STATUS_ENABLED;
+            }
+            if (empty($data['entity_id'])) {
+                $data['entity_id'] = null;
+            }
+
+            /** @var CategoryWidget $model */
+            $model = $this->categoryWidgetFactory->create();
+
+            $id = $this->getRequest()->getParam('entity_id');
+            if ($id) {
+                try {
+                    $model = $this->categoryWidgetRepository->getById($id);
+                } catch (Exception $e) {
+                    $this->messageManager->addErrorMessage(__('This category widget no longer exists.'));
+                    return $resultRedirect->setPath('*/*/');
+                }
+            }
+
+            $model->setData($data);
+
+            try {
+                $this->categoryWidgetRepository->save($model);
+                $this->messageManager->addSuccessMessage(__('You saved the category widget.'));
+                $this->dataPersistor->clear('nicolasbejean_categorywidget_item');
+                return $this->processCategoryWidgetReturn($model, $data, $resultRedirect);
+            } catch (Exception $e) {
+                $this->messageManager->addExceptionMessage($e, __('Something went wrong while saving the category widget.'));
+            }
+
+            $this->dataPersistor->set('nicolasbejean_categorywidget_item', $data);
+            return $resultRedirect->setPath('*/*/edit', ['entity_id' => $id]);
+        }
+        return $resultRedirect->setPath('*/*/');
+    }
+
+    /**
+     * Process and set the category widget return
+     *
+     * @param CategoryWidget $model
+     * @param array $data
+     * @param ResultInterface $resultRedirect
+     * @return ResultInterface
+     */
+    private function processCategoryWidgetReturn($model, $data, $resultRedirect)
+    {
+        $redirect = $data['back'] ?? 'close';
+
+        if ($redirect ==='continue') {
+            $resultRedirect->setPath('*/*/edit', ['entity_id' => $model->getId()]);
+        } elseif ($redirect === 'close') {
+            $resultRedirect->setPath('*/*/');
+        } elseif ($redirect === 'new') {
+            $resultRedirect->setPath('*/*/new');
+        } elseif ($redirect === 'duplicate') {
+            $duplicateModel = $this->categoryWidgetFactory->create(['data' => $data]);
+            $duplicateModel->setId(null);
+            $duplicateModel->setIdentifier($data['identifier'] . '-' . uniqid());
+            $duplicateModel->setName($data['name']);
+            $duplicateModel->setContent($data['content']);
+            $duplicateModel->setIsActive(CategoryWidget::STATUS_DISABLED);
+            $this->categoryWidgetRepository->save($duplicateModel);
+            $id = $duplicateModel->getId();
+            $this->messageManager->addSuccessMessage(__('You duplicated the category widget.'));
+            $this->dataPersistor->set('nicolasbejean_categorywidget_item', $data);
+            $resultRedirect->setPath('*/*/edit', ['entity_id' => $id]);
+        }
+        return $resultRedirect;
+    }
+}
diff --git a/Controller/Adminhtml/CategoryWidget/Widget/Chooser.php b/Controller/Adminhtml/CategoryWidget/Widget/Chooser.php
new file mode 100755
index 0000000000000000000000000000000000000000..4139dd302ab0562f05727de00769ca29209300bd
--- /dev/null
+++ b/Controller/Adminhtml/CategoryWidget/Widget/Chooser.php
@@ -0,0 +1,78 @@
+<?php
+namespace NicolasBejean\CategoryWidget\Controller\Adminhtml\CategoryWidget\Widget;
+
+use \Magento\Backend\App\Action;
+use \Magento\Backend\App\Action\Context;
+use \Magento\Framework\Controller\Result\Raw;
+use \Magento\Framework\Controller\ResultInterface;
+use \Magento\Framework\View\Layout;
+use \Magento\Framework\View\LayoutFactory;
+use \Magento\Framework\Controller\Result\RawFactory;
+use \NicolasBejean\CategoryWidget\Block\Adminhtml\CategoryWidget\Widget\Chooser as WidgetChooser;
+
+/**
+ * Class Chooser
+ *
+ * @category PHP
+ * @package  NicolasBejean\CategoryWidget\Controller\Adminhtml\CategoryWidget\Widget
+ * @author   Nicolas Béjean <nicolas@bejean.eu>
+ * @license  https://github.com/nicolasbejean/category-widget/blob/master/licence.txt BSD Licence
+ * @link     https://www.bejean.eu
+ */
+class Chooser extends Action
+{
+    /**
+     * Authorization level of a basic admin session
+     */
+    const ADMIN_RESOURCE = 'Magento_Widget::widget_instance';
+
+    /**
+     * @var LayoutFactory
+     */
+    protected $layoutFactory;
+
+    /**
+     * @var RawFactory
+     */
+    protected $resultRawFactory;
+
+    /**
+     * @param Context           $context
+     * @param LayoutFactory     $layoutFactory
+     * @param RawFactory        $resultRawFactory
+     */
+    public function __construct
+    (
+        Context                 $context,
+        LayoutFactory           $layoutFactory,
+        RawFactory              $resultRawFactory
+    ) {
+        $this->layoutFactory    = $layoutFactory;
+        $this->resultRawFactory = $resultRawFactory;
+
+        parent::__construct($context);
+    }
+
+    /**
+     * Chooser Source action
+     *
+     * @return ResultInterface
+     */
+    public function execute()
+    {
+        /** @var Layout $layout */
+        $layout = $this->layoutFactory->create();
+
+        $uniqId = $this->getRequest()->getParam('uniq_id');
+        $pagesGrid = $layout->createBlock(
+            WidgetChooser::class,
+            '',
+            ['data' => ['id' => $uniqId]]
+        );
+
+        /** @var Raw $resultRaw */
+        $resultRaw = $this->resultRawFactory->create();
+        $resultRaw->setContents($pagesGrid->toHtml());
+        return $resultRaw;
+    }
+}