Skip to content
  1. Extras
  2. MiniShop3
  3. Development
  4. Events
  5. Manager

Manager events

Events for customizing the MiniShop3 manager interface.

msOnManagerCustomCssJs

Fired when MiniShop3 manager pages load. Lets you add your own CSS and JavaScript files.

Parameters

ParameterTypeDescription
controllerobjectPage controller object
pagestringPage identifier

Available pages

page valueDescription
product_createCreate product
product_updateEdit product
category_createCreate category
category_updateEdit category
ordersOrder list
orderOrder card
customersCustomer list
settingsComponent settings
notificationsNotification management

Adding files

php
<?php
switch ($modx->event->name) {
    case 'msOnManagerCustomCssJs':
        $controller = $scriptProperties['controller'];
        $page = $scriptProperties['page'];

        // Add CSS
        $controller->addCss('/assets/components/mycomponent/css/ms3-custom.css');

        // Add JavaScript
        $controller->addJavascript('/assets/components/mycomponent/js/ms3-custom.js');

        // Add HTML (for ES modules)
        $controller->addHtml('<script type="module" src="/assets/components/mycomponent/js/ms3-module.js"></script>');
        break;
}

Conditional loading by page

php
<?php
switch ($modx->event->name) {
    case 'msOnManagerCustomCssJs':
        $controller = $scriptProperties['controller'];
        $page = $scriptProperties['page'];

        $controller->addCss('/assets/components/mycomponent/css/common.css');

        if (in_array($page, ['product_create', 'product_update'])) {
            $controller->addJavascript('/assets/components/mycomponent/js/product-extend.js');
        }
        if (in_array($page, ['orders', 'order'])) {
            $controller->addJavascript('/assets/components/mycomponent/js/order-extend.js');
        }
        if ($page === 'category_update') {
            $controller->addJavascript('/assets/components/mycomponent/js/category-extend.js');
        }
        break;
}

Passing data to JavaScript

