Здравствуйте. Есть своя табличка CMP. Данные сохраняются, обновляются с этим проблем нет. Проблема в другом. Есть строка в таблице, в строке 5 колонок, id скрыто по умолчанию (его не считаем), название (текст), урл (текст), класс ресурса (варчар), шаблон (инт) и в конце этого дела есть кнопка. Так вот, кнопку я явно сделал не правильно, мне бы такую как в минишопе в таблице заказов, где редактировать, удалить и тд. Я как бы впиперил кнопку туда, но теперь ума не приложу как заставить ее работать.
Теперь основная суть вопроса: как по клику на кнопку передать id записи таблицы в процессор в виде переменной. То есть клик, айди попал в процессоре в переменную, и дальше я уже использую эту переменную для формирования нужного запроса и тд.

Изучая исходники минишопа я понял что рендер для кнопок находится в js/mgr/misc/ms2.utils.js
Вот он:
miniShop2.utils.renderActions = function (value, props, row) {
    var res = [];
    var cls, icon, title, action, item = '';
    for (var i in row.data.actions) {
        if (!row.data.actions.hasOwnProperty(i)) {
            continue;
        }
        var a = row.data.actions[i];
        if (!a['button']) {
            continue;
        }

        icon = a['icon'] ? a['icon'] : '';
        if (typeof(a['cls']) == 'object') {
            if (typeof(a['cls']['button']) != 'undefined') {
                icon += ' ' + a['cls']['button'];
            }
        }
        else {
            cls = a['cls'] ? a['cls'] : '';
        }
        action = a['action'] ? a['action'] : '';
        title = a['title'] ? a['title'] : '';

        item = String.format(
            '<li class="{0}"><button class="btn btn-default {1}" action="{2}" title="{3}"></button></li>',
            cls, icon, action, title
        );

        res.push(item);
    }

    return String.format(
        '<ul class="minishop2-row-actions">{0}</ul>',
        res.join('')
    );
};


У меня что-то рендер не хочет работать если его поместить в отдельный файл как в минишопе, сделал так.

