├── src
└── MX
│ └── MegaMenu
│ ├── view
│ ├── adminhtml
│ │ ├── web
│ │ │ ├── fonts
│ │ │ │ ├── fa-solid.eot
│ │ │ │ ├── fa-solid.ttf
│ │ │ │ ├── fa-solid.woff
│ │ │ │ └── fa-solid.woff2
│ │ │ ├── css
│ │ │ │ ├── megamenu-core.css
│ │ │ │ ├── lib
│ │ │ │ │ └── jquery.nestable.min.css
│ │ │ │ ├── form
│ │ │ │ │ └── element
│ │ │ │ │ │ └── toggle.css
│ │ │ │ └── megamenu.css
│ │ │ └── js
│ │ │ │ ├── megamenu
│ │ │ │ ├── dialog.js
│ │ │ │ ├── form.js
│ │ │ │ └── form
│ │ │ │ │ └── builder.js
│ │ │ │ ├── megamenu.js
│ │ │ │ └── megamenu-editor.js
│ │ ├── templates
│ │ │ ├── editor
│ │ │ │ ├── settings
│ │ │ │ │ └── form.phtml
│ │ │ │ └── form.phtml
│ │ │ ├── export.phtml
│ │ │ ├── import.phtml
│ │ │ └── editor.phtml
│ │ ├── layout
│ │ │ ├── mx_megamenu_menu_create.xml
│ │ │ ├── mx_megamenu_menu_edit.xml
│ │ │ ├── default.xml
│ │ │ ├── mx_megamenu_importexport.xml
│ │ │ ├── mx_megamenu_menu_index.xml
│ │ │ ├── mx_megamenu_menu_form.xml
│ │ │ ├── mx_megamenu_menu_export.xml
│ │ │ ├── mx_megamenu_menu_import.xml
│ │ │ └── mx_megamenu_edit.xml
│ │ ├── requirejs-config.js
│ │ └── ui_component
│ │ │ └── mx_megamenu_menu_listing.xml
│ └── frontend
│ │ ├── requirejs-config.js
│ │ ├── templates
│ │ ├── topmenu.phtml
│ │ └── topmenu
│ │ │ ├── structure.phtml
│ │ │ └── structure
│ │ │ └── children.phtml
│ │ ├── layout
│ │ └── default.xml
│ │ └── web
│ │ ├── css
│ │ └── megamenu.css
│ │ └── js
│ │ └── view
│ │ └── megamenu.js
│ ├── registration.php
│ ├── etc
│ ├── module.xml
│ ├── config.xml
│ ├── adminhtml
│ │ ├── routes.xml
│ │ ├── system.xml
│ │ └── menu.xml
│ └── di.xml
│ ├── Api
│ ├── ImportHandlerInterface.php
│ ├── MenuRepositoryInterface.php
│ └── Data
│ │ └── MenuInterface.php
│ ├── Controller
│ └── Adminhtml
│ │ ├── Menu
│ │ ├── Index.php
│ │ ├── Export.php
│ │ ├── Import.php
│ │ ├── Create.php
│ │ ├── Form.php
│ │ ├── Delete.php
│ │ ├── Edit.php
│ │ ├── ImportPost.php
│ │ ├── ExportPost.php
│ │ └── Save.php
│ │ └── Menu.php
│ ├── Model
│ ├── ResourceModel
│ │ ├── Menu
│ │ │ ├── Collection.php
│ │ │ └── Relation
│ │ │ │ ├── Item
│ │ │ │ ├── ReadHandler.php
│ │ │ │ └── SaveHandler.php
│ │ │ │ └── Store
│ │ │ │ ├── ReadHandler.php
│ │ │ │ └── SaveHandler.php
│ │ └── Menu.php
│ ├── MenuRepository.php
│ ├── Menu
│ │ ├── ImportHandler.php
│ │ └── Item.php
│ └── Menu.php
│ ├── Block
│ ├── Adminhtml
│ │ └── Menu
│ │ │ ├── Export.php
│ │ │ ├── Import.php
│ │ │ ├── Edit
│ │ │ ├── Settings
│ │ │ │ └── Form.php
│ │ │ └── Form.php
│ │ │ └── Edit.php
│ ├── TopMenu
│ │ └── Children.php
│ └── TopMenu.php
│ ├── Data
│ └── Form
│ │ └── Element
│ │ ├── Toggle.php
│ │ └── Chooser
│ │ └── Category.php
│ ├── Ui
│ └── Component
│ │ └── Listing
│ │ └── Column
│ │ └── Actions.php
│ └── Setup
│ ├── UpgradeSchema.php
│ └── InstallSchema.php
├── .gitignore
├── README.md
└── composer.json
/src/MX/MegaMenu/view/adminhtml/web/fonts/fa-solid.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/inviqa/mx-megamenu/HEAD/src/MX/MegaMenu/view/adminhtml/web/fonts/fa-solid.eot
--------------------------------------------------------------------------------
/src/MX/MegaMenu/view/adminhtml/web/fonts/fa-solid.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/inviqa/mx-megamenu/HEAD/src/MX/MegaMenu/view/adminhtml/web/fonts/fa-solid.ttf
--------------------------------------------------------------------------------
/src/MX/MegaMenu/view/adminhtml/web/fonts/fa-solid.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/inviqa/mx-megamenu/HEAD/src/MX/MegaMenu/view/adminhtml/web/fonts/fa-solid.woff
--------------------------------------------------------------------------------
/src/MX/MegaMenu/view/adminhtml/web/fonts/fa-solid.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/inviqa/mx-megamenu/HEAD/src/MX/MegaMenu/view/adminhtml/web/fonts/fa-solid.woff2
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Composer packages directory
2 | /vendor
3 |
4 | # Composer executable files directory
5 | /bin
6 |
7 | # Installed Composer packages versions
8 | /composer.lock
--------------------------------------------------------------------------------
/src/MX/MegaMenu/view/frontend/requirejs-config.js:
--------------------------------------------------------------------------------
1 | var config = {
2 | map: {
3 | '*': {
4 | 'megaMenu': 'MX_MegaMenu/js/view/megamenu'
5 | }
6 | }
7 | };
8 |
--------------------------------------------------------------------------------
/src/MX/MegaMenu/registration.php:
--------------------------------------------------------------------------------
1 |
4 |
5 |
8 |
--------------------------------------------------------------------------------
/src/MX/MegaMenu/view/adminhtml/layout/mx_megamenu_menu_create.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/MX/MegaMenu/view/adminhtml/layout/mx_megamenu_menu_edit.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/src/MX/MegaMenu/view/adminhtml/layout/default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/MX/MegaMenu/etc/module.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/src/MX/MegaMenu/view/adminhtml/layout/mx_megamenu_importexport.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/src/MX/MegaMenu/Api/ImportHandlerInterface.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 0
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/MX/MegaMenu/view/frontend/templates/topmenu.phtml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/MX/MegaMenu/view/adminhtml/layout/mx_megamenu_menu_index.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/MX/MegaMenu/etc/adminhtml/routes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/src/MX/MegaMenu/view/adminhtml/layout/mx_megamenu_menu_form.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/src/MX/MegaMenu/Controller/Adminhtml/Menu/Index.php:
--------------------------------------------------------------------------------
1 | resultFactory->create(ResultFactory::TYPE_PAGE);
13 | $resultPage->getConfig()->getTitle()->prepend((__('MX Mega Menu')));
14 |
15 | return $resultPage;
16 | }
17 | }
--------------------------------------------------------------------------------
/src/MX/MegaMenu/view/adminhtml/layout/mx_megamenu_menu_export.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/src/MX/MegaMenu/view/adminhtml/layout/mx_megamenu_menu_import.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/src/MX/MegaMenu/view/adminhtml/requirejs-config.js:
--------------------------------------------------------------------------------
1 | var config = {
2 | map: {
3 | "*": {
4 | "nestable": "MX_MegaMenu/js/lib/jquery.nestable.min",
5 | "MXMegaMenu": "MX_MegaMenu/js/megamenu",
6 | "MXMegaMenuEditor": "MX_MegaMenu/js/megamenu-editor",
7 | "MXMegaMenuForm": "MX_MegaMenu/js/megamenu/form",
8 | "MXMegaMenuFormDialog": "MX_MegaMenu/js/megamenu/dialog",
9 | "MXMegaMenuFormBuilder": "MX_MegaMenu/js/megamenu/form/builder"
10 | }
11 | }
12 | };
13 |
--------------------------------------------------------------------------------
/src/MX/MegaMenu/Controller/Adminhtml/Menu/Export.php:
--------------------------------------------------------------------------------
1 | resultFactory->create(ResultFactory::TYPE_PAGE);
13 | $resultPage->getConfig()->getTitle()->prepend(__('MX Mega Menu - Export'));
14 |
15 | return $resultPage;
16 | }
17 | }
--------------------------------------------------------------------------------
/src/MX/MegaMenu/Controller/Adminhtml/Menu/Import.php:
--------------------------------------------------------------------------------
1 | resultFactory->create(ResultFactory::TYPE_PAGE);
13 | $resultPage->getConfig()->getTitle()->prepend(__('MX Mega Menu - Import'));
14 |
15 | return $resultPage;
16 | }
17 | }
--------------------------------------------------------------------------------
/src/MX/MegaMenu/Controller/Adminhtml/Menu/Create.php:
--------------------------------------------------------------------------------
1 | resultFactory->create(ResultFactory::TYPE_PAGE);
13 | $resultPage->getConfig()->getTitle()->prepend((__('MX Mega Menu - Create New Menu')));
14 |
15 | return $resultPage;
16 | }
17 | }
--------------------------------------------------------------------------------
/src/MX/MegaMenu/view/adminhtml/templates/editor/form.phtml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
15 |
--------------------------------------------------------------------------------
/src/MX/MegaMenu/view/adminhtml/templates/export.phtml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
--------------------------------------------------------------------------------
/src/MX/MegaMenu/view/adminhtml/web/css/megamenu-core.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: 'font-awesome-icons';
3 | src: url('../fonts/fa-solid.eot?diou9j');
4 | src: url('../fonts/fa-solid.eot?diou9j#iefix') format('embedded-opentype'),
5 | url('../fonts/fa-solid.ttf?diou9j') format('truetype'),
6 | url('../fonts/fa-solid.woff?diou9j') format('woff'),
7 | url('../fonts/fa-solid.woff2?diou9j') format('woff2'),
8 | url('../fonts/fa-solid.svg?diou9j#icomoon') format('svg');
9 | font-weight: normal;
10 | font-style: normal;
11 | }
12 |
13 | .admin__menu .level-0.item-mxmenuall > a:before {
14 | font-family: 'font-awesome-icons';
15 | content: '\f0c9';
16 | }
--------------------------------------------------------------------------------
/src/MX/MegaMenu/Model/ResourceModel/Menu/Collection.php:
--------------------------------------------------------------------------------
1 | _init(
21 | 'MX\MegaMenu\Model\Menu',
22 | 'MX\MegaMenu\Model\ResourceModel\Menu'
23 | );
24 | }
25 |
26 | }
--------------------------------------------------------------------------------
/src/MX/MegaMenu/view/adminhtml/templates/import.phtml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
--------------------------------------------------------------------------------
/src/MX/MegaMenu/view/adminhtml/layout/mx_megamenu_edit.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/src/MX/MegaMenu/Controller/Adminhtml/Menu/Form.php:
--------------------------------------------------------------------------------
1 | resultLayoutFactory = $resultLayoutFactory;
25 | parent::__construct($context);
26 | }
27 |
28 | public function execute()
29 | {
30 | $resultLayout = $this->resultLayoutFactory->create();
31 | $resultLayout->addHandle('overlay_popup');
32 |
33 | return $resultLayout;
34 | }
35 | }
--------------------------------------------------------------------------------
/src/MX/MegaMenu/view/frontend/layout/default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # MX_MegaMenu
2 | The module provides useful topmenu to be able to create complex menu structure in Magento 2.
3 |
4 | ## Compatibility
5 | Magento 2.X (tested in magento 2.2.11)
6 |
7 | ## Installation ##
8 |
9 | ### Composer ###
10 |
11 | 1. Make sure that you have added [Github's OAuth token](https://getcomposer.org/doc/articles/troubleshooting.md#api-rate-limit-and-oauth-tokens) to your composer configuration.
12 |
13 |
14 | 2. Add the Git repository to composer as the source of this package
15 | ```
16 | composer config repositories.mx-megamenu vcs git@github.com:inviqa/mx-megamenu.git
17 | ```
18 |
19 | 3. Run the command below from your project root directory.
20 | ```
21 | composer require "inviqa/mx-megamenu"
22 | ```
23 |
24 | ## Usage
25 | Enable the menu in the admin:
26 | MegaMenu > Settings > Enable Mega Menu
27 |
28 | The default topmenu will be automatically removed when the module is enabled:
29 | To move the megamenu in your layout structure the reference name is *mx.megamenu.topnav*
30 |
31 |
--------------------------------------------------------------------------------
/src/MX/MegaMenu/Block/Adminhtml/Menu/Export.php:
--------------------------------------------------------------------------------
1 | _objectId = 'menu_id';
23 | $this->_controller = 'adminhtml_menu';
24 | $this->_blockGroup = 'MX_MegaMenu';
25 |
26 | parent::_construct();
27 |
28 | $this->removeButton('reset');
29 | $this->removeButton('delete');
30 | $this->removeButton('save');
31 | }
32 |
33 | /**
34 | * Get Export Url
35 | *
36 | * @return string
37 | */
38 | public function getExportUrl()
39 | {
40 | return $this->getUrl('mx_megamenu/menu/exportpost');
41 | }
42 | }
--------------------------------------------------------------------------------
/src/MX/MegaMenu/Block/Adminhtml/Menu/Import.php:
--------------------------------------------------------------------------------
1 | _objectId = 'menu_id';
23 | $this->_controller = 'adminhtml_menu';
24 | $this->_blockGroup = 'MX_MegaMenu';
25 |
26 | parent::_construct();
27 |
28 | $this->removeButton('reset');
29 | $this->removeButton('delete');
30 | $this->removeButton('save');
31 | }
32 |
33 | /**
34 | * Get Import Url
35 | *
36 | * @return string
37 | */
38 | public function getImportUrl()
39 | {
40 | return $this->getUrl('mx_megamenu/menu/importpost');
41 | }
42 | }
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "inviqa/mx-megamenu",
3 | "description": "Inviqa - MX Mega Menu",
4 | "type": "magento2-module",
5 | "repositories": {
6 | "magento-repo": {
7 | "type": "composer",
8 | "url": "https://repo.magento.com/"
9 | }
10 | },
11 | "license": "MIT",
12 | "authors": [
13 | {
14 | "name": "Laszlo Kispal",
15 | "email": "lkispal@inviqa.com",
16 | "homepage": "https://github.com/kispali",
17 | "role": "Developer"
18 | }
19 | ],
20 | "require": {
21 | "symfony/yaml": "~2.1|~3.0",
22 | "magento/framework": "^100.1|^101.0",
23 | "magento/module-backend": "^100.1|^100.2",
24 | "magento/module-cms": "^101.0|^102.0",
25 | "magento/module-ui": "^100.1|^101.0",
26 | "magento/module-catalog": "^101.0|^102.0"
27 | },
28 | "config": {
29 | "bin-dir": "bin",
30 | "use-include-path": true
31 | },
32 | "autoload": {
33 | "files": [
34 | "src/MX/MegaMenu/registration.php"
35 | ],
36 | "psr-0": {
37 | "MX\\MegaMenu\\": "src"
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/MX/MegaMenu/Block/TopMenu/Children.php:
--------------------------------------------------------------------------------
1 | item = $item;
29 | }
30 |
31 | /**
32 | * Get item
33 | *
34 | * @return array
35 | */
36 | public function getItem()
37 | {
38 | return $this->item;
39 | }
40 |
41 | /**
42 | * Get item level
43 | *
44 | * @param array $item
45 | * @return integer
46 | */
47 | public function getItemLevel($item)
48 | {
49 | return isset($item['level']) ? $item['level'] : Item::LEVEL_DEFAULT;
50 | }
51 | }
--------------------------------------------------------------------------------
/src/MX/MegaMenu/Model/ResourceModel/Menu/Relation/Item/ReadHandler.php:
--------------------------------------------------------------------------------
1 | resourceMenu = $resourceMenu;
25 | }
26 |
27 | /**
28 | * @param object $entity
29 | * @param array $arguments
30 | * @return object
31 | */
32 | public function execute($entity, $arguments = [])
33 | {
34 | if ($entity->getMenuId()) {
35 | $items = $this->resourceMenu->lookupMenuItems((int)$entity->getMenuId());
36 | $entity->addSpecialMenuItems($items);
37 | $entity->setMenuItems($items);
38 | }
39 | return $entity;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/MX/MegaMenu/Model/ResourceModel/Menu/Relation/Store/ReadHandler.php:
--------------------------------------------------------------------------------
1 | resourceMenu = $resourceMenu;
25 | }
26 |
27 | /**
28 | * @param object $entity
29 | * @param array $arguments
30 | * @return object
31 | */
32 | public function execute($entity, $arguments = [])
33 | {
34 | if ($entity->getMenuId()) {
35 | $stores = $this->resourceMenu->lookupStoreIds((int)$entity->getMenuId());
36 | $entity->setData('store_id', $stores);
37 | $entity->setData('stores', $stores);
38 | }
39 | return $entity;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/MX/MegaMenu/etc/adminhtml/system.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/src/MX/MegaMenu/etc/adminhtml/menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
--------------------------------------------------------------------------------
/src/MX/MegaMenu/Data/Form/Element/Toggle.php:
--------------------------------------------------------------------------------
1 | getHtmlId();
16 |
17 | $beforeElementHtml = $this->getBeforeElementHtml();
18 | if ($beforeElementHtml) {
19 | $html .= '';
20 | }
21 |
22 | $html .= '';
23 | $html .= '_getUiId() . ' value="' .
24 | $this->getEscapedValue() . '" ' . $this->serialize($this->getHtmlAttributes()) . ' class="onoffswitch-checkbox" />';
25 | $html .= '';
27 | $html .= '
';
28 |
29 | $afterElementJs = $this->getAfterElementJs();
30 | if ($afterElementJs) {
31 | $html .= $afterElementJs;
32 | }
33 |
34 | $afterElementHtml = $this->getAfterElementHtml();
35 | if ($afterElementHtml) {
36 | $html .= '';
37 | }
38 |
39 | return $html;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/MX/MegaMenu/view/adminhtml/web/css/lib/jquery.nestable.min.css:
--------------------------------------------------------------------------------
1 | .dd{position:relative;display:block;margin:0;padding:0;max-width:600px;list-style:none;font-size:13px;line-height:20px}.dd-list{display:block;position:relative;margin:0;padding:0;list-style:none}.dd-list .dd-list{padding-left:30px}.dd-empty,.dd-item,.dd-placeholder{display:block;position:relative;margin:0;padding:0;min-height:20px;font-size:13px;line-height:20px}.dd-handle{display:block;height:30px;margin:5px 0;padding:5px 10px;color:#333;text-decoration:none;font-weight:700;border:1px solid #ccc;background:#fafafa;border-radius:3px;box-sizing:border-box}.dd-handle:hover{color:#2ea8e5;background:#fff}.dd-item>button{position:relative;cursor:pointer;float:left;width:25px;height:20px;margin:5px 0;padding:0;text-indent:100%;white-space:nowrap;overflow:hidden;border:0;background:0 0;font-size:12px;line-height:1;text-align:center;font-weight:700}.dd-item>button:before{display:block;position:absolute;width:100%;text-align:center;text-indent:0}.dd-item>button.dd-expand:before{content:'+'}.dd-item>button.dd-collapse:before{content:'-'}.dd-expand{display:none}.dd-collapsed .dd-collapse,.dd-collapsed .dd-list{display:none}.dd-collapsed .dd-expand{display:block}.dd-empty,.dd-placeholder{margin:5px 0;padding:0;min-height:30px;background:#f2fbff;border:1px dashed #b6bcbf;box-sizing:border-box;-moz-box-sizing:border-box}.dd-empty{border:1px dashed #bbb;min-height:100px;background-color:#e5e5e5;background-size:60px 60px;background-position:0 0,30px 30px}.dd-dragel{position:absolute;pointer-events:none;z-index:9999}.dd-dragel>.dd-item .dd-handle{margin-top:0}.dd-dragel .dd-handle{box-shadow:2px 4px 6px 0 rgba(0,0,0,.1)}.dd-nochildren .dd-placeholder{display:none}
--------------------------------------------------------------------------------
/src/MX/MegaMenu/Api/MenuRepositoryInterface.php:
--------------------------------------------------------------------------------
1 | menuFactory = $menuFactory;
34 | parent::__construct($context, $registry, $jsonDataHelper);
35 | }
36 |
37 | public function execute()
38 | {
39 | $resultRedirect = $this->resultRedirectFactory->create();
40 |
41 | $id = $this->getRequest()->getParam('menu_id');
42 | if ($id) {
43 | $menuModel = $this->menuFactory->create();
44 | $menuModel->load($id);
45 |
46 | if (!$menuModel->getMenuId()) {
47 | $this->messageManager->addErrorMessage(__('The menu with this specific ID does not exist.'));
48 | } else {
49 | $menuModel->delete();
50 | $this->messageManager->addSuccessMessage(__('The menu with this specific ID was successfully deleted.'));
51 | }
52 | }
53 |
54 | return $resultRedirect->setPath('*/*/');
55 | }
56 | }
--------------------------------------------------------------------------------
/src/MX/MegaMenu/view/adminhtml/web/js/megamenu/dialog.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'jquery',
3 | 'jquery/ui'
4 | ], function ($) {
5 | "use strict";
6 |
7 | // Widget for separate dialog instance to avoid the conflicts with other dialog instances opened by MediaBrowserUtility
8 | $.widget('mx.megaMenuDialog', {
9 | dialogId: 'mega-menu-dialog',
10 | title: 'Insert/Edit Form Data',
11 |
12 | _create: function() {
13 | this.getModalInstance();
14 | },
15 |
16 | /**
17 | * Get modal instance
18 | */
19 | getModalInstance: function() {
20 | var content = '';
21 |
22 | if (menuModal) {
23 | menuModal.html($(content).html());
24 | } else {
25 | menuModal = $(content).modal($.extend({
26 | title: this.title,
27 | modalClass: 'magento',
28 | type: 'slide',
29 | buttons: []
30 | }, []));
31 | }
32 | },
33 |
34 | openDialog: function(url, title) {
35 | if (typeof title !== 'undefined' && title !== '') {
36 | this.title += ' - ' + title;
37 | }
38 |
39 | menuModal.modal('setTitle', this.title);
40 | menuModal.modal('openModal');
41 |
42 | $.ajax({
43 | url: url,
44 | type: 'get',
45 | context: $(this),
46 | showLoader: true
47 | }).done(function (data) {
48 | menuModal.html(data).trigger('contentUpdated');
49 | });
50 | },
51 |
52 | /**
53 | * Close dialog.
54 | */
55 | closeDialog: function() {
56 | menuModal.modal('closeModal');
57 | }
58 | });
59 |
60 | return $.mx.megaMenuDialog;
61 | });
--------------------------------------------------------------------------------
/src/MX/MegaMenu/Controller/Adminhtml/Menu/Edit.php:
--------------------------------------------------------------------------------
1 | menuFactory = $menuFactory;
35 | parent::__construct($context, $registry, $jsonDataHelper);
36 | }
37 |
38 | public function execute()
39 | {
40 | $id = $this->getRequest()->getParam('menu_id');
41 | if ($id) {
42 | $menuModel = $this->menuFactory->create();
43 | $menuModel->load($id);
44 | if (!$menuModel->getId()) {
45 | $this->messageManager->addErrorMessage(__('The menu with this specific ID does not exist.'));
46 | $resultRedirect = $this->resultRedirectFactory->create();
47 |
48 | return $resultRedirect->setPath('*/*/');
49 | }
50 |
51 | // Register data to pass through to the Form
52 | $this->coreRegistry->register('mx_megamenu_menu', $menuModel);
53 | }
54 |
55 | $resultPage = $this->resultFactory->create(ResultFactory::TYPE_PAGE);
56 | $resultPage->getConfig()->getTitle()->prepend((__('MX Mega Menu - Edit Menu')));
57 |
58 | return $resultPage;
59 | }
60 | }
--------------------------------------------------------------------------------
/src/MX/MegaMenu/Controller/Adminhtml/Menu.php:
--------------------------------------------------------------------------------
1 | coreRegistry = $coreRegistry;
45 | $this->jsonDataHelper = $jsonDataHelper;
46 | parent::__construct($context);
47 | }
48 |
49 | /**
50 | * Send Response
51 | *
52 | * @param array $response
53 | *
54 | * @return Http
55 | */
56 | public function sendHtmlResponse($response)
57 | {
58 | return $this->getResponse()->representJson(
59 | $this->jsonDataHelper->jsonEncode($response)
60 | );
61 | }
62 |
63 | /**
64 | * Init page
65 | *
66 | * @param Page $resultPage
67 | * @return Page
68 | */
69 | protected function initPage($resultPage)
70 | {
71 | $resultPage->setActiveMenu('MX_MegaMenu::menu')
72 | ->addBreadcrumb(__('Mega Menu'), __('Mega Menu'))
73 | ->addBreadcrumb(__('Menu'), __('Menu'));
74 | return $resultPage;
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/MX/MegaMenu/Controller/Adminhtml/Menu/ImportPost.php:
--------------------------------------------------------------------------------
1 | importHandler = $importHandler;
34 |
35 | parent::__construct($context, $coreRegistry, $jsonDataHelper);
36 | }
37 |
38 | public function execute()
39 | {
40 | try {
41 | $file = $this->getRequest()->getFiles('file');
42 |
43 | if ($file) {
44 | /** @var $importHandler \MX\MegaMenu\Model\Menu\ImportHandler */
45 | $importHandler = $this->importHandler->create();
46 | $importHandler->importFromFile($file);
47 |
48 | $this->messageManager->addSuccessMessage(__('You successfully imported the menu items.'));
49 | }
50 | } catch (LocalizedException $e) {
51 | $this->messageManager->addErrorMessage($e->getMessage());
52 | } catch (\Exception $e) {
53 | $this->messageManager->addExceptionMessage($e, __('Something went wrong while importing the menu items.'));
54 | }
55 |
56 | /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */
57 | $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT);
58 |
59 | return $resultRedirect->setPath('mx_megamenu/menu/import');
60 | }
61 | }
--------------------------------------------------------------------------------
/src/MX/MegaMenu/view/frontend/templates/topmenu/structure.phtml:
--------------------------------------------------------------------------------
1 | getStructure();
6 |
7 | ?>
8 |
9 |
10 |
11 |
12 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/src/MX/MegaMenu/Model/ResourceModel/Menu/Relation/Store/SaveHandler.php:
--------------------------------------------------------------------------------
1 | metadataPool = $metadataPool;
36 | $this->resourceMenu = $resourceMenu;
37 | }
38 |
39 | /**
40 | * @param object $entity
41 | * @param array $arguments
42 | * @return object
43 | * @throws \Exception
44 | */
45 | public function execute($entity, $arguments = [])
46 | {
47 | $entityMetadata = $this->metadataPool->getMetadata(MenuInterface::class);
48 | $linkField = $entityMetadata->getLinkField();
49 |
50 | $connection = $entityMetadata->getEntityConnection();
51 |
52 | $oldStores = $this->resourceMenu->lookupStoreIds((int)$entity->getMenuId());
53 | $newStores = (array)$entity->getStores();
54 |
55 | $table = $this->resourceMenu->getTable('mx_megamenu_store');
56 |
57 | $delete = array_diff($oldStores, $newStores);
58 | if (!empty($delete)) {
59 | $where = [
60 | $linkField . ' = ?' => (int)$entity->getData($linkField),
61 | 'store_id IN (?)' => $delete,
62 | ];
63 | $connection->delete($table, $where);
64 | }
65 |
66 | $insert = array_diff($newStores, $oldStores);
67 | if (!empty($insert)) {
68 | $data = [];
69 | foreach ($insert as $storeId) {
70 | $data[] = [
71 | $linkField => (int)$entity->getData($linkField),
72 | 'store_id' => (int)$storeId,
73 | ];
74 | }
75 | $connection->insertMultiple($table, $data);
76 | }
77 |
78 | return $entity;
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/src/MX/MegaMenu/Ui/Component/Listing/Column/Actions.php:
--------------------------------------------------------------------------------
1 | urlBuilder = $urlBuilder;
39 | parent::__construct($context, $uiComponentFactory, $components, $data);
40 | }
41 |
42 | /**
43 | * Prepare Data Source
44 | *
45 | * @param array $dataSource
46 | * @return array
47 | */
48 | public function prepareDataSource(array $dataSource)
49 | {
50 | if (isset($dataSource['data']['items'])) {
51 | foreach ($dataSource['data']['items'] as & $item) {
52 | $name = $this->getData('name');
53 | if (isset($item['menu_id'])) {
54 | $item[$name]['edit'] = [
55 | 'href' => $this->urlBuilder->getUrl(self::URL_PATH_EDIT, ['menu_id' => $item['menu_id']]),
56 | 'label' => __('Edit')
57 | ];
58 |
59 | $item[$name]['delete'] = [
60 | 'href' => $this->urlBuilder->getUrl(self::URL_PATH_DELETE, ['menu_id' => $item['menu_id']]),
61 | 'label' => __('Delete'),
62 | 'confirm' => [
63 | 'title' => __('Delete'),
64 | 'message' => __('Are you sure you wan\'t to delete this record?')
65 | ]
66 | ];
67 | }
68 | }
69 | }
70 |
71 | return $dataSource;
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/MX/MegaMenu/Controller/Adminhtml/Menu/ExportPost.php:
--------------------------------------------------------------------------------
1 | menuFactory = $menuFactory;
41 | $this->menuRepository = $menuRepository;
42 |
43 | parent::__construct($context, $coreRegistry, $jsonDataHelper);
44 | }
45 |
46 | public function execute()
47 | {
48 | $response = [
49 | 'status' => false,
50 | 'result' => '',
51 | 'redirect' => $this->getUrl('mx_megamenu/menu/export')
52 | ];
53 |
54 | try {
55 | $result = [];
56 | $items = $this->menuRepository->getAllItems();
57 |
58 | foreach ($items as $item) {
59 | $id = $item->getMenuId();
60 | $menu = $this->menuRepository->getById($id);
61 | $result[$id] = $menu->getData();
62 |
63 | $response['status'] = true;
64 | $response['result'] = json_encode($result);
65 |
66 | $this->messageManager->addSuccessMessage(__('You successfully exported the menu items.'));
67 | }
68 | } catch (LocalizedException $e) {
69 | $this->messageManager->addErrorMessage($e->getMessage());
70 | } catch (\Exception $e) {
71 | $this->messageManager->addExceptionMessage($e, __('Something went wrong while exporting the menu items.'));
72 | }
73 |
74 | return $this->sendHtmlResponse($response);
75 | }
76 | }
--------------------------------------------------------------------------------
/src/MX/MegaMenu/Api/Data/MenuInterface.php:
--------------------------------------------------------------------------------
1 | getMenuJson();
5 | ?>
6 |
7 |
30 |
31 |
50 |
51 |
54 |
55 |
58 |
59 |
--------------------------------------------------------------------------------
/src/MX/MegaMenu/Setup/UpgradeSchema.php:
--------------------------------------------------------------------------------
1 | getVersion(), '1.1.0', '<')) {
17 | $this->addCategoryTypeField($setup);
18 | }
19 |
20 | if (version_compare($context->getVersion(), '1.2.0', '<')) {
21 | $this->addCustomClassField($setup);
22 | $this->addRemoveCategoryLinkField($setup);
23 | }
24 | }
25 |
26 | /**
27 | * Add category type title
28 | *
29 | * @param SchemaSetupInterface $setup
30 | * @return $this
31 | */
32 | protected function addCategoryTypeField(SchemaSetupInterface $setup)
33 | {
34 | $setup->getConnection()->addColumn(
35 | $setup->getTable(self::TABLE_MEGAMENU_ITEM),
36 | 'content_category_type',
37 | [
38 | 'type' => Table::TYPE_TEXT,
39 | 'nullable' => false,
40 | 'size' => 10,
41 | 'after' => 'content_category',
42 | 'comment' => 'Content Category Type'
43 | ]
44 | );
45 | return $this;
46 | }
47 |
48 | /**
49 | * Add custom class field
50 | *
51 | * @param SchemaSetupInterface $setup
52 | * @return $this
53 | */
54 | protected function addCustomClassField(SchemaSetupInterface $setup)
55 | {
56 | $setup->getConnection()->addColumn(
57 | $setup->getTable(self::TABLE_MEGAMENU_ITEM),
58 | 'custom_class',
59 | [
60 | 'type' => Table::TYPE_TEXT,
61 | 'nullable' => false,
62 | 'size' => 255,
63 | 'after' => 'content_category_type',
64 | 'comment' => 'Custom Class'
65 | ]
66 | );
67 | return $this;
68 | }
69 |
70 | /**
71 | * Add remove category link field
72 | *
73 | * @param SchemaSetupInterface $setup
74 | * @return $this
75 | */
76 | protected function addRemoveCategoryLinkField(SchemaSetupInterface $setup)
77 | {
78 | $setup->getConnection()->addColumn(
79 | $setup->getTable(self::TABLE_MEGAMENU_ITEM),
80 | 'remove_category_anchor',
81 | [
82 | 'type' => Table::TYPE_SMALLINT,
83 | 'nullable' => false,
84 | 'unsigned' => true,
85 | 'default' => 0,
86 | 'after' => 'custom_class',
87 | 'comment' => 'Remove Category Link'
88 | ]
89 | );
90 | return $this;
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/src/MX/MegaMenu/view/frontend/templates/topmenu/structure/children.phtml:
--------------------------------------------------------------------------------
1 | getItem();
6 | $level = $block->getItemLevel($item);
7 | $levelClass = 'level' . $level;
8 | $first = $block->getFirst();
9 |
10 | ?>
11 |
12 |
13 |
37 |
38 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/src/MX/MegaMenu/etc/di.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | - MX\MegaMenu\Api\MenuRepositoryInterface
8 |
9 |
10 |
11 |
12 |
13 |
14 | -
15 |
- mx_megamenu
16 | - menu_id
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | - Magento\Framework\EntityManager\AbstractModelHydrator
25 |
26 |
27 |
28 |
29 |
30 |
31 | -
32 |
-
33 |
- MX\MegaMenu\Model\ResourceModel\Menu\Relation\Store\ReadHandler
34 | - MX\MegaMenu\Model\ResourceModel\Menu\Relation\Item\ReadHandler
35 |
36 | -
37 |
- MX\MegaMenu\Model\ResourceModel\Menu\Relation\Store\SaveHandler
38 | - MX\MegaMenu\Model\ResourceModel\Menu\Relation\Item\SaveHandler
39 |
40 | -
41 |
- MX\MegaMenu\Model\ResourceModel\Menu\Relation\Store\SaveHandler
42 | - MX\MegaMenu\Model\ResourceModel\Menu\Relation\Item\SaveHandler
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | - MX\MegaMenu\Model\ResourceModel\Menu\Grid\Collection
52 |
53 |
54 |
55 |
56 |
57 | mx_megamenu
58 | MX\MegaMenu\Model\ResourceModel\Menu
59 |
60 |
61 |
--------------------------------------------------------------------------------
/src/MX/MegaMenu/view/frontend/web/css/megamenu.css:
--------------------------------------------------------------------------------
1 | .nav-sections-item-content > .mx-megamenu {
2 | display: block;
3 | }
4 |
5 | /* Fix the iOS background scrolling issue */
6 | .megamenu-init-toggle.nav-open,
7 | .megamenu-init-toggle body {
8 | height: 100%;
9 | overflow: hidden;
10 | }
11 | /* Fix the iOS background scrolling issue */
12 |
13 | .mx-megamenu .navigation > ul {
14 | display: flex;
15 | flex-direction: column;
16 | margin: 0;
17 | padding: 0;
18 | list-style-type: none;
19 | }
20 |
21 | .mx-megamenu .navigation .mx-megamenu__item.level-top {
22 | padding: 0.5em;
23 | }
24 |
25 | .mx-megamenu .navigation .mx-megamenu__item.level-top .mx-megamenu__link {
26 | display: block;
27 | padding: 0.5em;
28 | }
29 |
30 | .mx-megamenu .navigation .mx-megamenu__item > .mx-megamenu__link > span {
31 | display: block;
32 | }
33 |
34 | .mx-megamenu .navigation .mx-megamenu__item.level-top .mx-megamenu__submenu {
35 | display: none;
36 | margin-left: 1.25em;
37 | }
38 |
39 | .mx-megamenu .navigation .mx-megamenu__item.current > .mx-megamenu__submenu {
40 | display: block;
41 | }
42 |
43 | .mx-megamenu__sidebar,
44 | .mx-megamenu__header,
45 | .mx-megamenu__footer {
46 | display: none;
47 | }
48 |
49 | @media screen and (max-width: 1024px) {
50 | .navigation li.level0 {
51 | border-bottom: 1px solid #cccccc;
52 | }
53 | }
54 |
55 | @media screen and (min-width: 1025px) {
56 | .mx-megamenu .navigation > ul {
57 | position: relative;
58 | flex-direction: row;
59 | justify-content: center;
60 | }
61 |
62 | .mx-megamenu .navigation .mx-megamenu__item.level-top {
63 | position: static;
64 | padding: 0 1em;
65 | }
66 |
67 | .mx-megamenu .navigation .mx-megamenu__item.level-top.current > .mx-megamenu__submenu {
68 | display: flex;
69 | left: 0;
70 | width: 100%;
71 | border-color: #cccccc;
72 | border-left: 0;
73 | border-right: 0;
74 | margin-left: 0;
75 | }
76 |
77 | .mx-megamenu .navigation .mx-megamenu__item.level-top > .mx-megamenu__link {
78 | display: inline-block;
79 | padding: 0;
80 | }
81 |
82 | .mx-megamenu .navigation .mx-megamenu__item.level-top.current > .mx-megamenu__link {
83 | position: relative;
84 | }
85 |
86 | .mx-megamenu__content {
87 | display: flex;
88 | justify-content: space-between;
89 | }
90 |
91 | .mx-megamenu .navigation .mx-megamenu__item .mx-megamenu__categories {
92 | display: flex;
93 | width: 100%;
94 | flex-wrap: wrap;
95 | -webkit-box-orient: vertical;
96 | -webkit-box-direction: normal;
97 | flex-direction: column;
98 | max-height: 500px;
99 | }
100 |
101 | .mx-megamenu__main-content,
102 | .mx-megamenu__categories {
103 | padding: 0 1em;
104 | }
105 |
106 | .mx-megamenu .navigation .mx-megamenu__item .mx-megamenu__categories .mx-megamenu__category {
107 | margin: 0 0.5em;
108 | }
109 |
110 | .mx-megamenu__item .mx-megamenu__categories .mx-megamenu__category>.mx-megamenu__link {
111 | font-weight: bold;
112 | }
113 |
114 | .mx-megamenu .navigation .mx-megamenu__item .mx-megamenu__categories .mx-megamenu__submenu {
115 | display: flex;
116 | flex-direction: column;
117 | }
118 |
119 | .mx-megamenu .navigation .mx-megamenu__item .mx-megamenu__categories .mx-megamenu__category-item .mx-megamenu__link {
120 | padding: 0 3px;
121 | }
122 |
123 | .mx-megamenu__sidebar,
124 | .mx-megamenu__header,
125 | .mx-megamenu__footer {
126 | display: flex;
127 | width: auto;
128 | text-align: center;
129 | }
130 |
131 | .mx-megamenu__header,
132 | .mx-megamenu__footer {
133 | align-self: center;
134 | }
135 | }
--------------------------------------------------------------------------------
/src/MX/MegaMenu/Block/Adminhtml/Menu/Edit/Settings/Form.php:
--------------------------------------------------------------------------------
1 | store = $store;
44 | parent::__construct($context, $registry, $formFactory, $data);
45 | }
46 |
47 | protected function _construct()
48 | {
49 | parent::_construct();
50 |
51 | $this->setId('settings_form');
52 | }
53 |
54 | protected function _prepareForm()
55 | {
56 | $menuModel = $this->_coreRegistry->registry('mx_megamenu_menu');
57 | $menuId = $this->getRequest()->getParam('menu_id', 0);
58 |
59 | $form = $this->_formFactory->create(
60 | ['data' =>
61 | [
62 | 'id' => 'settings_form',
63 | 'enctype' => 'multipart/form-data',
64 | 'action' => $this->getData('action'),
65 | 'method' => 'post'
66 | ]
67 | ]
68 | );
69 |
70 | $fieldset = $form->addFieldset(
71 | 'settings_fieldset',
72 | [
73 | 'legend' => __(''),
74 | 'class' => 'fieldset-wide'
75 | ]
76 | );
77 |
78 | if ($menuId) {
79 | $fieldset->addField(
80 | 'menu_id',
81 | 'hidden',
82 | [
83 | 'name' => 'menu_id',
84 | 'value' => $menuId
85 | ]
86 | );
87 | }
88 |
89 | $fieldset->addField(
90 | 'enabled',
91 | 'MX\MegaMenu\Data\Form\Element\Toggle',
92 | [
93 | 'label' => __('Enabled'),
94 | 'title' => __('Enabled'),
95 | 'required' => false,
96 | 'name' => 'status',
97 | 'value' => '1',
98 | 'checked' => $menuModel ? $menuModel->getStatus() : ''
99 | ]
100 | );
101 |
102 | $fieldset->addField(
103 | 'name',
104 | 'text',
105 | [
106 | 'label' => __('Name'),
107 | 'title' => __('Name'),
108 | 'required' => true,
109 | 'name' => 'name',
110 | 'value' => $menuModel ? $menuModel->getName() : ''
111 | ]
112 | );
113 |
114 | $fieldset->addField(
115 | 'store_id',
116 | 'multiselect',
117 | [
118 | 'name' => 'store_id',
119 | 'label' => __('Store View'),
120 | 'title' => __('Store View'),
121 | 'required' => true,
122 | 'values' => $this->store->getStoreValuesForForm(false, true),
123 | 'value' => $menuModel ? $menuModel->getStoreId() : self::DEFAULT_STORE_ID
124 | ]
125 | );
126 |
127 | $form->setUseContainer(true);
128 | $this->setForm($form);
129 |
130 | return parent::_prepareForm();
131 | }
132 | }
--------------------------------------------------------------------------------
/src/MX/MegaMenu/Model/ResourceModel/Menu/Relation/Item/SaveHandler.php:
--------------------------------------------------------------------------------
1 | metadataPool = $metadataPool;
36 | $this->resourceMenu = $resourceMenu;
37 | }
38 |
39 | /**
40 | * @param object $entity
41 | * @param array $arguments
42 | * @return object
43 | * @throws \Exception
44 | */
45 | public function execute($entity, $arguments = [])
46 | {
47 | $entityMetadata = $this->metadataPool->getMetadata(MenuInterface::class);
48 | $linkField = $entityMetadata->getLinkField();
49 |
50 | $connection = $entityMetadata->getEntityConnection();
51 |
52 | $oldItems = $this->resourceMenu->lookupMenuItems((int)$entity->getMenuId());
53 | $oldItemIds = array_keys($oldItems);
54 | $newItemIds = (array)$entity->getMenuItemIds();
55 |
56 | $table = $this->resourceMenu->getTable('mx_megamenu_item');
57 |
58 | // Delete old items
59 | $delete = array_diff($oldItemIds, $newItemIds);
60 | if (!empty($delete)) {
61 | $where = [
62 | $linkField . ' = ?' => (int)$entity->getData($linkField),
63 | 'menu_item_id IN (?)' => $delete,
64 | ];
65 | $connection->delete($table, $where);
66 | }
67 |
68 | // Remove special menu items before save
69 | $menuItems = $entity->getMenuItems();
70 | $entity->removeSpecialMenuItems($menuItems);
71 |
72 | // Insert new items
73 | $insert = array_diff($newItemIds, $oldItemIds);
74 | if (!empty($insert)) {
75 | $data = [];
76 | foreach ($insert as $itemId) {
77 | $data[$itemId] = [
78 | $linkField => (int)$entity->getData($linkField),
79 | 'menu_item_id' => null,
80 | ];
81 |
82 | foreach ($menuItems as $menuItem) {
83 | if ($menuItem['menu_item_id'] == $itemId) {
84 | foreach ($menuItem as $name => $value) {
85 | if ($name !== 'menu_item_id') {
86 | $data[$itemId][$name] = $value;
87 | }
88 | }
89 | }
90 | }
91 | }
92 | $connection->insertMultiple($table, $data);
93 | }
94 |
95 | /**
96 | * Update existing items
97 | */
98 | if (count($oldItemIds) > 0 && count($menuItems) > 0) {
99 | foreach ($menuItems as $menuItem) {
100 | if (in_array($menuItem['menu_item_id'], $oldItemIds)) {
101 | $where = [
102 | $linkField . ' = ?' => (int)$entity->getData($linkField),
103 | 'menu_item_id = (?)' => $menuItem['menu_item_id'],
104 | ];
105 | unset($menuItem['menu_id']);
106 | unset($menuItem['menu_item_id']);
107 | $connection->update($table, $menuItem, $where);
108 | }
109 | }
110 | }
111 |
112 | return $entity;
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/src/MX/MegaMenu/Block/Adminhtml/Menu/Edit.php:
--------------------------------------------------------------------------------
1 | coreRegistry = $registry;
61 | $this->messageManager = $messageManager;
62 | $this->menuFactory = $menuFactory;
63 | $this->menuItemFactory = $menuItemFactory;
64 | parent::__construct($context, $data);
65 | }
66 |
67 | /**
68 | * Initialize cms page edit block
69 | *
70 | * @return void
71 | */
72 | protected function _construct()
73 | {
74 | $this->_objectId = 'menu_id';
75 | $this->_controller = 'adminhtml_menu';
76 | $this->_blockGroup = 'MX_MegaMenu';
77 |
78 | parent::_construct();
79 | }
80 |
81 | /**
82 | * Get Menu Json String
83 | *
84 | * @return string
85 | */
86 | public function getMenuJson()
87 | {
88 | /** @var $menuModel \MX\MegaMenu\Model\Menu|null */
89 | $menuModel = $this->coreRegistry->registry('mx_megamenu_menu');
90 |
91 | if ($menuModel) {
92 | $items = $menuModel->getSortedMenuItems();
93 | return $this->encodeItems($items);
94 | }
95 |
96 | return '';
97 | }
98 |
99 | /**
100 | * Get Edit form Url
101 | *
102 | * @return string
103 | */
104 | public function getEditFormUrl()
105 | {
106 | return $this->getUrl('mx_megamenu/menu/form');
107 | }
108 |
109 | /**
110 | * Get Save Form Url
111 | *
112 | * @return string
113 | */
114 | public function getSaveFormUrl()
115 | {
116 | return $this->getUrl('mx_megamenu/menu/save');
117 | }
118 |
119 | /**
120 | * Get encoded string - useful for widget encoded strings
121 | *
122 | * @param array $items
123 | * @return mixed
124 | */
125 | protected function encodeItems(&$items)
126 | {
127 | $menuItem = $this->menuItemFactory->create();
128 | foreach ($items as &$item) {
129 | foreach ($item as $name => $value) {
130 | if ($menuItem->needEncode($name)) {
131 | $value = $menuItem->decodeContent($value);
132 | $value = $menuItem->encodeSpecialCharacters($value);
133 | $item[$name] = base64_encode($value);
134 | }
135 | }
136 | }
137 |
138 | return json_encode($items);
139 | }
140 |
141 | /**
142 | * Check permission for passed action
143 | *
144 | * @param string $resourceId
145 | * @return bool
146 | */
147 | protected function isAllowedAction($resourceId)
148 | {
149 | return $this->_authorization->isAllowed($resourceId);
150 | }
151 | }
--------------------------------------------------------------------------------
/src/MX/MegaMenu/Block/TopMenu.php:
--------------------------------------------------------------------------------
1 | storeManager = $storeManager;
55 | $this->menuRepository = $menuRepository;
56 | }
57 |
58 | protected function _construct()
59 | {
60 | parent::_construct();
61 |
62 | $this->addData(
63 | [
64 | 'cache_lifetime' => 86400,
65 | 'cache_tags' => [Menu::CACHE_TAG]
66 | ]
67 | );
68 | }
69 |
70 | /**
71 | * Get key pieces for caching block content
72 | *
73 | * @return array
74 | */
75 | public function getCacheKeyInfo()
76 | {
77 | return [
78 | self::CACHE_KEY,
79 | $this->storeManager->getStore()->getId(),
80 | $this->_design->getDesignTheme()->getId(),
81 | 'template' => $this->getTemplate(),
82 | $this->getData('menu_id')
83 | ];
84 | }
85 |
86 | /**
87 | * Get structure
88 | *
89 | * @return array|false
90 | * @throws \Magento\Framework\Exception\LocalizedException
91 | * @throws \Magento\Framework\Exception\NoSuchEntityException
92 | */
93 | public function getStructure()
94 | {
95 | $storeId = $this->storeManager->getStore()->getId();
96 | /** @var \MX\MegaMenu\Model\Menu $menu */
97 | $menu = $this->menuRepository->getByStoreId($storeId);
98 | if ($menu->getMenuId() && $menu->isActive()) {
99 | return $menu->getProcessedMenuItems();
100 | }
101 |
102 | return false;
103 | }
104 |
105 | /**
106 | * Render children items
107 | *
108 | * @param array $item
109 | * @param boolean $firstLevelChild
110 | * @throws \Magento\Framework\Exception\LocalizedException
111 | */
112 | public function renderChildren($item, $firstLevelChild = false)
113 | {
114 | /** @var $block MX\MegaMenu\Block\TopMenu\Children */
115 | $block = $this->getChildBlock(self::CHILDREN_ALIAS);
116 | $block->setFirst($firstLevelChild); // First level child shouldn't be rendered again as it is already rendered in the parent
117 | $block->setItem($item);
118 |
119 | return $this->getChildHtml(self::CHILDREN_ALIAS, false);
120 | }
121 |
122 | /**
123 | * Can show content
124 | *
125 | * @param array $item
126 | * @return boolean
127 | */
128 | public function canShowContent($item)
129 | {
130 | return !empty($item['header_status'])
131 | || !empty($item['content_status'])
132 | || !empty($item['leftside_status'])
133 | || !empty($item['rightside_status'])
134 | || !empty($item['footer_status'])
135 | || isset($item['children']);
136 | }
137 |
138 | /**
139 | * Return identifiers for produced content
140 | *
141 | * @return array
142 | */
143 | public function getIdentities()
144 | {
145 | return [Menu::CACHE_TAG . '_' . $this->getBlockId()];
146 | }
147 | }
--------------------------------------------------------------------------------
/src/MX/MegaMenu/Controller/Adminhtml/Menu/Save.php:
--------------------------------------------------------------------------------
1 | dataPersistor = $dataPersistor;
49 | $this->menuFactory = $menuFactory;
50 | $this->menuRepository = $menuRepository;
51 |
52 | parent::__construct($context, $coreRegistry, $jsonDataHelper);
53 | }
54 |
55 | /**
56 | * Save action
57 | */
58 | public function execute()
59 | {
60 | $response = [
61 | 'status' => false,
62 | 'url' => $this->getUrl('*/*/')
63 | ];
64 |
65 | $resultRedirect = $this->resultRedirectFactory->create();
66 | $data = $this->getRequest()->getPostValue();
67 | if ($data) {
68 | if (empty($data['menu_id'])) {
69 | $data['menu_id'] = null;
70 | }
71 |
72 | $this->coreRegistry->register('mx_megamenu_menu_items', $data['items']);
73 |
74 | /** @var \MX\MegaMenu\Model\Menu $model */
75 | $model = $this->menuFactory->create();
76 |
77 | $id = $this->getRequest()->getParam('menu_id');
78 | if ($id) {
79 | try {
80 | $model = $this->menuRepository->getById($id);
81 | } catch (LocalizedException $e) {
82 | $this->messageManager->addErrorMessage(__('This menu no longer exists.'));
83 | $response['url'] = $resultRedirect->setPath('*/*/');
84 |
85 | return $this->sendHtmlResponse($response);
86 | }
87 | }
88 |
89 | $model->setData($data);
90 |
91 | try {
92 | $this->menuRepository->save($model);
93 | $this->messageManager->addSuccessMessage(__('You saved the menu.'));
94 | $this->dataPersistor->clear('mx_megamenu');
95 | if ($this->getRequest()->getParam('back')) {
96 | $response['url'] = $resultRedirect->setPath('*/*/edit', ['menu_id' => $model->getId()]);
97 |
98 | return $this->sendHtmlResponse($response);
99 | }
100 |
101 | $response['status'] = true;
102 | $response['url'] = $resultRedirect->setPath('*/*/');
103 |
104 | return $this->sendHtmlResponse($response);
105 | } catch (LocalizedException $e) {
106 | $this->messageManager->addErrorMessage($e->getMessage());
107 | } catch (\Exception $e) {
108 | $this->messageManager->addExceptionMessage($e, __('Something went wrong while saving the menu.'));
109 | }
110 |
111 | $this->dataPersistor->set('mx_megamenu', $data);
112 |
113 | $response['url'] = $resultRedirect->setPath(
114 | '*/*/edit',
115 | [
116 | 'menu_id' => $this->getRequest()->getParam('menu_id')
117 | ]
118 | );
119 | }
120 |
121 | return $this->sendHtmlResponse($response);
122 | }
123 | }
--------------------------------------------------------------------------------
/src/MX/MegaMenu/view/adminhtml/web/css/megamenu.css:
--------------------------------------------------------------------------------
1 | /* Font Awesome icons */
2 | @font-face {
3 | font-family: 'font-awesome-icons';
4 | src: url('../fonts/fa-solid.eot');
5 | src: url('../fonts/fa-solid.eot#iefix') format('embedded-opentype'),
6 | url('../fonts/fa-solid.ttf') format('truetype'),
7 | url('../fonts/fa-solid.woff') format('woff'),
8 | url('../fonts/fa-solid.woff2') format('woff2'),
9 | url('../fonts/fa-solid.svg#icomoon') format('svg');
10 | font-weight: normal;
11 | font-style: normal;
12 | }
13 | @-webkit-keyframes blink {
14 | 0% { border-color: #eb5202; }
15 | 100% { border-color: #e3e3e3; }
16 | }
17 | @-moz-keyframes blink {
18 | 0% { border-color: #eb5202; }
19 | 100% { border-color: #e3e3e3; }
20 | }
21 | @-keyframes blink {
22 | 0% { border-color: #eb5202; }
23 | 100% { border-color: #e3e3e3; }
24 | }
25 | .fa {
26 | font-family: 'font-awesome-icons';
27 | font-size: 20px;
28 | line-height: 30px;
29 | color: #007bdb;
30 | }
31 | .fa-edit:before {
32 | content: "\f044";
33 | }
34 | .fa-remove:before {
35 | content: "\f1f8";
36 | }
37 | .fa-hand:before {
38 | content: "\f255";
39 | }
40 | .fa-caret-up,
41 | .fa-caret-down {
42 | font-size: 30px;
43 | }
44 | .fa-caret-up:before {
45 | content: "\f0d8";
46 | }
47 | .fa-caret-down:before {
48 | content: "\f0d7";
49 | }
50 | /* Font Awesome icons */
51 | .megamenu-editor a:hover {
52 | text-decoration: none;
53 | }
54 | .megamenu-editor .input-text {
55 | padding: 3px 6px;
56 | }
57 | .megamenu-editor > .actions {
58 | margin: 10px 0;
59 | }
60 | .megamenu-editor .structure {
61 | margin-top: 20px;
62 | }
63 | .megamenu-editor .structure .dd-list {
64 | margin-left: 50px;
65 | margin-top: 20px;
66 | }
67 | .megamenu-editor .structure .menu-item {
68 | position: relative;
69 | padding: 10px 0;
70 | background-color: #f8f8f8;
71 | border: 1px solid #e3e3e3;
72 | border-left: 0;
73 | border-right: 0;
74 | }
75 | .megamenu-editor .structure .menu-item .btn-caret {
76 | display: none;
77 | padding: 0 10px;
78 | border: 0;
79 | background-color: #f8f8f8;
80 | }
81 | .megamenu-editor .structure .menu-item .btn-caret.show {
82 | display: block;
83 | }
84 | .megamenu-editor .structure .menu-item .actions {
85 | float: right;
86 | display: inline-block;
87 | margin-left: 10px;
88 | margin-top: 4px;
89 | }
90 | .megamenu-editor .structure .menu-item .actions a {
91 | padding: 0 10px;
92 | }
93 | .megamenu-editor .structure .menu-item .actions a:hover {
94 | text-decoration: none;
95 | }
96 | .megamenu-editor .structure .menu-item .label {
97 | font-weight: bold;
98 | padding: 0 10px;
99 | font-size: 14px;
100 | }
101 | .megamenu-editor .structure .menu-item .drag-menu {
102 | display: inline-block;
103 | background-color: #f8f8f8;
104 | width: 74%;
105 | cursor: pointer;
106 | border: 0;
107 | padding: 0 10px;
108 | }
109 | .megamenu-editor .structure .menu-item .drag-menu:hover {
110 | background: none;
111 | }
112 | .megamenu-editor .structure .menu-item.new {
113 | animation: blink 1s linear 1s 3;
114 | -webkit-animation: blink 1s linear 1s 3;
115 | -moz-animation: blink 1s linear 1s 3;
116 | }
117 | .megamenu-editor .megamenu-tabs {
118 | display: none;
119 | }
120 | .megamenu-editor .megamenu-tabs.active {
121 | display: block;
122 | }
123 | .megamenu-editor .nestable {
124 | max-width: 100%;
125 | }
126 | .megamenu-editor .nestable .dd-empty {
127 | display: none;
128 | }
129 | .megamenu-editor .nestable .dd-actions {
130 | display: inline-block;
131 | }
132 | #tabs-menu {
133 | list-style-type: none;
134 | display: flex;
135 | background-color: #f8f8f8;
136 | }
137 | #tabs-menu li {
138 | display: inline-block;
139 | border-top: 3px solid #f8f8f8;
140 | }
141 | #tabs-menu li.active {
142 | background-color: #ffffff;
143 | border-top: 3px solid #007bdb;
144 | }
145 | #tabs-menu li a {
146 | display: inline-block;
147 | padding: 20px 40px;
148 | color: #231d1a;
149 | }
150 | .megamenu-editor .megamenu-tabs {
151 | padding: 10px;
152 | }
153 | .hidden-form {
154 | display: none;
155 | }
156 | .megamenu-container {
157 | margin: 10px 0;
158 | }
159 | .megamenu-container .text {
160 | padding: 10px 0;
161 | }
162 | .megamenu-container .input-field {
163 | margin: 10px 0 30px;
164 | }
165 | .dd-dragel > .dd-item > .dd-handle.drag-menu {
166 | height: 46px;
167 | }
--------------------------------------------------------------------------------
/src/MX/MegaMenu/Model/MenuRepository.php:
--------------------------------------------------------------------------------
1 | resource = $resource;
50 | $this->menuFactory = $menuFactory;
51 | $this->menuCollectionFactory = $menuCollectionFactory;
52 | $this->storeManager = $storeManager;
53 | }
54 |
55 | /**
56 | * Save Menu data
57 | *
58 | * @param MenuInterface $menu
59 | * @return Menu
60 | * @throws CouldNotSaveException
61 | */
62 | public function save(MenuInterface $menu)
63 | {
64 | if (empty($menu->getStores())) {
65 | $menu->setStores($this->storeManager->getStore()->getId());
66 | }
67 |
68 | try {
69 | $this->resource->save($menu);
70 | } catch (\Exception $exception) {
71 | throw new CouldNotSaveException(__($exception->getMessage()));
72 | }
73 |
74 | return $menu;
75 | }
76 |
77 | /**
78 | * Load Menu data by given Menu Identity
79 | *
80 | * @param string $menuId
81 | * @return Menu
82 | * @throws \Magento\Framework\Exception\NoSuchEntityException
83 | */
84 | public function getById($menuId)
85 | {
86 | $menu = $this->menuFactory->create();
87 | $this->resource->load($menu, $menuId);
88 | if (!$menu->getMenuId()) {
89 | throw new NoSuchEntityException(__('Menu with id "%1" does not exist.', $menuId));
90 | }
91 |
92 | return $menu;
93 | }
94 |
95 | /**
96 | * Load Menu data by given Store Id
97 | *
98 | * @param $storeId
99 | * @return Menu
100 | */
101 | public function getByStoreId($storeId)
102 | {
103 | $menu = $this->menuFactory->create();
104 | $menu->setStores($storeId);
105 | $this->resource->load($menu, $storeId, 'store_id');
106 |
107 | return $menu;
108 | }
109 |
110 | /**
111 | * Get all items
112 | *
113 | * @return MenuInterface
114 | */
115 | public function getAllItems()
116 | {
117 | $collection = $this->menuCollectionFactory->create();
118 |
119 | return $collection->getItems();
120 | }
121 |
122 | /**
123 | * Delete Menu
124 | *
125 | * @param MenuInterface $menu
126 | * @return boolean
127 | * @throws CouldNotDeleteException
128 | */
129 | public function delete(MenuInterface $menu)
130 | {
131 | try {
132 | $this->resource->delete($menu);
133 | } catch (\Exception $exception) {
134 | throw new CouldNotDeleteException(__($exception->getMessage()));
135 | }
136 |
137 | return true;
138 | }
139 |
140 | /**
141 | * Delete Menu by given Menu Identity
142 | *
143 | * @param string $menuId
144 | * @return bool
145 | * @throws CouldNotDeleteException
146 | * @throws NoSuchEntityException
147 | */
148 | public function deleteById($menuId)
149 | {
150 | return $this->delete($this->getById($menuId));
151 | }
152 |
153 | /**
154 | * Truncate tables
155 | **/
156 | public function deleteAll()
157 | {
158 | $this->resource->deleteAll();
159 | }
160 | }
161 |
--------------------------------------------------------------------------------
/src/MX/MegaMenu/Model/Menu/ImportHandler.php:
--------------------------------------------------------------------------------
1 | menuFactory = $menuFactory;
39 | $this->menuRepository = $menuRepository;
40 | }
41 |
42 | /**
43 | * Import from file
44 | *
45 | * @param mixed $file
46 | * @throws LocalizedException
47 | */
48 | public function importFromFile($file)
49 | {
50 | if (!$this->validateFile($file)) {
51 | throw new LocalizedException(__('Invalid file upload attempt.'));
52 | }
53 |
54 | $data = file_get_contents($file['tmp_name']);
55 | if (!$this->validateData($data)) {
56 | throw new LocalizedException(__('Invalid json file.'));
57 | }
58 |
59 | $this->import($data);
60 | }
61 |
62 | /**
63 | * Import data
64 | *
65 | * @param string $data
66 | */
67 | protected function import($data)
68 | {
69 | // Truncate data from database
70 | $this->menuRepository->deleteAll();
71 |
72 | // Fetch data
73 | $menuData = $this->decodeData($data);
74 |
75 | $newMenuId = 1;
76 | $newItemId = 1;
77 | $menuIdsRef = [];
78 | $menuItemIdsRef = [];
79 |
80 | // Assign references for the new menu item ids
81 | foreach ($menuData as $menuId => $menu) {
82 | $menuItems = [];
83 | foreach ($menu['menu_items'] as $itemId => $item) {
84 | // Gather old menu item id references
85 | $menuItemIdsRef[$itemId] = $newItemId;
86 |
87 | // Gather item data
88 | $menuItems[$newItemId] = $item;
89 | $menuItems[$newItemId]['menu_item_id'] = $newItemId;
90 |
91 | $newItemId++;
92 | }
93 |
94 | // Unset old indexes and set new indexes
95 | unset($menuData[$menuId]['menu_items']);
96 | $menuData[$menuId]['menu_items'] = $menuItems;
97 |
98 | // Gather old menu id references
99 | $menuIdsRef[$menuId] = $newMenuId;
100 | $newMenuId++;
101 | }
102 |
103 | // Assign references for the new menu ids
104 | foreach ($menuData as $menuId => $menu) {
105 | foreach ($menu['menu_items'] as $itemId => $item) {
106 | $parentId = $item['menu_item_parent_id'];
107 | if ($item['menu_item_parent_id'] > 0 && isset($menuItemIdsRef[$parentId])) {
108 | $menuData[$menuId]['menu_items'][$itemId]['menu_item_parent_id'] = $menuItemIdsRef[$parentId];
109 | }
110 |
111 | // Save new menu_id for menu item
112 | if (isset($menuIdsRef[$menuId])) {
113 | $menuData[$menuId]['menu_items'][$itemId]['menu_id'] = $menuIdsRef[$menuId];
114 | }
115 | }
116 |
117 | // Save new menu_id for menu
118 | if (isset($menuIdsRef[$menuId])) {
119 | $menuData[$menuId]['menu_id'] = $menuIdsRef[$menuId];
120 | }
121 | }
122 |
123 | // Save
124 | /** @var \MX\MegaMenu\Model\Menu $model */
125 | $model = $this->menuFactory->create();
126 |
127 | foreach ($menuData as $menu) {
128 | $model->setData($menu);
129 | $this->menuRepository->save($model);
130 | }
131 | }
132 |
133 | /**
134 | * Validate file data
135 | *
136 | * @param string $data
137 | * @return boolean
138 | */
139 | protected function validateData($data)
140 | {
141 | $menuData = $this->decodeData($data);
142 | if ($menuData) {
143 | $keysFound = 0;
144 | foreach ($menuData as $menu) {
145 | foreach ($menu as $key => $item) {
146 | if (in_array($key, $this->menuKeys)) {
147 | $keysFound++;
148 | }
149 | }
150 | }
151 |
152 | return $keysFound == (count($menuData) * count($this->menuKeys));
153 | }
154 |
155 | return false;
156 | }
157 |
158 | /**
159 | * Validate file
160 | *
161 | * @param mixed $file
162 | * @return boolean
163 | */
164 | protected function validateFile($file)
165 | {
166 | return $file && isset($file['tmp_name']) && $file['error'] == self::FILE_NO_ERROR;
167 | }
168 |
169 | /**
170 | * @param string $data
171 | * @return mixed
172 | */
173 | protected function decodeData($data)
174 | {
175 | return json_decode($data, true);
176 | }
177 | }
--------------------------------------------------------------------------------
/src/MX/MegaMenu/view/adminhtml/ui_component/mx_megamenu_menu_listing.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
- mx_megamenu_menu_listing.mx_megamenu_menu_listing_data_source
5 | - mx_megamenu_menu_listing.mx_megamenu_menu_listing_data_source
6 |
7 | - megamenu_columns
8 | -
9 |
-
10 |
- add
11 | - Create New Menu
12 | - primary
13 | - */*/create
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | Magento\Framework\View\Element\UiComponent\DataProvider\DataProvider
24 | mx_megamenu_menu_listing_data_source
25 | menu_id
26 | id
27 |
28 | -
29 |
- Magento_Ui/js/grid/provider
30 |
31 | -
32 |
- menu_id
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 | -
42 |
- false
43 | - 55
44 | - menu_id
45 |
46 |
47 |
48 |
49 |
50 | -
51 |
- textRange
52 | - asc
53 | - ID
54 |
55 |
56 |
57 |
58 |
59 | -
60 |
- text
61 | -
62 |
- text
63 | -
64 |
- true
65 |
66 |
67 | - Name
68 |
69 |
70 |
71 |
72 |
73 | -
74 |
- dateRange
75 | - Magento_Ui/js/grid/columns/date
76 | - date
77 | - Created
78 |
79 |
80 |
81 |
82 |
83 | -
84 |
- dateRange
85 | - Magento_Ui/js/grid/columns/date
86 | - date
87 | - Modified
88 |
89 |
90 |
91 |
92 |
93 | -
94 |
- false
95 | - 120
96 | - menu_id
97 |
98 |
99 |
100 |
101 |
--------------------------------------------------------------------------------
/src/MX/MegaMenu/Data/Form/Element/Chooser/Category.php:
--------------------------------------------------------------------------------
1 | widgetChooser = $widgetChooser;
68 | $this->categoryFactory = $categoryFactory;
69 | $this->jsonEncoder = $jsonEncoder;
70 |
71 | parent::__construct($factoryElement, $factoryCollection, $escaper, $data);
72 | }
73 |
74 | public function getElementHtml()
75 | {
76 | $this->prepareElementHtml($this);
77 |
78 | $html = $this->getBeforeElementHtml()
79 | . '' . $this->getText() . '
'
80 | . $this->getAfterElementHtml()
81 | . $this->getAfterElementJs();
82 |
83 | return $html;
84 | }
85 |
86 | protected function prepareElementHtml(AbstractElement $element)
87 | {
88 | $id = $this->getId();
89 | $config = $this->getConfig();
90 |
91 | // Chooser element
92 | $currentValue = $element->getValue();
93 | $currentLabel = $config->getLabel();
94 | $element->setId($id . 'label')->setForm($element->getForm());
95 | if ($currentValue) {
96 | $value = explode('/', $currentValue);
97 | $categoryId = false;
98 |
99 | if (isset($value[0]) && isset($value[1]) && $value[0] == 'category') {
100 | $categoryId = $value[1];
101 | }
102 |
103 | if ($categoryId) {
104 | $currentLabel = $this->categoryFactory->create()->load($categoryId)->getName();
105 | }
106 | }
107 | $element->setText($currentLabel);
108 |
109 | // Hidden element
110 | $hidden = $this->_factoryElement->create(Hidden::class, ['data' => $element->getData()]);
111 | $hidden->setId($id . 'value')->setForm($element->getForm())->setValue($currentValue);
112 | $hiddenHtml = $hidden->getElementHtml();
113 |
114 | // Button
115 | $buttons = $config->getButtons();
116 | $button = $this->widgetChooser->getLayout()->createBlock(
117 | Button::class
118 | )->setType(
119 | 'button'
120 | )->setId(
121 | $id . 'control'
122 | )->setClass(
123 | 'btn-chooser'
124 | )->setLabel(
125 | $buttons['open']
126 | )->setDisabled(
127 | $element->getReadonly()
128 | );
129 | $element->setData('after_element_html', $hiddenHtml . $button->toHtml());
130 |
131 | // Chooser Scripts
132 | $sourceUrl = $this->widgetChooser->getUrl(
133 | 'catalog/category_widget/chooser',
134 | ['uniq_id' => $id, 'use_massaction' => false]
135 | );
136 | $configJson = $this->jsonEncoder->encode($config->getData());
137 | $afterElementJs = '
138 |
158 | ';
159 | $element->setData('after_element_js', $afterElementJs);
160 | }
161 |
162 | /**
163 | * Convert Array config to Object
164 | *
165 | * @return DataObject
166 | */
167 | protected function getConfig()
168 | {
169 | $config = new DataObject();
170 | $this->setConfig($config);
171 |
172 | // define chooser label
173 | $config->setData('label', self::DEFAULT_CATEGORY_LABEL);
174 |
175 | // chooser control buttons
176 | $buttons = [
177 | 'open' => __(self::BUTTON_OPEN_LABEL),
178 | 'close' => __(self::BUTTON_CLOSE_LABEL)
179 | ];
180 |
181 | $config->setButtons($buttons);
182 |
183 | return $config;
184 | }
185 |
186 | }
187 |
--------------------------------------------------------------------------------
/src/MX/MegaMenu/view/frontend/web/js/view/megamenu.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'jquery',
3 | 'matchMedia',
4 | 'jquery/ui'
5 | ], function($, mediaCheck) {
6 | 'use strict';
7 |
8 | $.widget('mx.megaMenu', {
9 | _create: function() {
10 | this.init();
11 | this.bind();
12 | },
13 |
14 | init: function() {
15 | var self = this;
16 |
17 | if (!$('html').hasClass('mx-megamenu-init')) {
18 | $('html').addClass('mx-megamenu-init');
19 |
20 | this.element.data('mage-menu', 1); // Add mageMenu attribute to fix breadcrumbs rendering on product page
21 |
22 | $(document).on('click', '.action.nav-toggle', function () {
23 | if ($('html').hasClass('nav-open')) {
24 | $('html').removeClass('nav-open');
25 |
26 | self.element.find('.level-top').removeClass('current');
27 | self.element.find('.mx-megamenu__item').removeClass('current');
28 |
29 | setTimeout(function () {
30 | $('html').removeClass('nav-before-open');
31 | }, 300);
32 | } else {
33 | $('html').addClass('nav-before-open');
34 |
35 | setTimeout(function () {
36 | $('html').addClass('nav-open');
37 | }, 42);
38 | }
39 | });
40 | }
41 |
42 | this._adjustSubMenuItems();
43 |
44 | // Add class for nav-anchor where the link has href
45 | this.element.find('.mx-megamenu__item .mx-megamenu__link').each(function(i, item) {
46 | if (self._hasSubmenu($(item))) {
47 | $(item).addClass('has-submenu');
48 | }
49 | });
50 | },
51 |
52 | bind: function() {
53 | var self = this;
54 |
55 | mediaCheck({
56 | media: '(min-width: 1025px)',
57 |
58 | /**
59 | * Switch to Desktop Version.
60 | */
61 | entry: function () {
62 | self.element.find('.level-top').hover(function() {
63 | self.element.find('.level-top').removeClass('current');
64 | $(this).addClass('current');
65 | }, function() {
66 | $(this).removeClass('current');
67 | });
68 |
69 | /**
70 | * New functionality - toggle
71 | */
72 | self.element.find('.level1').find('.nav-anchor').each(function(i, el) {
73 | if ($(el).hasClass('hide')) {
74 | $(el).next('.mx-megamenu__submenu').hide();
75 | }
76 |
77 | if ($(el).hasClass('toggle')) {
78 | $(el).next('.mx-megamenu__submenu').hide();
79 | $(el).on('mouseenter', function() {
80 | $(el).next('.mx-megamenu__submenu').show();
81 | });
82 |
83 | $(el).next('.mx-megamenu__submenu').on('mouseleave', function() {
84 | $(this).hide();
85 | });
86 | }
87 | });
88 | },
89 | /**
90 | * Switch to Mobile Version.
91 | */
92 | exit: function () {
93 | var $item,
94 | $items;
95 |
96 | // Init sidebar links. Add link class for sidebar elements
97 | self._initSideBarLinks();
98 |
99 | self.element.find('.mx-megamenu__item > .mx-megamenu__link').on('click', function(e) {
100 | $item = $(e.target).closest('.mx-megamenu__item');
101 |
102 | if (self._canShowSubmenu($(e.target))) {
103 | // Open, close
104 | e.preventDefault();
105 |
106 | if (!$item.hasClass('current')) {
107 | if ($item.hasClass('level0')) {
108 | $items = $('.level0.mx-megamenu__item');
109 | }
110 |
111 | if ($item.hasClass('level1')) {
112 | $items = $('.level1.mx-megamenu__item');
113 | }
114 |
115 | if ($items.length) {
116 | $items.not($item).removeClass('current');
117 | }
118 | }
119 |
120 | $item.toggleClass('current');
121 | }
122 | });
123 | }
124 | });
125 | },
126 |
127 | // Adjust sub menu items where wrapper is defined
128 | _adjustSubMenuItems: function() {
129 | var $parent,
130 | $subMenu;
131 |
132 | $('.mx-megamenu__submenu.wrapper').each(function(wrapperIndex, wrapperItem) {
133 | $subMenu = $(this);
134 | $parent = $subMenu.parent();
135 |
136 | $parent.find('.mx-megamenu__item').each(function(i, item) {
137 | $subMenu.append($(item));
138 | });
139 | });
140 | },
141 |
142 | // Sidebar items with "menu-sidebar__item" class can be handled as generated links
143 | _initSideBarLinks: function() {
144 | this.element.find('.menu-sidebar__item').each(function(i, item) {
145 | $(item).addClass('mx-megamenu__item').addClass('level1');
146 | $(item).find('h4, a').addClass('mx-megamenu__link');
147 | $(item).find('ul').addClass('mx-megamenu__submenu');
148 | });
149 | },
150 |
151 | _canShowSubmenu: function($item) {
152 | var $link = $item.closest('.mx-megamenu__link');
153 |
154 | return this._hasSubmenu($link) || $link.next('.mx-megamenu__submenu').length
155 | },
156 |
157 | _hasSubmenu: function($item) {
158 | return $item.next('.mx-megamenu__submenu').length == 1;
159 | }
160 | });
161 |
162 | return $.mx.megaMenu;
163 | });
164 |
--------------------------------------------------------------------------------
/src/MX/MegaMenu/view/adminhtml/web/js/megamenu/form.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'jquery',
3 | 'MXMegaMenuFormBuilder',
4 | 'MXMegaMenuFormDialog',
5 | 'jquery/ui'
6 | ], function ($, formBuilder, menuDialog) {
7 | "use strict";
8 |
9 | var defaultLabel = 'Menu Item',
10 | categoryLabelId = 'category-label',
11 | $generalNameContainer,
12 | $categoryLabelContainer;
13 |
14 | $.widget('mx.megaMenuForm', {
15 | options: {
16 | statusDisabled: 0,
17 | statusEnabled: 1
18 | },
19 |
20 | _create: function() {
21 | this.bind();
22 | },
23 |
24 | _init: function() {
25 | var $form = this._getForm();
26 | $generalNameContainer = $('#general_name');
27 | $categoryLabelContainer = $('#content_categorylabel');
28 |
29 | $form.find('.field-content_category').hide(); // Hide the category selector at content
30 | $form.find('.field-content_category_type').hide(); // Hide the category type selector at content
31 | $form.find('.field-remove_category_anchor').hide(); // Hide the remove category link selector at content
32 |
33 | // Load saved params
34 | this._loadSavedParams();
35 | },
36 |
37 | bind: function() {
38 | var self = this;
39 |
40 | // Save form
41 | this.element.find('.action-primary').on('click', function() {
42 | self._saveForm();
43 | });
44 |
45 | // Category / Content Switcher
46 | $('#content_chooser').on('change', function() {
47 | var value = $(this).val();
48 |
49 | self._switchContent(value);
50 | });
51 |
52 | // Category Select
53 | $('#content_categorycontrol').on('click', function() {
54 | if (typeof window.content_category !== 'undefined') {
55 | window.content_category.choose();
56 | }
57 | });
58 |
59 | // Toggle - Status Box Change
60 | $('input[type="checkbox"]').on('change', function() {
61 | if ($(this).prop('checked')) {
62 | $(this).val(self.options.statusEnabled);
63 | } else {
64 | $(this).val(self.options.statusDisabled);
65 | }
66 | });
67 | },
68 |
69 | _loadSavedParams: function() {
70 | var self = this,
71 | itemId = this._getItemId(),
72 | $form = this._getForm(),
73 | $megaMenuContainer = formBuilder().getMegaMenuContainer(itemId),
74 | $dataProvider = formBuilder().getDataProvider(itemId),
75 | $elements = $megaMenuContainer.find('>.form').find('.menu_item_hidden'),
76 | $formElement,
77 | params,
78 | value;
79 |
80 | if ($elements.length) {
81 | $elements.each(function(i, el) {
82 | params = formBuilder().decodeParams($(el).val());
83 | $formElement = $form.find('[name="' + params.name + '"]');
84 | if ($formElement.length) {
85 | // Reset form element first
86 | $formElement.val('');
87 |
88 | value = params.value;
89 |
90 | if ($formElement.is('select')) {
91 | $formElement.val(value);
92 | $formElement.trigger('change');
93 | }
94 |
95 | if ($formElement.is('input') || $formElement.is('textarea')) {
96 | $formElement.val(formBuilder().decodeContent(params.name, value));
97 | }
98 |
99 | if ($formElement.hasClass('onoffswitch-checkbox')) {
100 | $formElement.val(value);
101 | if (params.value == self.options.statusEnabled) {
102 | $formElement.prop('checked', true);
103 | }
104 | }
105 | }
106 | });
107 | }
108 |
109 | if ($dataProvider.length) {
110 | // Load the category name back
111 | params = formBuilder().decodeParams($dataProvider.val());
112 | if (params.name === categoryLabelId) {
113 | $categoryLabelContainer.html(params.value);
114 | }
115 | } else {
116 | // No params defined - set default label for name
117 | $generalNameContainer.val(defaultLabel);
118 | }
119 | },
120 |
121 | _saveForm: function() {
122 | var $form = this._getForm(),
123 | itemId = this._getItemId(),
124 | miscData,
125 | $dataProvider = formBuilder().getDataProvider(itemId),
126 | $megaMenuContainer = formBuilder().getMegaMenuContainer(itemId),
127 | name = defaultLabel,
128 | $formNameValue = $.trim($generalNameContainer.val());
129 |
130 | // Save form data
131 | $form.find('input,select,textarea').each(function(i, el) {
132 | var name = $(el).attr('name'),
133 | value = $(el).val();
134 |
135 | formBuilder().saveHiddenElement(itemId, name, formBuilder().encodeContent(name, value));
136 | });
137 |
138 | // Save misc data - category name
139 | miscData = {
140 | 'name': 'category-label',
141 | 'value': $categoryLabelContainer.html()
142 | };
143 | $dataProvider.val(formBuilder().encodeParams(miscData));
144 |
145 | // Pass Category name for menu item
146 | if ($formNameValue !== '') {
147 | name = $formNameValue;
148 | }
149 | $megaMenuContainer.find('>.drag-menu').find('.label').html(name);
150 |
151 | menuDialog().closeDialog();
152 | },
153 |
154 | _switchContent: function(value) {
155 | var $form = this._getForm(),
156 | itemId = this._getItemId(),
157 | $element;
158 |
159 | $element = (value === 'category') ? $form.find('.field-content_' + value) : $form.find('.field-content_' + value + '_' + itemId);
160 | if ($element.length) {
161 | $form.find('.field-content_wysiwyg_' + itemId).hide();
162 | $form.find('.field-content_category').hide();
163 | $form.find('.field-content_category_type').hide();
164 | $form.find('.field-remove_category_anchor').hide();
165 | $element.show();
166 |
167 | if (value === 'category') {
168 | $form.find('.field-content_category_type').show();
169 | $form.find('.field-remove_category_anchor').show();
170 | }
171 | }
172 | },
173 |
174 | _getItemId: function() {
175 | var $form = this._getForm();
176 |
177 | return $form.find('input[name="menu_item_id"]').val();
178 | },
179 |
180 | _getForm: function() {
181 | return this.element.find('form');
182 | }
183 | });
184 |
185 | return $.mx.megaMenuForm;
186 | });
--------------------------------------------------------------------------------
/src/MX/MegaMenu/Model/ResourceModel/Menu.php:
--------------------------------------------------------------------------------
1 | storeManager = $storeManager;
57 | $this->entityManager = $entityManager;
58 | $this->metadataPool = $metadataPool;
59 |
60 | parent::__construct($context, $connectionName);
61 | }
62 |
63 | protected function _construct()
64 | {
65 | $this->_init('mx_megamenu', 'menu_id');
66 | }
67 |
68 | /**
69 | * @param AbstractModel $object
70 | * @param mixed $value
71 | * @param null $field
72 | * @return bool|int|string
73 | * @throws LocalizedException
74 | * @throws \Exception
75 | */
76 | private function getMenuId(AbstractModel $object, $value, $field = null)
77 | {
78 | $entityMetadata = $this->metadataPool->getMetadata(MenuInterface::class);
79 | if (!is_numeric($value) && $field === null) {
80 | $field = MenuModel::MENU_ID;
81 | } elseif (!$field) {
82 | $field = $entityMetadata->getIdentifierField();
83 | }
84 |
85 | $entityId = $value;
86 | if ($field != $entityMetadata->getIdentifierField() || $object->getStoreId()) {
87 | $select = $this->_getLoadSelect($field, $value, $object);
88 | $select->reset(Select::COLUMNS)
89 | ->columns($this->getMainTable() . '.' . $entityMetadata->getIdentifierField())
90 | ->limit(1);
91 |
92 | $result = $this->getConnection()->fetchCol($select);
93 | $entityId = count($result) ? $result[0] : false;
94 | }
95 |
96 | return $entityId;
97 | }
98 |
99 | /**
100 | * Load an object
101 | *
102 | * @param MenuModel|AbstractModel $object
103 | * @param mixed $value
104 | * @param string $field field to load by (defaults to model id)
105 | * @return $this
106 | */
107 | public function load(AbstractModel $object, $value, $field = null)
108 | {
109 | $menuId = $this->getMenuId($object, $value, $field);
110 | if ($menuId) {
111 | $this->entityManager->load($object, $menuId);
112 | }
113 |
114 | return $this;
115 | }
116 |
117 | /**
118 | * @param AbstractModel $object
119 | * @return $this
120 | * @throws \Exception
121 | */
122 | public function save(AbstractModel $object)
123 | {
124 | $this->entityManager->save($object);
125 | return $this;
126 | }
127 |
128 | /**
129 | * @inheritDoc
130 | */
131 | public function delete(AbstractModel $object)
132 | {
133 | $this->entityManager->delete($object);
134 | return $this;
135 | }
136 |
137 | /**
138 | * Delete all menu and items
139 | */
140 | public function deleteAll()
141 | {
142 | $connection = $this->getConnection();
143 |
144 | // Disable foreign key check for truncate
145 | $sql = "SET FOREIGN_KEY_CHECKS=0;";
146 | $connection->query($sql);
147 |
148 | foreach ($this->tableNames as $tableName) {
149 | $connection->truncateTable($this->getTable($tableName));
150 | }
151 |
152 | // Enable foreign key check for truncate
153 | $sql = "SET FOREIGN_KEY_CHECKS=1;";
154 | $connection->query($sql);
155 | }
156 |
157 | /**
158 | * Get store ids to which specified item is assigned
159 | *
160 | * @param int $id
161 | * @return array
162 | */
163 | public function lookupStoreIds($id)
164 | {
165 | $connection = $this->getConnection();
166 |
167 | $entityMetadata = $this->metadataPool->getMetadata(MenuInterface::class);
168 | $linkField = $entityMetadata->getLinkField();
169 |
170 | $select = $connection->select()
171 | ->from(['cbs' => $this->getTable('mx_megamenu_store')], 'store_id')
172 | ->join(
173 | ['cb' => $this->getMainTable()],
174 | 'cbs.' . $linkField . ' = cb.' . $linkField,
175 | []
176 | )
177 | ->where('cb.' . $entityMetadata->getIdentifierField() . ' = :menu_id');
178 |
179 | return $connection->fetchCol($select, ['menu_id' => (int)$id]);
180 | }
181 |
182 | /**
183 | * Get store ids to which specified item is assigned
184 | *
185 | * @param int $id
186 | * @return array
187 | */
188 | public function lookupMenuItems($id)
189 | {
190 | $connection = $this->getConnection();
191 |
192 | $entityMetadata = $this->metadataPool->getMetadata(MenuInterface::class);
193 | $linkField = $entityMetadata->getLinkField();
194 |
195 | $select = $connection->select()
196 | ->from(['cbs' => $this->getTable('mx_megamenu_item')])
197 | ->join(
198 | ['cb' => $this->getMainTable()],
199 | 'cbs.' . $linkField . ' = cb.' . $linkField,
200 | []
201 | )
202 | ->where('cb.' . $entityMetadata->getIdentifierField() . ' = :menu_id');
203 |
204 | return $connection->fetchAssoc($select, ['menu_id' => (int)$id]);
205 | }
206 |
207 | /**
208 | * Retrieve select object for load object data
209 | *
210 | * @param string $field
211 | * @param mixed $value
212 | * @param \Magento\Cms\Model\Block|AbstractModel $object
213 | * @return Select
214 | */
215 | protected function _getLoadSelect($field, $value, $object)
216 | {
217 | $entityMetadata = $this->metadataPool->getMetadata(MenuInterface::class);
218 | $linkField = $entityMetadata->getLinkField();
219 |
220 | $select = parent::_getLoadSelect($field, $value, $object);
221 |
222 | if ($object->getStoreId()) {
223 | $stores = [(int)$object->getStoreId(), Store::DEFAULT_STORE_ID];
224 |
225 | $select->reset(Select::WHERE)
226 | ->join(
227 | ['cbs' => $this->getTable('mx_megamenu_store')],
228 | $this->getMainTable() . '.' . $linkField . ' = cbs.' . $linkField,
229 | ['store_id']
230 | )
231 | ->where($this->getMainTable() . '.status = ?', MenuModel::STATUS_ENABLED)
232 | ->where('cbs.store_id in (?)', $stores)
233 | ->order('store_id DESC')
234 | ->limit(1);
235 | }
236 |
237 | return $select;
238 | }
239 | }
--------------------------------------------------------------------------------
/src/MX/MegaMenu/view/adminhtml/web/js/megamenu/form/builder.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'jquery',
3 | 'mage/template',
4 | 'jquery/ui'
5 | ], function ($, mageTemplate) {
6 | "use strict";
7 |
8 | var $megaMenuContainer,
9 | $structureContainer,
10 | megaMenuEditor = '#megamenu-editor',
11 | menuItemTemplate = '#menu-item-template';
12 |
13 | $.widget('mx.megaMenuFormBuilder', {
14 | _create: function() {
15 | $megaMenuContainer = $('#megamenu-editor');
16 | $structureContainer = $megaMenuContainer.find('.structure');
17 | },
18 |
19 | buildItem: function(item) {
20 | var self = this,
21 | miscData,
22 | itemId = item['menu_item_id'],
23 | $dataProvider;
24 |
25 | // Build main list element
26 | var tmpl = mageTemplate(menuItemTemplate),
27 | $parentElement,
28 | label = this.decodeContent('name', item['name']);
29 |
30 | var newItem = tmpl({
31 | data: {
32 | id: itemId,
33 | label: label,
34 | classname: item['classname'] || ''
35 | }
36 | });
37 |
38 | if (item['menu_item_parent_id'] == 0) {
39 | // No parent
40 | $structureContainer.append(newItem);
41 | } else {
42 | // Nested
43 | $parentElement = $('.dd-item-' + item['menu_item_parent_id']);
44 | if ($parentElement.length) {
45 | if (!$parentElement.find('.dd-list').length) {
46 | $parentElement.append('
');
47 | }
48 | $parentElement.find('.dd-list').append(newItem);
49 | }
50 | }
51 |
52 | // Build hidden values
53 | $.each(item, function(name, value) {
54 | self.saveHiddenElement(itemId, name, value);
55 | });
56 |
57 | // Build data for data provider
58 | miscData = {
59 | 'name': 'category-label',
60 | 'value': item['category_name'] == '' ? 'Not Selected' : item['category_name']
61 | };
62 | $dataProvider = this.getDataProvider(itemId);
63 | $dataProvider.val(this.encodeParams(miscData));
64 | },
65 |
66 | saveHiddenElement: function(itemId, name, value) {
67 | var $megaMenuContainer = this.getMegaMenuContainer(itemId),
68 | elementClassName = this.getHiddenElementClassName(itemId, name),
69 | elementValue = this.getHiddenElementValue(name, value),
70 | $element = $megaMenuContainer.find('>.form').find('.' + elementClassName);
71 |
72 | if ($element.length) {
73 | $element.val(elementValue);
74 | } else {
75 | this.createHiddenElement(itemId, name, value);
76 | }
77 | },
78 |
79 | createHiddenElement: function(itemId, name, value) {
80 | var $megaMenuContainer = this.getMegaMenuContainer(itemId),
81 | elementClassName = this.getHiddenElementClassName(itemId, name),
82 | elementName = this.getHiddenElementName(itemId),
83 | elementValue = this.getHiddenElementValue(name, value);
84 |
85 | $megaMenuContainer.find('>.form').append("");
86 | },
87 |
88 | getHiddenElementValue: function(name, value) {
89 | // Set default value for category type (new field > 1.1.0)
90 | if (name === 'content_category_type' && value === '') {
91 | value = 'show';
92 | }
93 |
94 | var params = {
95 | 'name': name,
96 | 'value': value
97 | };
98 |
99 | return this.encodeParams(params);
100 | },
101 |
102 | getHiddenElementName: function(itemId) {
103 | return 'menu_item_' + itemId;
104 | },
105 |
106 | getHiddenElementClassName: function(itemId, name) {
107 | return 'menu_item_' + itemId + '_' + name;
108 | },
109 |
110 | getDataProvider: function(itemId) {
111 | return this.getMegaMenuContainer(itemId).find('>.form').find('.data-provider');
112 | },
113 |
114 | getMegaMenuContainer: function(itemId) {
115 | return $(megaMenuEditor).find('.dd-item-' + itemId);
116 | },
117 |
118 | encodeParams: function(params) {
119 | if (params !== '') {
120 | return JSON.stringify(params);
121 | }
122 |
123 | return '';
124 | },
125 |
126 | decodeParams: function(params) {
127 | if (params !== '') {
128 | return JSON.parse(params);
129 | }
130 |
131 | return '';
132 | },
133 |
134 | encodeContent: function(name, value) {
135 | if (name.match('_content')) {
136 | value = this._convertContentForEditor(value);
137 |
138 | return window.btoa(value);
139 | }
140 |
141 | if (name.match('link') || name === 'name') {
142 | return window.btoa(value);
143 | }
144 |
145 | return value;
146 | },
147 |
148 | decodeContent: function(name, value) {
149 | if (name.match('_content')) {
150 | value = window.atob(value);
151 | value = this._decodeSpecialCharacters(value);
152 |
153 | return this._convertContentForEditor(value);
154 | }
155 |
156 | if (name === 'name') {
157 | value = window.atob(value);
158 | value = this._decodeSpecialCharacters(value);
159 |
160 | return this._htmlEntityDecode(value);
161 | }
162 |
163 | if (name.match('link')) {
164 | return window.atob(value);
165 | }
166 |
167 | return value;
168 | },
169 |
170 | _htmlEntityDecode: function(content) {
171 | var $elem = $('');
172 |
173 | $elem.html(content);
174 |
175 | return $elem.html().replace(/&/g, '&');
176 | },
177 |
178 | /**
179 | * Decode special symbols e.g. ® ©right;
180 | *
181 | * @param string content
182 | * @returns {*}
183 | * @private
184 | */
185 | _decodeSpecialCharacters: function(content) {
186 | return content.replace(/{amp}/g, '&').replace(/{comma}/g, ';');
187 | },
188 |
189 | /**
190 | * Convert content - workaround for html contents in widget textarea fields
191 | *
192 | * @param string content
193 | * @returns string
194 | * @private
195 | */
196 | _convertContentForEditor: function(content) {
197 | content = content
198 | .replace(/>\s+</g,'><') // <>
199 | .replace(/"/g, '"') // Fix for M2 Wysiwyg Editor
200 | .replace(/\'/g, '"'); // Backward compatiblity <= 1.1.2
201 |
202 | return this._convertSpecialAttributes(content);
203 | },
204 |
205 | /**
206 | * Convert special attributes.
207 | * Place any logic here that relates to any desired html attributes.
208 | * This is within the widget string e.g. content="something"
209 | *
210 | * @param string content
211 | * @returns string
212 | * @private
213 | */
214 | _convertSpecialAttributes: function(content) {
215 | return content.replace(/(href|src|class)="(.*?)"/g, '$1="$2"');
216 | }
217 | });
218 |
219 | return $.mx.megaMenuFormBuilder;
220 | });
221 |
--------------------------------------------------------------------------------
/src/MX/MegaMenu/Model/Menu/Item.php:
--------------------------------------------------------------------------------
1 | categoryRepository = $categoryRepository;
56 | $this->filterProvider = $filterProvider;
57 | parent::__construct($context, $registry, $resource, $resourceCollection, $data);
58 | }
59 |
60 | /**
61 | * Get Item Data
62 | *
63 | * @param array $item
64 | * @return array
65 | */
66 | public function getItemData($item)
67 | {
68 | return [
69 | 'status' => $this->isEnabled($item, 'status'),
70 | 'name' => $this->getDecodedContent($item['name']),
71 | 'link' => $this->getItemLink($item),
72 | 'header_status' => $this->isEnabled($item, 'header_status'),
73 | 'header_content' => $this->getDecodedContent($item['header_content']),
74 | 'content_status' => $this->isEnabled($item, 'content_status'),
75 | 'content_content' => $this->getDecodedContent($item['content_content']),
76 | 'content_categories' => $this->getCategories($item),
77 | 'content_category_type' => $item['content_category_type'],
78 | 'remove_category_anchor' => $item['remove_category_anchor'],
79 | 'custom_class' => $item['custom_class'],
80 | 'footer_status' => $this->isEnabled($item, 'footer_status'),
81 | 'footer_content' => $this->getDecodedContent($item['footer_content']),
82 | 'leftside_status' => $this->isEnabled($item, 'leftside_status'),
83 | 'leftside_content' => $this->getDecodedContent($item['leftside_content']),
84 | 'rightside_status' => $this->isEnabled($item, 'rightside_status'),
85 | 'rightside_content' => $this->getDecodedContent($item['rightside_content']),
86 | 'level' => self::LEVEL_DEFAULT
87 | ];
88 | }
89 |
90 | /**
91 | * Need encode
92 | *
93 | * @param string $name
94 | * @return boolean
95 | */
96 | public function needEncode($name)
97 | {
98 | return strpos($name, '_content') !== false || strpos($name, 'link') !== false
99 | || $name === 'name';
100 | }
101 |
102 | /**
103 | * Encode entities - do not encode & " < >
104 | * It's for very special cases: e.g. ® ©right;
105 | *
106 | * @param string $content
107 | * @return string
108 | */
109 | public function encodeSpecialCharacters($content)
110 | {
111 | return preg_replace('#(&)([^amp|quot|lt|gt].*?)(;)#', '{amp}$2{comma}', $content);
112 | }
113 |
114 | /**
115 | * Decode entities - see encode special characters above
116 | *
117 | * @param string $content
118 | * @return string
119 | */
120 | public function decodeSpecialCharacters($content)
121 | {
122 | return str_replace(['{amp}', '{comma}'], ['&', ';'], $content);
123 | }
124 |
125 | /**
126 | * Encode content
127 | *
128 | * @param string $content
129 | * @return string
130 | */
131 | public function encodeContent($content)
132 | {
133 | return htmlentities($content);
134 | }
135 |
136 | /**
137 | * Decode content
138 | *
139 | * @param string $content
140 | * @return string
141 | */
142 | public function decodeContent($content)
143 | {
144 | return html_entity_decode($content, ENT_QUOTES);
145 | }
146 |
147 | /**
148 | * Get category name
149 | *
150 | * @param string $item
151 | * @return string
152 | */
153 | public function getCategoryName($item)
154 | {
155 | $category = $this->getCategory($item['content_category']);
156 | if (!is_null($category)) {
157 | return $category->getName();
158 | }
159 |
160 | return '';
161 | }
162 |
163 | /**
164 | * Get categories
165 | *
166 | * @param array $item
167 | * @return array|string
168 | */
169 | protected function getCategories($item)
170 | {
171 | if ($item['content_type'] === self::CONTENT_TYPE_CATEGORY) {
172 | $category = $this->getCategory($item['content_category']);
173 | // Parent Category
174 | if (!is_null($category)) {
175 | $categories = [
176 | 'category' => $this->getCategoryData($category),
177 | 'children' => []
178 | ];
179 |
180 | // Overwrite main category link if link is defined already for the menu item
181 | if (!empty($item['link'])) {
182 | $categories['category']['link'] = $this->getItemLink($item);
183 | }
184 |
185 | // Remove main category link if it is defined separately
186 | if (!empty($item['remove_category_anchor'])) {
187 | $categories['category']['link'] = '';
188 | }
189 |
190 | // Subcategories
191 | if ($this->isChildrenCategoriesVisible($item)) {
192 | $subcategories = $category->getChildrenCategories();
193 | foreach ($subcategories as $subcategory) {
194 | $categories['children'][] = $this->getCategoryData($subcategory);
195 | }
196 | }
197 |
198 | return $categories;
199 | }
200 | }
201 |
202 | return '';
203 | }
204 |
205 | /**
206 | * Get Category
207 | *
208 | * @param string $categoryIdPath
209 | * @return Category|null
210 | */
211 | protected function getCategory($categoryIdPath)
212 | {
213 | try {
214 | $contentCategory = explode('/', $categoryIdPath);
215 | if ($contentCategory && isset($contentCategory[self::OFFSET_CATEGORY_ID])) {
216 | $categoryId = $contentCategory[self::OFFSET_CATEGORY_ID];
217 |
218 | return $this->categoryRepository->get($categoryId);
219 | }
220 | } catch (NoSuchEntityException $error) {
221 | // Magento Category Repository throws NoSuchEntityException when no category found. That shouldn't break the FE rendering
222 | $this->_logger->error($error);
223 | }
224 |
225 | return null;
226 | }
227 |
228 | /**
229 | * Is children categories visible
230 | *
231 | * @param array $item
232 | * @return boolean
233 | */
234 | protected function isChildrenCategoriesVisible($item)
235 | {
236 | return $item['content_category_type'] === self::CATEGORY_TYPE_SHOW
237 | || $item['content_category_type'] === self::CATEGORY_TYPE_TOGGLE;
238 | }
239 |
240 | /**
241 | * Get Category Data
242 | *
243 | * @param Category $category
244 | * @return array
245 | */
246 | protected function getCategoryData($category)
247 | {
248 | $result = [];
249 |
250 | if ($category->getId() && $category->getIsActive() == 1) {
251 | $result = [
252 | 'id' => $category->getId(),
253 | 'name' => $category->getName(),
254 | 'link' => $category->getUrl(),
255 | ];
256 | }
257 |
258 | return $result;
259 | }
260 |
261 | /**
262 | * Is the property enabled
263 | *
264 | * @param array $item
265 | * @param string $property
266 | * @return boolean
267 | */
268 | protected function isEnabled($item, $property)
269 | {
270 | return isset($item[$property]) && $item[$property] == self::STATUS_ENABLED;
271 | }
272 |
273 | /**
274 | * Get Decoded Content
275 | *
276 | * @param string $content
277 | * @return string
278 | */
279 | protected function getDecodedContent($content)
280 | {
281 | $content = $this->decodeContent($content);
282 | $content = $this->decodeSpecialCharacters($content);
283 |
284 | return $this->filterProvider->getBlockFilter()->filter($content);
285 | }
286 |
287 | /**
288 | * Get Item Link
289 | *
290 | * @param array $item
291 | * @return string
292 | */
293 | protected function getItemLink($item)
294 | {
295 | if (!empty($item['link'])) {
296 | return $this->getDecodedContent($item['link']); // Also resolve magento url directives
297 | }
298 |
299 | return 'javascript:;'; // For opening submenu items, no links defined
300 | }
301 | }
--------------------------------------------------------------------------------
/src/MX/MegaMenu/Model/Menu.php:
--------------------------------------------------------------------------------
1 | menuItemFactory = $menuItemFactory;
44 | parent::__construct($context, $registry, $resource, $resourceCollection, $data);
45 | }
46 |
47 | protected function _construct()
48 | {
49 | $this->_init(ResourceMenu::class);
50 | }
51 |
52 | public function getIdentities()
53 | {
54 | return [self::CACHE_TAG . '_' . $this->getMenuId()];
55 | }
56 |
57 | /**
58 | * Retrieve menu id
59 | *
60 | * @return integer
61 | */
62 | public function getMenuId()
63 | {
64 | return $this->getData(self::MENU_ID);
65 | }
66 |
67 | /**
68 | * Retrieve name
69 | *
70 | * @return string
71 | */
72 | public function getName()
73 | {
74 | return $this->getData(self::NAME);
75 | }
76 |
77 | /**
78 | * Retrieve link
79 | *
80 | * @return string
81 | */
82 | public function getLink()
83 | {
84 | return $this->getData(self::LINK);
85 | }
86 |
87 | /**
88 | * Retrieve creation time
89 | *
90 | * @return string
91 | */
92 | public function getCreatedAt()
93 | {
94 | return $this->getData(self::CREATED_AT);
95 | }
96 |
97 | /**
98 | * Retrieve update time
99 | *
100 | * @return string
101 | */
102 | public function getUpdatedAt()
103 | {
104 | return $this->getData(self::UPDATED_AT);
105 | }
106 |
107 | /**
108 | * Get status
109 | *
110 | * @return boolean
111 | */
112 | public function getStatus()
113 | {
114 | return (bool)$this->getData(self::STATUS);
115 | }
116 |
117 | /**
118 | * Receive page store ids
119 | *
120 | * @return mixed
121 | */
122 | public function getStores()
123 | {
124 | return $this->getData(self::STORE_ID);
125 | }
126 |
127 | /**
128 | * Get sorted menu items
129 | *
130 | * @return array
131 | */
132 | public function getSortedMenuItems()
133 | {
134 | $items = $this->getMenuItems();
135 |
136 | usort($items, function($previous, $next) {
137 | return ($previous['sort_order'] <=> $next['sort_order']);
138 | });
139 |
140 | return $items;
141 | }
142 |
143 | /**
144 | * Receive menu items
145 | *
146 | * @return array
147 | */
148 | public function getMenuItems()
149 | {
150 | if ($this->hasData(self::MENU_ITEMS)) {
151 | return $this->getData(self::MENU_ITEMS);
152 | }
153 |
154 | $items = [];
155 | $data = $this->_registry->registry('mx_megamenu_menu_items');
156 | if ($data) {
157 | $menuItems = json_decode($data, true);
158 | if ($menuItems) {
159 | $items = $menuItems;
160 | $this->setData(self::MENU_ITEMS, $items);
161 | }
162 | }
163 |
164 | return $items;
165 | }
166 |
167 | /**
168 | * Get menu item ids
169 | *
170 | * @return array
171 | */
172 | public function getMenuItemIds()
173 | {
174 | $ids = [];
175 | $items = $this->getMenuItems();
176 |
177 | if (count($items)) {
178 | foreach ($items as $item) {
179 | $ids[] = $item['menu_item_id'];
180 | }
181 | }
182 |
183 | return $ids;
184 | }
185 |
186 | /**
187 | * Is active
188 | *
189 | * @return boolean
190 | */
191 | public function isActive()
192 | {
193 | $status = $this->getStatus();
194 |
195 | return $status == self::STATUS_ENABLED;
196 | }
197 |
198 | /**
199 | * Set ID
200 | *
201 | * @param integer $id
202 | * @return MenuInterface
203 | */
204 | public function setMenuId($id)
205 | {
206 | return $this->setData(self::MENU_ID, $id);
207 | }
208 |
209 | /**
210 | * Set Stores
211 | *
212 | * @param int|array $storeId
213 | * @return $this
214 | */
215 | public function setStores($storeId)
216 | {
217 | return $this->setData(self::STORE_ID, $storeId);
218 | }
219 |
220 | /**
221 | * Set NAME
222 | *
223 | * @param string $name
224 | * @return MenuInterface
225 | */
226 | public function setName($name)
227 | {
228 | return $this->setData(self::NAME, $name);
229 | }
230 |
231 | /**
232 | * Set Link
233 | *
234 | * @param string $link
235 | * @return MenuInterface
236 | */
237 | public function setLink($link)
238 | {
239 | return $this->setData(self::NAME, $link);
240 | }
241 |
242 | /**
243 | * Set creation time
244 | *
245 | * @param string $createdAt
246 | * @return MenuInterface
247 | */
248 | public function setCreatedAt($createdAt)
249 | {
250 | return $this->setData(self::CREATED_AT, $createdAt);
251 | }
252 |
253 | /**
254 | * Set update time
255 | *
256 | * @param string $updatedAt
257 | * @return MenuInterface
258 | */
259 | public function setUpdatedAt($updatedAt)
260 | {
261 | return $this->setData(self::UPDATED_AT, $updatedAt);
262 | }
263 |
264 | /**
265 | * Set status
266 | *
267 | * @param bool|int $status
268 | * @return MenuInterface
269 | */
270 | public function setStatus($status)
271 | {
272 | return $this->setData(self::STATUS, $status);
273 | }
274 |
275 | /**
276 | * Set menu items
277 | *
278 | * @param array $items
279 | * @return $this
280 | */
281 | public function setMenuItems(&$items)
282 | {
283 | $menuItem = $this->menuItemFactory->create();
284 | foreach ($items as $id => $item) {
285 | foreach ($item as $name => $value) {
286 | if ($menuItem->needEncode($name)) {
287 | $value = $menuItem->encodeContent($value);
288 |
289 | $items[$id][$name] = $menuItem->encodeSpecialCharacters($value);
290 | }
291 | }
292 | }
293 |
294 | return $this->setData(self::MENU_ITEMS, $items);
295 | }
296 |
297 | /**
298 | * Add special items - e.g. real category name based on the category id
299 | *
300 | * @param array $items
301 | */
302 | public function addSpecialMenuItems(&$items)
303 | {
304 | $menuItem = $this->menuItemFactory->create();
305 | foreach ($items as $id => $item) {
306 | $items[$id]['category_name'] = $menuItem->getCategoryName($item);
307 | }
308 | }
309 |
310 | /**
311 | * Remove special items before save
312 | *
313 | * @param array $items
314 | */
315 | public function removeSpecialMenuItems(&$items)
316 | {
317 | foreach ($items as $id => $item) {
318 | unset($items[$id]['category_name']);
319 | }
320 | }
321 |
322 | /**
323 | * Prepare menu's statuses.
324 | *
325 | * @return array
326 | */
327 | public function getAvailableStatuses()
328 | {
329 | return [
330 | self::STATUS_ENABLED => __('Enabled'),
331 | self::STATUS_DISABLED => __('Disabled')
332 | ];
333 | }
334 |
335 | /**
336 | * Get Processed Menu Items
337 | *
338 | * @return array
339 | */
340 | public function getProcessedMenuItems()
341 | {
342 | $result = [];
343 | foreach ($this->getSortedMenuItems() as $item) {
344 | $itemId = $item['menu_item_id'];
345 | $parentId = $item['menu_item_parent_id'];
346 |
347 | $menuItem = $this->menuItemFactory->create();
348 | $menuItemData = $menuItem->getItemData($item);
349 |
350 | if ($parentId == 0) {
351 | $result[$itemId] = $menuItemData;
352 | } else {
353 | // 2nd level (1st child)
354 | if (isset($result[$parentId])) {
355 | $menuItemData['level'] = Item::LEVEL_2;
356 | $result[$parentId]['children'][$itemId] = $menuItemData;
357 | } else {
358 | // 3rd level (2nd child)
359 | $menuItemData['level'] = Item::LEVEL_3;
360 | $this->setChildrenItems($result, $item, $menuItemData);
361 | }
362 | }
363 | }
364 |
365 | return $result;
366 | }
367 |
368 | /**
369 | * Set lower level children
370 | *
371 | * @param array $result
372 | * @param array $item
373 | * @param array $menuItemData
374 | */
375 | protected function setChildrenItems(&$result, $item, $menuItemData)
376 | {
377 | $parentId = $item['menu_item_parent_id'];
378 | $itemId = $item['menu_item_id'];
379 |
380 | foreach ($result as $id => $child) {
381 | if (isset($child['children'])) {
382 | foreach ($child['children'] as $i => $ch) {
383 | if ($i == $parentId) {
384 | $result[$id]['children'][$i]['wrapper'] = true; // Add wrapper for 3rd level items
385 | $result[$id]['children'][$i]['children'][$itemId] = $menuItemData;
386 | }
387 | }
388 | }
389 | }
390 | }
391 | }
--------------------------------------------------------------------------------
/src/MX/MegaMenu/view/adminhtml/web/js/megamenu-editor.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'jquery',
3 | 'mage/template',
4 | 'MXMegaMenuFormBuilder',
5 | 'MXMegaMenuFormDialog',
6 | 'nestable',
7 | 'mage/adminhtml/browser',
8 | 'jquery/ui'
9 | ], function ($, mageTemplate, formBuilder, menuDialog) {
10 | "use strict";
11 |
12 | var $megaMenuContainer,
13 | $structureContainer,
14 | $actionsContainer,
15 | $tabsMenu,
16 | $saveButton,
17 | $settingsForm,
18 | $menuItemsForm,
19 | $nestableContainer,
20 | dataProviderLabel = 'data-provider',
21 | categoryNameLabel = 'category-label',
22 | classnameLabel = 'classname',
23 | formKeyLabel = 'form_key',
24 | defaultMenuItemLabel = 'Menu Item';
25 |
26 | $.widget('mx.megaMenuEditor', {
27 | options: {
28 | editUrl: '',
29 | saveUrl: '',
30 | maxDepth: 3
31 | },
32 |
33 | _create: function() {
34 | $megaMenuContainer = this.element;
35 | $structureContainer = $megaMenuContainer.find('.structure');
36 | $actionsContainer = $megaMenuContainer.find('.actions');
37 | $tabsMenu = $('#tabs-menu'),
38 | $saveButton = $('#save');
39 | $settingsForm = $('#settings_form');
40 | $menuItemsForm = $('#menu-items-form'),
41 | $nestableContainer = $menuItemsForm.find('.nestable');
42 |
43 | this.bind();
44 | this.build();
45 | },
46 |
47 | bind: function() {
48 | var self = this;
49 |
50 | $nestableContainer.nestable({
51 | maxDepth: self.options.maxDepth,
52 | onDragStart: function(l, e) {
53 | $(e).find('.actions').hide();
54 | $(e).find('.dd-actions').hide();
55 | },
56 | beforeDragStop: function(l, e) {
57 | $(e).find('.actions').show();
58 | $(e).find('.dd-actions').show();
59 | }
60 | });
61 |
62 | // Add
63 | $actionsContainer.find('.btn-add').on('click', function() {
64 | self.addMenuItem();
65 | $("html, body").animate({ scrollTop: $(document).height() }, 1000); // Scroll to the bottom as the new menu item is added to the bottom
66 | });
67 |
68 | //Tabs
69 | $tabsMenu.find('a').on('click', function(e) {
70 | var currentTab = $(e.target).data('target');
71 |
72 | $tabsMenu.find('li').removeClass('active');
73 | $(e.target).closest('li').addClass('active');
74 |
75 | $megaMenuContainer.find('.megamenu-tabs').removeClass('active');
76 | if ($(currentTab).length) {
77 | $(currentTab).addClass('active');
78 | }
79 | });
80 |
81 | // Save
82 | $saveButton.on('click', function() {
83 | self.save();
84 | });
85 | },
86 |
87 | build: function() {
88 | if (menuItemsData) {
89 | // Build items
90 | this.buildItems(menuItemsData);
91 |
92 | // Add expand/collapse buttons as it's buggy for the third-party
93 | this.buildButtons();
94 | }
95 | },
96 |
97 | buildButtons: function() {
98 | var self = this;
99 |
100 | $structureContainer.find('.menu-item').each(function(i, el) {
101 | if ($(el).find('>.dd-list').length) {
102 | $(el).find('>.dd-actions').append(self._getButtonHtml('#btn-expand-template'));
103 | $(el).find('>.dd-actions').append(self._getButtonHtml('#btn-collapse-template'));
104 | $(el).find('>.dd-actions').find('.btn-collapse').addClass('show');
105 | }
106 | });
107 |
108 | $structureContainer.find('.menu-item').find('.btn-caret').on('click', function(e) {
109 | e.preventDefault();
110 |
111 | var $this = $(e.target).closest('.btn-caret'),
112 | $parent = $this.closest('.dd-actions'),
113 | $expandButton = $parent.find('.btn-expand'),
114 | $collapseButton = $parent.find('.btn-collapse'),
115 | action = $this.data('action'),
116 | $children = $this.closest('.menu-item').find('.dd-list');
117 |
118 | if (action === 'expand') {
119 | $collapseButton.addClass('show');
120 | $expandButton.removeClass('show');
121 | $children.slideDown();
122 | }
123 |
124 | if (action === 'collapse') {
125 | $expandButton.addClass('show');
126 | $collapseButton.removeClass('show');
127 | $children.slideUp();
128 | }
129 | });
130 | },
131 |
132 | save: function() {
133 | var self = this;
134 |
135 | if (this.options.saveUrl !== '') {
136 | var data = $settingsForm.serialize(),
137 | itemsData = {};
138 |
139 | $structureContainer.find('.menu-item').each(function(i, el) {
140 | var itemId = $(el).data('id'),
141 | $form = $(el).find('>.form');
142 |
143 | itemsData[itemId] = self._getDefaultsForMenuItem(itemId);
144 |
145 | $form.find('input').each(function(idx, element) {
146 | var item = formBuilder().decodeParams($(element).val()),
147 | parentId = 0,
148 | $parentElement;
149 |
150 | // Get menu item data
151 | if (!$(element).hasClass(dataProviderLabel) && self._canSaveMenuItem(item)) {
152 | itemsData[itemId][item.name] = formBuilder().decodeContent(item.name, item.value);
153 | }
154 |
155 | // Get parent
156 | $parentElement = $(element).closest('.dd-list').closest('.menu-item');
157 | if ($parentElement.length && $parentElement.data('id') != itemId) {
158 | parentId = $parentElement.data('id');
159 | }
160 | itemsData[itemId]['menu_item_parent_id'] = parentId;
161 | });
162 |
163 | // Get sort order
164 | var sortOrder = $(el).index();
165 | if (itemsData[itemId]['menu_item_parent_id'] != 0) {
166 | sortOrder = (itemsData[itemId]['menu_item_parent_id'] * 100) + sortOrder;
167 | }
168 | itemsData[itemId]['sort_order'] = sortOrder;
169 | });
170 |
171 | if (itemsData !== '') {
172 | data += '&items=' + encodeURIComponent(formBuilder().encodeParams(itemsData));
173 | }
174 |
175 | // Send Main Form Data
176 | $.ajax({
177 | url: this.options.saveUrl,
178 | type: 'POST',
179 | data: data,
180 | showLoader: true,
181 | success: function(response) {
182 | if (response.status && response.url) {
183 | location.href = response.url;
184 | }
185 | }
186 | });
187 | }
188 | },
189 |
190 | buildItems: function(data) {
191 | var self = this,
192 | items = formBuilder().decodeParams(data);
193 |
194 | $.each(items, function(i, el) {
195 | self.addMenuItem(el);
196 | });
197 | },
198 |
199 | addMenuItem: function(item) {
200 | var self = this;
201 |
202 | if (typeof item === 'undefined') {
203 | var item = {
204 | menu_item_id: this._getMaxMenuItemId() + 1,
205 | menu_item_parent_id: 0,
206 | name: formBuilder().encodeContent('name', defaultMenuItemLabel),
207 | classname: 'new'
208 | };
209 | }
210 |
211 | formBuilder().buildItem(item);
212 |
213 | $(document).on('click', '.btn-edit', function(e) {
214 | e.stopImmediatePropagation();
215 |
216 | self.editMenuItem($(e.target).closest('.menu-item'));
217 | });
218 |
219 | $(document).on('click', '.btn-remove', function(e) {
220 | e.stopImmediatePropagation();
221 |
222 | self.removeMenuItem($(e.target).closest('.menu-item'));
223 | });
224 | },
225 |
226 | _getMaxMenuItemId: function() {
227 | var value,
228 | max = 0;
229 |
230 | $structureContainer.find('.menu-item').each(function() {
231 | value = parseInt($(this).data('id'));
232 | max = (value > max) ? value : max;
233 | });
234 |
235 | return max;
236 | },
237 |
238 | editMenuItem: function($element) {
239 | var additionalParams = '',
240 | $nameElement,
241 | menuItemNameValue,
242 | menuItemName = '';
243 |
244 | if ($element.data('id')) {
245 | additionalParams = 'item_id/' + $element.data('id');
246 | $nameElement = $element.find('.form').find('.menu_item_' + $element.data('id') + '_name');
247 | menuItemNameValue = $nameElement.val();
248 | menuItemNameValue = JSON.parse(menuItemNameValue);
249 | menuItemName = formBuilder().decodeContent('name', menuItemNameValue.value);
250 | }
251 |
252 | menuDialog().openDialog(this.options.editUrl + additionalParams, menuItemName);
253 | },
254 |
255 | removeMenuItem: function($element) {
256 | if ($element.length && confirm('Are you sure you want to remove this item?')) {
257 | $element.remove();
258 | }
259 | },
260 |
261 | _getButtonHtml: function(templateId) {
262 | var tmpl = mageTemplate(templateId);
263 |
264 | return tmpl();
265 | },
266 |
267 | _canSaveMenuItem(item) {
268 | return item.name !== categoryNameLabel
269 | && item.name != formKeyLabel
270 | && item.name !== classnameLabel;
271 | },
272 |
273 | _getDefaultsForMenuItem: function(itemId) {
274 | return {
275 | 'menu_item_id': itemId,
276 | 'status': 0,
277 | 'name': defaultMenuItemLabel,
278 | 'link': '',
279 | 'custom_class': '',
280 | 'header_status': 0,
281 | 'header_content': '',
282 | 'content_status': 0,
283 | 'content_category': '',
284 | 'content_content': '',
285 | 'content_category_type': 'show',
286 | 'content_type': 'wysiwyg',
287 | 'remove_category_anchor': 0,
288 | 'leftside_status': 0,
289 | 'leftside_content': '',
290 | 'rightside_status': 0,
291 | 'rightside_content': '',
292 | 'footer_status': 0,
293 | 'footer_content': '',
294 | 'sort_order': 0
295 | }
296 | }
297 | });
298 |
299 | return $.mx.megaMenuEditor;
300 | });
--------------------------------------------------------------------------------
/src/MX/MegaMenu/Block/Adminhtml/Menu/Edit/Form.php:
--------------------------------------------------------------------------------
1 | 'Content',
46 | 'category' => 'Category'
47 | ];
48 |
49 | /**
50 | * @var array
51 | */
52 | protected $categoryTypeChooserOptions = [
53 | 'show' => 'Show children always',
54 | 'hide' => 'Hide children always',
55 | 'toggle' => 'Toggle children'
56 | ];
57 |
58 | /**
59 | * @param Context $context
60 | * @param Registry $registry
61 | * @param FormFactory $formFactory
62 | * @param Config $wysiwygConfig
63 | * @param CategoryFactory $categoryFactory
64 | * @param array $data
65 | */
66 | public function __construct(
67 | Context $context,
68 | Registry $registry,
69 | FormFactory $formFactory,
70 | Config $wysiwygConfig,
71 | CategoryFactory $categoryFactory,
72 | array $data = []
73 | ) {
74 | $this->wysiwygConfig = $wysiwygConfig;
75 | $this->categoryFactory = $categoryFactory;
76 | parent::__construct($context, $registry, $formFactory, $data);
77 | }
78 |
79 | protected function _construct()
80 | {
81 | parent::_construct();
82 |
83 | $this->setId('wysiwyg_form');
84 | $this->setTitle(__('Edit Menu Item'));
85 | }
86 |
87 | protected function _prepareForm()
88 | {
89 | $itemId = $this->getRequest()->getParam('item_id', 0);
90 |
91 | $form = $this->_formFactory->create(
92 | ['data' =>
93 | [
94 | 'id' => 'edit_form',
95 | 'enctype' => 'multipart/form-data',
96 | 'action' => $this->getData('action'),
97 | 'method' => 'post'
98 | ]
99 | ]
100 | );
101 |
102 | $general = $form->addFieldset(
103 | 'general_fieldset',
104 | [
105 | 'legend' => __('General'),
106 | 'class' => 'fieldset-wide'
107 | ]
108 | );
109 | $general->addField(
110 | 'general_item_id',
111 | 'hidden',
112 | [
113 | 'name' => 'menu_item_id',
114 | 'value' => $itemId
115 | ]
116 | );
117 | $general->addField(
118 | 'general_status',
119 | Toggle::class,
120 | [
121 | 'label' => __('Enabled'),
122 | 'title' => __('Enabled'),
123 | 'required' => false,
124 | 'name' => 'status',
125 | 'value' => Menu::STATUS_DISABLED
126 | ]
127 | );
128 | $general->addField(
129 | 'general_name',
130 | 'text',
131 | [
132 | 'label' => __('Name'),
133 | 'title' => __('Name'),
134 | 'required' => false,
135 | 'name' => 'name'
136 | ]
137 | );
138 | $general->addField(
139 | 'general_link',
140 | 'text',
141 | [
142 | 'label' => __('Link'),
143 | 'title' => __('Link'),
144 | 'required' => false,
145 | 'name' => 'link'
146 | ]
147 | );
148 | $general->addField(
149 | 'general_custom_class',
150 | 'text',
151 | [
152 | 'label' => __('Custom Classes'),
153 | 'title' => __('Custom Classes'),
154 | 'required' => false,
155 | 'name' => 'custom_class'
156 | ]
157 | );
158 |
159 | $header = $form->addFieldset(
160 | 'header_fieldset',
161 | [
162 | 'legend' => __('Header'),
163 | 'class' => 'fieldset-wide'
164 | ]
165 | );
166 | $header->addField(
167 | 'header_status',
168 | Toggle::class,
169 | [
170 | 'label' => __('Status'),
171 | 'title' => __('Status'),
172 | 'required' => false,
173 | 'name' => 'header_status',
174 | 'value' => Menu::STATUS_DISABLED
175 | ]
176 | );
177 | $header->addField(
178 | $this->getFormFieldId('header_wysiwyg', $itemId),
179 | 'editor',
180 | [
181 | 'name' => 'header_content',
182 | 'label' => __('Content'),
183 | 'title' => __('Content'),
184 | 'style' => 'height:10em',
185 | 'required' => false,
186 | 'config' => $this->wysiwygConfig->getConfig()
187 | ]
188 | );
189 |
190 | $content = $form->addFieldset(
191 | 'content_fieldset',
192 | [
193 | 'legend' => __('Main Content'),
194 | 'class' => 'fieldset-wide'
195 | ]
196 | );
197 | $content->addField(
198 | 'content_status',
199 | Toggle::class,
200 | [
201 | 'label' => __('Status'),
202 | 'title' => __('Status'),
203 | 'required' => false,
204 | 'name' => 'content_status',
205 | 'value' => Menu::STATUS_DISABLED
206 | ]
207 | );
208 | $content->addField(
209 | 'content_chooser',
210 | 'select',
211 | [
212 | 'label' => __('Content Type'),
213 | 'title' => __('Content Type'),
214 | 'required' => true,
215 | 'options' => $this->getContentChooserOptions(),
216 | 'name' => 'content_type',
217 | ]
218 | );
219 | $content->addField(
220 | $this->getFormFieldId('content_wysiwyg', $itemId),
221 | 'editor',
222 | [
223 | 'name' => 'content_content',
224 | 'label' => __('Content'),
225 | 'title' => __('Content'),
226 | 'style' => 'height:10em',
227 | 'required' => false,
228 | 'config' => $this->wysiwygConfig->getConfig()
229 | ]
230 | );
231 | $content->addField(
232 | 'content_category',
233 | 'MX\MegaMenu\Data\Form\Element\Chooser\Category',
234 | [
235 | 'label' => __('Category'),
236 | 'title' => __('Category'),
237 | 'required' => false,
238 | 'name' => 'content_category',
239 | 'button_label' => __('Select Category...')
240 | ]
241 | );
242 | $content->addField(
243 | 'content_category_type',
244 | 'select',
245 | [
246 | 'label' => __('Category Type'),
247 | 'title' => __('Category Type'),
248 | 'required' => false,
249 | 'options' => $this->getCategoryTypeChooserOptions(),
250 | 'name' => 'content_category_type',
251 | ]
252 | );
253 | $content->addField(
254 | 'remove_category_anchor',
255 | Toggle::class,
256 | [
257 | 'label' => __('Remove Category Link'),
258 | 'title' => __('Remove Category Link'),
259 | 'required' => false,
260 | 'name' => 'remove_category_anchor',
261 | 'value' => Menu::STATUS_DISABLED
262 | ]
263 | );
264 |
265 | $leftside = $form->addFieldset(
266 | 'left_side_fieldset',
267 | [
268 | 'legend' => __('Left Side'),
269 | 'class' => 'fieldset-wide'
270 | ]
271 | );
272 | $leftside->addField(
273 | 'leftside_status',
274 | Toggle::class,
275 | [
276 | 'label' => __('Status'),
277 | 'title' => __('Status'),
278 | 'required' => false,
279 | 'name' => 'leftside_status',
280 | 'value' => Menu::STATUS_DISABLED
281 | ]
282 | );
283 | $leftside->addField(
284 | $this->getFormFieldId('left_side_wysiwyg', $itemId),
285 | 'editor',
286 | [
287 | 'name' => 'leftside_content',
288 | 'label' => __('Content'),
289 | 'title' => __('Content'),
290 | 'style' => 'height:10em',
291 | 'required' => false,
292 | 'config' => $this->wysiwygConfig->getConfig()
293 | ]
294 | );
295 |
296 | $rightside = $form->addFieldset(
297 | 'right_side_fieldset',
298 | [
299 | 'legend' => __('Right Side'),
300 | 'class' => 'fieldset-wide'
301 | ]
302 | );
303 | $rightside->addField(
304 | 'rightside_status',
305 | Toggle::class,
306 | [
307 | 'label' => __('Status'),
308 | 'title' => __('Status'),
309 | 'required' => false,
310 | 'name' => 'rightside_status',
311 | 'value' => Menu::STATUS_DISABLED
312 | ]
313 | );
314 | $rightside->addField(
315 | $this->getFormFieldId('right_side_wysiwyg', $itemId),
316 | 'editor',
317 | [
318 | 'name' => 'rightside_content',
319 | 'label' => __('Content'),
320 | 'title' => __('Content'),
321 | 'style' => 'height:10em',
322 | 'required' => false,
323 | 'config' => $this->wysiwygConfig->getConfig()
324 | ]
325 | );
326 |
327 | $footer = $form->addFieldset(
328 | 'footer_fieldset',
329 | [
330 | 'legend' => __('Footer'),
331 | 'class' => 'fieldset-wide'
332 | ]
333 | );
334 | $footer->addField(
335 | 'footer_status',
336 | Toggle::class,
337 | [
338 | 'label' => __('Status'),
339 | 'title' => __('Status'),
340 | 'required' => false,
341 | 'name' => 'footer_status',
342 | 'value' => Menu::STATUS_DISABLED
343 | ]
344 | );
345 | $footer->addField(
346 | $this->getFormFieldId('footer_wysiwyg', $itemId),
347 | 'editor',
348 | [
349 | 'name' => 'footer_content',
350 | 'label' => __('Content'),
351 | 'title' => __('Content'),
352 | 'style' => 'height:10em',
353 | 'required' => false,
354 | 'config' => $this->wysiwygConfig->getConfig()
355 | ]
356 | );
357 |
358 | $form->setUseContainer(true);
359 | $this->setForm($form);
360 |
361 | return parent::_prepareForm();
362 | }
363 |
364 | /**
365 | * Get unique form field id
366 | *
367 | * @param string $field
368 | * @param integer $itemId
369 | * @return string
370 | */
371 | protected function getFormFieldId($field, $itemId)
372 | {
373 | return $field . '_' . $itemId;
374 | }
375 |
376 | /**
377 | * Get content chooser options
378 | *
379 | * @return array
380 | */
381 | protected function getContentChooserOptions()
382 | {
383 | return $this->contentChooserOptions;
384 | }
385 |
386 | /**
387 | * Get categor type chooser options
388 | *
389 | * @return array
390 | */
391 | protected function getCategoryTypeChooserOptions()
392 | {
393 | return $this->categoryTypeChooserOptions;
394 | }
395 | }
--------------------------------------------------------------------------------
/src/MX/MegaMenu/Setup/InstallSchema.php:
--------------------------------------------------------------------------------
1 | startSetup();
21 |
22 | if (!$installer->tableExists(self::TABLE_MEGAMENU)) {
23 | $table = $installer->getConnection()->newTable(
24 | $installer->getTable(self::TABLE_MEGAMENU)
25 | )
26 | ->addColumn(
27 | 'menu_id',
28 | Table::TYPE_SMALLINT,
29 | null,
30 | [
31 | 'identity' => true,
32 | 'nullable' => false,
33 | 'primary' => true,
34 | 'unsigned' => true,
35 | ],
36 | 'Menu ID'
37 | )
38 | ->addColumn(
39 | 'name',
40 | Table::TYPE_TEXT,
41 | 255,
42 | [
43 | 'nullable' => false
44 | ],
45 | 'Menu Name'
46 | )
47 | ->addColumn(
48 | 'status',
49 | Table::TYPE_SMALLINT,
50 | 1,
51 | [
52 | 'nullable' => false,
53 | 'unsigned' => true,
54 | 'default' => 0
55 | ],
56 | 'Menu Status'
57 | )
58 | ->addColumn(
59 | 'created_at',
60 | Table::TYPE_TIMESTAMP,
61 | null,
62 | ['nullable' => false, 'default' => Table::TIMESTAMP_INIT],
63 | 'Created At'
64 | )->addColumn(
65 | 'updated_at',
66 | Table::TYPE_TIMESTAMP,
67 | null,
68 | ['nullable' => false, 'default' => Table::TIMESTAMP_INIT_UPDATE],
69 | 'Updated At')
70 | ->setComment('Menu Table');
71 |
72 | $installer->getConnection()->createTable($table);
73 | }
74 |
75 | if (!$installer->tableExists(self::TABLE_MEGAMENU_ITEM)) {
76 | $table = $installer->getConnection()->newTable(
77 | $installer->getTable(self::TABLE_MEGAMENU_ITEM)
78 | )
79 | ->addColumn(
80 | 'menu_item_id',
81 | Table::TYPE_INTEGER,
82 | null,
83 | [
84 | 'identity' => true,
85 | 'nullable' => false,
86 | 'primary' => true,
87 | 'unsigned' => true,
88 | ],
89 | 'Menu Item ID'
90 | )
91 | ->addColumn(
92 | 'menu_id',
93 | Table::TYPE_SMALLINT,
94 | null,
95 | [
96 | 'nullable' => false,
97 | 'unsigned' => true,
98 | ],
99 | 'Menu ID'
100 | )
101 | ->addColumn(
102 | 'menu_item_parent_id',
103 | Table::TYPE_INTEGER,
104 | null,
105 | [
106 | 'nullable' => false,
107 | 'unsigned' => true,
108 | ],
109 | 'Menu Item Parent ID'
110 | )
111 | ->addColumn(
112 | 'status',
113 | Table::TYPE_SMALLINT,
114 | 1,
115 | [
116 | 'nullable' => false,
117 | 'unsigned' => true,
118 | 'default' => 0
119 | ],
120 | 'Menu Item Status'
121 | )
122 | ->addColumn(
123 | 'name',
124 | Table::TYPE_TEXT,
125 | 255,
126 | [
127 | 'nullable' => false
128 | ],
129 | 'Menu Item Name'
130 | )
131 | ->addColumn(
132 | 'link',
133 | Table::TYPE_TEXT,
134 | 255,
135 | [
136 | 'nullable' => false
137 | ],
138 | 'Menu Item Link'
139 | )
140 | ->addColumn(
141 | 'header_status',
142 | Table::TYPE_SMALLINT,
143 | 1,
144 | [
145 | 'nullable' => false,
146 | 'unsigned' => true,
147 | 'default' => 0
148 | ],
149 | 'Menu Item Header Status'
150 | )
151 | ->addColumn(
152 | 'header_content',
153 | Table::TYPE_TEXT,
154 | '64k',
155 | [
156 | 'nullable' => false
157 | ],
158 | 'Menu Item Header Content'
159 | )
160 | ->addColumn(
161 | 'content_status',
162 | Table::TYPE_SMALLINT,
163 | 1,
164 | [
165 | 'nullable' => false,
166 | 'unsigned' => true,
167 | 'default' => 0
168 | ],
169 | 'Menu Item Main Content Status'
170 | )
171 | ->addColumn(
172 | 'content_content',
173 | Table::TYPE_TEXT,
174 | '64k',
175 | [
176 | 'nullable' => false
177 | ],
178 | 'Menu Item Main Content Content'
179 | )
180 | ->addColumn(
181 | 'content_type',
182 | Table::TYPE_TEXT,
183 | 255,
184 | [
185 | 'nullable' => false
186 | ],
187 | 'Menu Item Content Type'
188 | )
189 | ->addColumn(
190 | 'content_category',
191 | Table::TYPE_TEXT,
192 | 255,
193 | [
194 | 'nullable' => false
195 | ],
196 | 'Menu Item Content Category'
197 | )
198 | ->addColumn(
199 | 'leftside_status',
200 | Table::TYPE_SMALLINT,
201 | 1,
202 | [
203 | 'nullable' => false,
204 | 'unsigned' => true,
205 | 'default' => 0
206 | ],
207 | 'Menu Item Left Side Status'
208 | )
209 | ->addColumn(
210 | 'leftside_content',
211 | Table::TYPE_TEXT,
212 | '64k',
213 | [
214 | 'nullable' => false
215 | ],
216 | 'Menu Item Left Side Content'
217 | )
218 | ->addColumn(
219 | 'rightside_status',
220 | Table::TYPE_SMALLINT,
221 | 1,
222 | [
223 | 'nullable' => false,
224 | 'unsigned' => true,
225 | 'default' => 0
226 | ],
227 | 'Menu Item Right Side Status'
228 | )
229 | ->addColumn(
230 | 'rightside_content',
231 | Table::TYPE_TEXT,
232 | '64k',
233 | [
234 | 'nullable' => false
235 | ],
236 | 'Menu Item Right Side Content'
237 | )
238 | ->addColumn(
239 | 'footer_status',
240 | Table::TYPE_SMALLINT,
241 | 1,
242 | [
243 | 'nullable' => false,
244 | 'unsigned' => true,
245 | 'default' => 0
246 | ],
247 | 'Menu Item Footer Status'
248 | )
249 | ->addColumn(
250 | 'footer_content',
251 | Table::TYPE_TEXT,
252 | '64k',
253 | [
254 | 'nullable' => false
255 | ],
256 | 'Menu Item Footer Content'
257 | )
258 | ->addColumn(
259 | 'sort_order',
260 | Table::TYPE_SMALLINT,
261 | null,
262 | [
263 | 'nullable' => false,
264 | 'unsigned' => true,
265 | ],
266 | 'Menu Item Sort Order'
267 | )
268 | ->addColumn(
269 | 'created_at',
270 | Table::TYPE_TIMESTAMP,
271 | null,
272 | ['nullable' => false, 'default' => Table::TIMESTAMP_INIT],
273 | 'Created At'
274 | )
275 | ->addColumn(
276 | 'updated_at',
277 | Table::TYPE_TIMESTAMP,
278 | null,
279 | ['nullable' => false, 'default' => Table::TIMESTAMP_INIT_UPDATE],
280 | 'Updated At')
281 | ->addIndex(
282 | $installer->getIdxName(self::TABLE_MEGAMENU_ITEM, ['menu_item_parent_id']),
283 | ['menu_item_parent_id']
284 | )
285 | ->addForeignKey(
286 | $installer->getFkName(
287 | self::TABLE_MEGAMENU_ITEM,
288 | 'menu_id',
289 | self::TABLE_MEGAMENU,
290 | 'menu_id'
291 | ),
292 | 'menu_id',
293 | $installer->getTable(self::TABLE_MEGAMENU),
294 | 'menu_id',
295 | Table::ACTION_CASCADE
296 | )
297 | ->setComment('Menu Item Table');
298 |
299 | $installer->getConnection()->createTable($table);
300 | }
301 |
302 | if (!$installer->tableExists(self::TABLE_MEGAMENU_STORE)) {
303 | $table = $installer->getConnection()->newTable(
304 | $installer->getTable(self::TABLE_MEGAMENU_STORE)
305 | )
306 | ->addColumn(
307 | 'menu_id',
308 | Table::TYPE_SMALLINT,
309 | null,
310 | [
311 | 'nullable' => false,
312 | 'unsigned' => true,
313 | ],
314 | 'Menu ID'
315 | )
316 | ->addColumn(
317 | 'store_id',
318 | Table::TYPE_SMALLINT,
319 | null,
320 | [
321 | 'nullable' => false,
322 | 'unsigned' => true,
323 | ],
324 | 'Store ID'
325 | )
326 | ->addIndex(
327 | $installer->getIdxName(self::TABLE_MEGAMENU_STORE, ['menu_id']),
328 | ['menu_id']
329 | )
330 | ->addIndex(
331 | $installer->getIdxName(self::TABLE_MEGAMENU_STORE, ['store_id']),
332 | ['store_id']
333 | )
334 | ->addForeignKey(
335 | $installer->getFkName(
336 | self::TABLE_MEGAMENU_STORE,
337 | 'menu_id',
338 | self::TABLE_MEGAMENU,
339 | 'menu_id'
340 | ),
341 | 'menu_id',
342 | $installer->getTable(self::TABLE_MEGAMENU),
343 | 'menu_id',
344 | Table::ACTION_CASCADE
345 | )
346 | ->addForeignKey(
347 | $installer->getFkName(
348 | self::TABLE_MEGAMENU_STORE,
349 | 'store_id',
350 | self::TABLE_STORE,
351 | 'store_id'
352 | ),
353 | 'store_id',
354 | $installer->getTable(self::TABLE_STORE),
355 | 'store_id',
356 | Table::ACTION_CASCADE
357 | )
358 | ->setComment('Menu Stores Table');
359 |
360 | $installer->getConnection()->createTable($table);
361 | }
362 |
363 | $installer->endSetup();
364 | }
365 | }
--------------------------------------------------------------------------------