php
<?php
switch ($modx->event->name) {
    case 'msOnManagerCustomCssJs':
        $controller = $scriptProperties['controller'];
        $page = $scriptProperties['page'];

        $config = [
            'apiUrl' => '/assets/components/mycomponent/connector.php',
            'userId' => $modx->user->get('id'),
            'page' => $page,
            'customSetting' => $modx->getOption('my_custom_setting'),
        ];

        // Pass config to JS
        $controller->addHtml('
            <script>
                window.MyComponent = window.MyComponent || {};
                window.MyComponent.config = ' . json_encode($config) . ';
            </script>
        ');

        $controller->addJavascript('/assets/components/mycomponent/js/main.js');
        break;
}

Extending the ExtJS interface

Add a button to the product toolbar

php
<?php
// Plugin
switch ($modx->event->name) {
    case 'msOnManagerCustomCssJs':
        $page = $scriptProperties['page'];

        if (in_array($page, ['product_create', 'product_update'])) {
            $controller = $scriptProperties['controller'];
            $controller->addJavascript('/assets/components/mycomponent/js/product-toolbar.js');
        }
        break;
}
javascript
// product-toolbar.js
Ext.onReady(function() {
    var checkPanel = setInterval(function() {
        var panel = Ext.getCmp('ms3-product-panel');
        if (panel && panel.getTopToolbar) {
            clearInterval(checkPanel);

            var toolbar = panel.getTopToolbar();
            toolbar.add('-');
            toolbar.add({
                text: 'My button',
                iconCls: 'icon-cog',
                handler: function() {
                    MODx.msg.alert('Info', 'Button clicked!');
                }
            });
            toolbar.doLayout();
        }
    }, 100);
});

Add a tab to the product card

php
<?php
switch ($modx->event->name) {
    case 'msOnManagerCustomCssJs':
        $page = $scriptProperties['page'];

        if ($page === 'product_update') {
            $controller = $scriptProperties['controller'];
            $controller->addJavascript('/assets/components/mycomponent/js/product-tab.js');
        }
        break;
}
javascript
// product-tab.js
Ext.onReady(function() {
    var checkTabs = setInterval(function() {
        var tabs = Ext.getCmp('modx-resource-tabs');
        if (tabs) {
            clearInterval(checkTabs);

            tabs.add({
                title: 'My tab',
                id: 'my-custom-tab',
                xtype: 'panel',
                layout: 'form',
                autoHeight: true,
                items: [{
                    xtype: 'textfield',
                    fieldLabel: 'Custom field',
                    name: 'my_custom_field',
                    anchor: '100%'
                }, {
                    xtype: 'textarea',
                    fieldLabel: 'Description',
                    name: 'my_description',
                    anchor: '100%',
                    height: 150
                }]
            });
            tabs.doLayout();
        }
    }, 100);
});

Add a column to the orders grid

php
<?php
switch ($modx->event->name) {
    case 'msOnManagerCustomCssJs':
        $page = $scriptProperties['page'];

        if ($page === 'orders') {
            $controller = $scriptProperties['controller'];
            $controller->addJavascript('/assets/components/mycomponent/js/orders-grid.js');
        }
        break;
}
javascript
// orders-grid.js
Ext.onReady(function() {
    var originalGetColumns = ms3.grid.Orders.prototype.getColumns;

    ms3.grid.Orders.prototype.getColumns = function() {
        var columns = originalGetColumns.apply(this, arguments);

        columns.splice(3, 0, {
            header: 'Source',
            dataIndex: 'source',
            width: 100,
            sortable: true,
            renderer: function(value) {
                return value || '-';
            }
        });

        return columns;
    };
});

Full example: extending the product card

php
<?php
/**
 * Plugin: Extend product card
 * Events: msOnManagerCustomCssJs
 */

switch ($modx->event->name) {
    case 'msOnManagerCustomCssJs':
        $controller = $scriptProperties['controller'];
        $page = $scriptProperties['page'];

        if (!in_array($page, ['product_create', 'product_update'])) {
            return;
        }

        $assetsUrl = MODX_ASSETS_URL . 'components/mycomponent/';

        $controller->addCss($assetsUrl . 'css/product-extend.css');

        $productId = isset($_GET['id']) ? (int)$_GET['id'] : 0;

        $config = [
            'productId' => $productId,
            'assetsUrl' => $assetsUrl,
            'connectorUrl' => $assetsUrl . 'connector.php',
            'options' => [
                'enableSync' => $modx->getOption('mycomp_enable_sync', null, true),
                'apiKey' => $modx->getOption('mycomp_api_key'),
            ],
        ];

        if ($productId > 0) {
            $product = $modx->getObject(\MiniShop3\Model\msProduct::class, $productId);
            if ($product) {
                $config['productData'] = [
                    'article' => $product->get('article'),
                    'vendor_id' => $product->get('vendor_id'),
                ];
            }
        }

        $controller->addHtml('
            <script>
                window.MyProductExtend = ' . json_encode($config) . ';
            </script>
        ');

        $controller->addJavascript($assetsUrl . 'js/product-extend.js');
        break;
}
javascript
// product-extend.js
Ext.onReady(function() {
    var config = window.MyProductExtend || {};

    var checkForm = setInterval(function() {
        var form = Ext.getCmp('modx-panel-resource');
        if (!form) return;

        clearInterval(checkForm);

        var toolbar = form.getTopToolbar();
        if (toolbar && config.options.enableSync) {
            toolbar.add('-');
            toolbar.add({
                text: 'Sync',
                iconCls: 'icon-refresh',
                handler: function() {
                    syncProduct(config.productId);
                }
            });
            toolbar.doLayout();
        }

        form.on('success', function(response) {
            console.log('Product saved:', response);
        });

    }, 100);

    function syncProduct(productId) {
        if (!productId) {
            MODx.msg.alert('Error', 'Save the product first');
            return;
        }

        MODx.Ajax.request({
            url: config.connectorUrl,
            params: {
                action: 'sync',
                product_id: productId
            },
            listeners: {
                success: {
                    fn: function(response) {
                        MODx.msg.alert('Success', 'Product synced');
                    }
                },
                failure: {
                    fn: function(response) {
                        MODx.msg.alert('Error', response.message || 'Sync failed');
                    }
                }
            }
        });
    }
});
css
/* product-extend.css */

.my-custom-field {
    border: 1px solid #ddd;
    padding: 10px;
    margin: 10px 0;
    background: #f9f9f9;
}

.my-sync-status {
    display: inline-block;
    padding: 2px 8px;
    border-radius: 3px;
    font-size: 11px;
}

.my-sync-status.synced {
    background: #d4edda;
    color: #155724;
}

.my-sync-status.pending {
    background: #fff3cd;
    color: #856404;
}

.my-sync-status.error {
    background: #f8d7da;
    color: #721c24;
}

Recommendations

Avoid conflicts

javascript
// Use unique namespaces
window.MyComponent = window.MyComponent || {};
window.MyComponent.init = function() {
    // Your code
};

// Check element existence
var panel = Ext.getCmp('ms3-product-panel');
if (panel) {
    // Safe to use panel
}

Use ExtJS events

javascript
Ext.onReady(function() {
    if (typeof ms3 !== 'undefined') {
        ms3.on('load', function() {
            console.log('MiniShop3 loaded');
        });
    }
});

Load order

php
// Use addLastJavascript for scripts that must load after core
$controller->addLastJavascript('/assets/components/mycomponent/js/after-all.js');