Scrapx.grid.ScrapxPresets = function(config) {
    config = config || {};
    Ext.applyIf(config,{
        id: 'scrapx-grid-scrapxpresets'
        ,url: Scrapx.config.connectorUrl
        ,baseParams: { action: 'mgr/scrapxpresets/getList' }
        ,fields: ['id','home_url','scrap_url','resource_class','category_id','actions']
        ,paging: true
        ,remoteSort: true
        ,anchor: '97%'
        ,autoExpandColumn: 'name'
        ,save_action: 'mgr/scrapxpresets/updateFromGrid'
        ,autosave: true
        ,columns: [{
				header:_('scrapx.header_id')
				,dataIndex: 'id'
				,sortable: true
				,hidden: true
			},{
				header:_('scrapx.header_home_url')
				,dataIndex: 'home_url'
				,sortable: true
				,editor: {
					xtype: 'textfield'
				}
			},{
				header:_('scrapx.header_scrap_url')
				,dataIndex: 'scrap_url'
				,sortable: true
				,editor: {
					xtype: 'textfield'
				}
			},{
				header:_('scrapx.header_resource_class')
				,dataIndex: 'resource_class'
				,sortable: true
				,editor: {
					xtype: 'modx-combo-class-derivatives'
				}
                ,fieldLabel: _('resource_type')
                ,description: '<b>[[*class_key]]</b><br />'
                ,name: 'class_key'
                ,hiddenName: 'class_key'
                ,id: 'modx-resource-class-key'
                ,anchor: '100%'
			},{
				header:_('scrapx.header_category_id')
				,dataIndex: 'category_id'
				,sortable: true
				,editor: {
					xtype: 'numberfield'
				}
			},{
				//header:_('scrapx.header_actions')
				
                dataIndex: 'actions',
                id: 'actions',
                width: 195,
                sortable: false,
                fixed: true,
                width: 150,
                renderer: this.renderActions

			}],tbar:[{
             text: '<i class="icon icon-clipboard"></i>  ' + _('scrapx.btn_create')


            ,handler: { xtype: 'scrapx-window-scrapxpresets-create' ,blankValues: true }
            },'->',{
            xtype: 'textfield'
            ,name: 'search'
            ,id: 'scrapx-search-filter'
            ,emptyText: _('scrapx.search')+'...'
            ,listeners: {
                'change': {fn:this.search,scope:this}
                ,'render': {fn: function(cmp) {
                    new Ext.KeyMap(cmp.getEl(), {
                        key: Ext.EventObject.ENTER
                        ,fn: function() {
                            this.fireEvent('change',this);
                            this.blur();
                            return true;
                        }
                        ,scope: cmp
                    });
                },scope:this}
            }
        },{
            xtype: 'button'
            ,id: 'scrapx-filter-clear'
            ,text: _('scrapx.filter_clear')
            ,listeners: {
                'click': {fn: this.clearFilter, scope: this}
            }
        }]


        ,getMenu: function() {
            return [{
                text: _('scrapx.menu.edit')
                ,handler: this.editScrapxPresets
            },'-',{
                text: _('scrapx.menu.remove')
                ,handler: this.removeScrapxPresets
            }];
        },editScrapxPresets: function(btn,e) {
            if (!this.editScrapxPresetsWindow) {
                this.editScrapxPresetsWindow = MODx.load({
                    xtype: 'scrapx-window-scrapxpresets-edit'
                    ,record: this.menu.record
                    ,listeners: {
                        'success': {fn:this.refresh,scope:this}
                    }
                });
            }
            this.editScrapxPresetsWindow.setValues(this.menu.record);
            this.editScrapxPresetsWindow.show(e.target);
        },removeScrapxPresets: function() {
            MODx.msg.confirm({
                title: _('scrapx.title.win_remove')
                ,text: _('scrapx.confirm.remove')
                ,url: this.config.url
                ,params: {
                    action: 'mgr/scrapxpresets/remove'
                    ,id: this.menu.record.id
                }
                ,listeners: {
                    'success': {fn:this.refresh,scope:this}
                }
            });
        }

    });
    Scrapx.grid.ScrapxPresets.superclass.constructor.call(this,config)
};
Ext.extend(Scrapx.grid.ScrapxPresets,MODx.grid.Grid,{
    search: function(tf,nv,ov) {
        var s = this.getStore();
        s.baseParams.query = tf.getValue();
        this.getBottomToolbar().changePage(1);
        this.refresh();
    }
    ,clearFilter: function() {
        var s = this.getStore();
        s.baseParams.search = '';
        Ext.getCmp('scrapx-search-filter').reset();
        this.getBottomToolbar().changePage(1);
        this.refresh();
    },
    renderActions:  function (value, props, row) {
    var res = [];
    var cls, icon, title, action, item;
    if (typeof(value) == 'object') {
        for (var i in value) {
            if (!value.hasOwnProperty(i)) {
                continue;
            }
            var a = value[i];
            if (!a['button']) {
                continue;
            }

            icon = a['icon'] ? a['icon'] : '';
            if (typeof(a['cls']) == 'object') {
                if (typeof(a['cls']['button']) != 'undefined') {
                    icon += ' ' + a['cls']['button'];
                }
            } else {
                cls = a['cls'] ? a['cls'] : '';
            }
            action = a['action'] ? a['action'] : '';
            title = a['title'] ? a['title'] : '';

            item = String.format(
                '<li class="{0}"><button class="btn btn-default {1}" action="{2}" title="{3}"></button></li>',
                cls, icon, action, title
            );

            res.push(item);
        }
    }

    return String.format(
        '<ul class="xp-row-actions">{0}</ul>',
        res.join('')
    );
}
});
Ext.reg('scrapx-grid-scrapxpresets',Scrapx.grid.ScrapxPresets);

Scrapx.window.EditScrapxPresets = function(config) {
    config = config || {};
    var self = this;
    Ext.applyIf(config,{
        title: _('scrapx.title.win_edit')
        ,url: Scrapx.config.connectorUrl
        ,autoHeight: true
        ,modal: true
        ,baseParams: {
            action: 'mgr/scrapxpresets/update'
        }
        ,fields: [{
				xtype: 'hidden'
				,name: 'id'
			},{
				xtype: 'textarea'
				,fieldLabel: _('scrapx.label_home_url')
				,description: '<b>[[*home_url]]</b><br />'+_('scrapx.label_home_url_help')
				,name: 'home_url'
				,allowBlank:false
				,anchor: '100%'
			},{
				xtype: 'textarea'
				,fieldLabel: _('scrapx.label_scrap_url')
				,description: '<b>[[*scrap_url]]</b><br />'+_('scrapx.label_scrap_url_help')
				,name: 'scrap_url'
				,allowBlank:false
				,anchor: '100%'
			},{
				xtype: 'textfield'
				,fieldLabel: _('scrapx.label_resource_class')
				,description: '<b>[[*resource_class]]</b><br />'+_('scrapx.label_resource_class_help')
				,name: 'resource_class'
				,allowBlank:false
				,anchor: '100%'
			},{
				xtype: 'numberfield'
				,fieldLabel: _('scrapx.label_category_id')
				,description: '<b>[[*category_id]]</b><br />'+_('scrapx.label_category_id_help')
				,name: 'category_id'
				,allowBlank:false
				,anchor: '100%'
			}]
    });
    Scrapx.window.EditScrapxPresets.superclass.constructor.call(this,config);
};
Ext.extend(Scrapx.window.EditScrapxPresets,MODx.Window,{});
Ext.reg('scrapx-window-scrapxpresets-edit',Scrapx.window.EditScrapxPresets);


Scrapx.window.CreateScrapxPresets = function(config) {
    config = config || {};
    var self = this;
    Ext.applyIf(config,{
        title: _('scrapx.title.win_create')
        ,url: Scrapx.config.connectorUrl
        ,autoHeight:true
        ,modal: true
        ,baseParams: {
            action: 'mgr/scrapxpresets/create'
        }
        ,fields: [{
				xtype: 'hidden'
				,name: 'id'
			},{
				xtype: 'textarea'
				,fieldLabel: _('scrapx.label_home_url')
				,description: '<b>[[*home_url]]</b><br />'+_('scrapx.label_home_url_help')
				,name: 'home_url'
				,allowBlank:false
				,anchor: '100%'
			},{
				xtype: 'textarea'
				,fieldLabel: _('scrapx.label_scrap_url')
				,description: '<b>[[*scrap_url]]</b><br />'+_('scrapx.label_scrap_url_help')
				,name: 'scrap_url'
				,allowBlank:false
				,anchor: '100%'
			},{
				xtype: 'textfield'
				,fieldLabel: _('scrapx.label_resource_class')
				,description: '<b>[[*resource_class]]</b><br />'+_('scrapx.label_resource_class_help')
				,name: 'resource_class'
				,allowBlank:false
				,anchor: '100%'
			},{
				xtype: 'numberfield'
				,fieldLabel: _('scrapx.label_category_id')
				,description: '<b>[[*category_id]]</b><br />'+_('scrapx.label_category_id_help')
				,name: 'category_id'
				,allowBlank:false
				,anchor: '100%'
			}]
    });
    Scrapx.window.CreateScrapxPresets.superclass.constructor.call(this,config);
};
Ext.extend(Scrapx.window.CreateScrapxPresets,MODx.Window);
Ext.reg('scrapx-window-scrapxpresets-create',Scrapx.window.CreateScrapxPresets);


Обертка для кнопок появляется в разметке, а вот кнопок нет. Значит их надо откуда-то передать. Смотрим getList процессор минишопа, там есть вот такая функция:

/**
     * @param array $data
     *
     * @return array
     */
    public function prepareArray(array $data)
    {
        if (empty($data['customer'])) {
            $data['customer'] = $data['customer_username'];
        }

        $data['status'] = '<span style="color:#' . $data['color'] . ';">' . $data['status'] . '</span>';
        unset($data['color']);

        if (isset($data['cost'])) {
            $data['cost'] = $this->ms2->formatPrice($data['cost']);
        }
        if (isset($data['cart_cost'])) {
            $data['cart_cost'] = $this->ms2->formatPrice($data['cart_cost']);
        }
        if (isset($data['delivery_cost'])) {
            $data['delivery_cost'] = $this->ms2->formatPrice($data['delivery_cost']);
        }
        if (isset($data['weight'])) {
            $data['weight'] = $this->ms2->formatWeight($data['weight']);
        }

        $data['actions'] = array(
            array(
                'cls' => '',
                'icon' => 'icon icon-edit',
                'title' => $this->modx->lexicon('ms2_menu_update'),
                'action' => 'updateOrder',
                'button' => true,
                'menu' => true,
            ),
            array(
                'cls' => array(
                    'menu' => 'red',
                    'button' => 'red',
                ),
                'icon' => 'icon icon-trash-o',
                'title' => $this->modx->lexicon('ms2_menu_remove'),
                'multiple' => $this->modx->lexicon('ms2_menu_remove_multiple'),
                'action' => 'removeOrder',
                'button' => true,
                'menu' => true,
            ),
            /*
            array(
                'cls' => '',
                'icon' => 'icon icon-cog actions-menu',
                'menu' => false,
                'button' => true,
                'action' => 'showMenu',
                'type' => 'menu',
            ),
            */
        );

        return $data;
    }


Вот это нас не интересует:

if (empty($data['customer'])) {
            $data['customer'] = $data['customer_username'];
        }

        $data['status'] = '<span style="color:#' . $data['color'] . ';">' . $data['status'] . '</span>';
        unset($data['color']);

        if (isset($data['cost'])) {
            $data['cost'] = $this->ms2->formatPrice($data['cost']);
        }
        if (isset($data['cart_cost'])) {
            $data['cart_cost'] = $this->ms2->formatPrice($data['cart_cost']);
        }
        if (isset($data['delivery_cost'])) {
            $data['delivery_cost'] = $this->ms2->formatPrice($data['delivery_cost']);
        }
        if (isset($data['weight'])) {
            $data['weight'] = $this->ms2->formatWeight($data['weight']);
        }
}

Значит оставляем примерно так:

/**
     * @param array $data
     *
     * @return array
     */
    public function prepareArray(array $data){
        $data['actions'] = array(
            array(
                'cls' => '',
                'icon' => 'icon icon-edit',
                'title' => $this->modx->lexicon('scrapx.action_parse'),
                'action' => 'getScraping',
                'button' => true,
                'menu' => true,
            ),
        );

        return $data;
    }


В итоге получем такой getList процессор:

<?php
class ScrapxPresetsGetListProcessor extends modObjectGetListProcessor {
    public $languageTopics = array('scrapx:scrapxpresets');
    public $classKey = 'ScrapxPresets';
    public $defaultSortField = 'home_url';
    public $defaultSortDirection = 'ASC';
    public $checkListPermission = true;
    public function prepareQueryBeforeCount(xPDOQuery $c) {
        $query = $this->getProperty('query');
        if (!empty($query)) {
            $c->where(array('name:LIKE' => '%'.$query.'%'));
        }
        return $c;
    }

    /**
     * @param array $data
     *
     * @return array
     */
    public function prepareArray(xPDOObject $object){
        $data = $object->toArray();
        $data['actions'] = array(
            array(
                'cls' => '',
                'icon' => 'icon icon-edit',
                'title' => $this->modx->lexicon('scrapx.action_parse'),
                'action' => 'getScraping',
                'button' => true,
                'menu' => true,
            ),
        );

        return $data;
    }
}
return 'ScrapxPresetsGetListProcessor';


Но вот это $data['actions'] тоже надо откуда то взять в процессоре… Блин как все запутано. Может я вообще нет так делаю?:)