[ExtJS] Расширяем нативную гриду юзеров
После статьи о расширении профиля юзера правильными дополнительными полями мне посыпались вопросы о расширении нативной таблицы со списком юзеров. Мы знаем, что практически любой стандартный компонент системы, работающий на ExtJS, можно расширить не затрагивая исходника. Главное
Сразу опишем задачу, которую реализуем в рамках статьи:
- Убрать слева каждой записи ненужный чекбокс,
- Добавить столбцы: Фото, Дата рождения, Страна, Город,
- Добавить возможность отфильтровать пользователей по стране,
- Заменить некрасивое поле поиска на симпатичное и компактное,
- Подсветить заблокированных красным цветом.
Почему не компонентом?
В виде компонента такое оформить было бы правильно, как я это делал в статье о Расширении компонента Collections, однако я не раз замечаю барьер у многих, при попытке запаковки какого-то функционала в пакет. Поэтому обойдемся более простым методом. А те, кому надо запаковать в пакет — разберутся без труда.
CSS
/assets/xusergrid/css/main.css
Стили комментировать не имеет смысла.JavaScript
/assets/xusergrid/js/xusergrid.js
Здесь происходит объявление и инициализация основного класса./assets/xusergrid/js/combo.js
Содержит поле поиска и поле выбора страны. Необходимо для фильтра./assets/xusergrid/js/extends/modx.grid.user.js
Основной файл, расширяющий таблицу пользователей.Я постарался указать комментарии в коде, чтобы он стал более понятным.
PHP
/assets/xusergrid/connector.php
Кастомный коннектор, ссылающийся на нашу папку процессоров./assets/xusergrid/processors/combo/getuserfield.class.php
Процессор, возвращающий в комбобокс, список используемых значений любого поля юзера./assets/xusergrid/processors/security/user/getlist.class.php
Процессор, возвращающий список пользователей. Расширяет стандартный класс modUserGetListProcessor, добавляя в него:— фильтрацию по стране,
— дополнительные поля в выборку,
— обработку сырых данных даты рождения, страны.
/assets/xusergrid/processors/security/user/updatefromgrid.class.php
Процессор, обновляющий данные юзера при «быстром» редактировании прямо из таблицы. Удаляется ключ country для того, чтобы не перезаписывать в таблицу обработанное (переведенное) значение.Остальные процессоры необходимы, потому что в стандартных методах объекта MODx.grid.User они вызываются непосредственно через коннектор, прописанный в свойстве основного объекта гриды. А т.к. мы заменили стандартный коннектор на свой, то и процессоры должны продублировать.
Это также можно решить, переписав немного методы activateSelected, deactivateSelected, removeSelected, removeUser и duplicateUser, а точнее, заменив this.config.url на MODx.config.connector_url. Но я считаю, что лучше расширить процессоры, к которым обращается JS, не трогая «ни грамма» исходников при этом.
/assets/xusergrid/processors/security/user/activatemultiple.class.php
/assets/xusergrid/processors/security/user/deactivatemultiple.class.php
/assets/xusergrid/processors/security/user/delete.class.php
/assets/xusergrid/processors/security/user/duplicate.class.php
/assets/xusergrid/processors/security/user/removemultiple.class.php
Плагин
Пожалуй, это самое основное, ибо без плагина на событие OnManagerPageBeforeRender про наш код MODX даже и не узнает.
$sp = &$scriptProperties;
/** @var modManagerController $controller */
$controller = $sp['controller'];
switch ($modx->event->name) {
case "OnManagerPageBeforeRender":
if (is_object($controller)) {
$config = array(
'connector_url' => $modx->getOption('assets_url') . 'xusergrid/connector.php',
);
$action = $controller->scriptProperties['a'];
if ($action == 'security/user') {
$controller->addCss($modx->getOption('assets_url') . 'xusergrid/css/main.css');
$controller->addJavascript($modx->getOption('assets_url') . 'xusergrid/js/xusergrid.js');
$controller->addHtml("<script type='text/javascript'>
xUserGrid['config'] = {$modx->toJSON($config)};
</script>");
$controller->addLastJavascript($modx->getOption('assets_url') . 'xusergrid/js/combo.js');
$controller->addLastJavascript($modx->getOption('assets_url') . 'xusergrid/js/extends/modx.grid.user.js');
}
}
break;
}
Рассмотрим подробнее
Вот здесь мы расширяем наш объект xUserGrid.grid.modxUserExt, добавляя в него методы и свойства из зарегистрированного компонента modx-grid-user, который хранится в Ext.ComponentMgr.types['modx-grid-user'], параллельно добавляя новые методы (там же можно заменить и существующие). Мы можем расширять наш объект и стандартным объектом гриды юзеров MODx.grid.User, но в этом случае, если до нашего кода уже внедряются какие-то изменения в гриду, то они будут утрачены.
А здесь мы регистрируем xUserGrid.grid.modxUserExt в качестве компонента modx-grid-user, фактически переопределяя зарегистрированный ранее компонент на это имя в коде системы.
Для того и используется метод addLastJavascript в плагине, дабы быть уверенным, что подключение наших файлов будет уже после подключения системного modx.grid.user.js.
В итоге
Получаем кастомизированную таблицу пользователей, без внедрения своего кода в исходники системы. Таким образом, мы сохраняем возможность обновления MODX на новые версии, всем будущим поколениям.
P.S.
Благодарность Николаю Ланецу за подсказку о более правильном расширении, с использованием зарегистрированного компонента, хранящегося в Ext.ComponentMgr.types['modx-grid-user'].
Поблагодарить автора
Отправить деньги
Комментарии: 15
спасибо, Паша)
а то я уже в плагин джаваскрипта насовал. Грид расширил, но с доп. фильтрами так и не разобрался. Так что статья кстати
а то я уже в плагин джаваскрипта насовал. Грид расширил, но с доп. фильтрами так и не разобрался. Так что статья кстати
Добрый день, Павел! Я вот так и не понял где и как происходит замена грида юзеров. В предыдущем примере понятно
Ext.ComponentMgr.onAvailable('modx-user-tabs', function() {
находиться компонент и в него добавляется нужный функционал. А здесь как? Поясните пожалуйста.
Вот здесь мы расширяем наш объект xUserGrid.grid.modxUserExt, добавляя в него методы и свойства из стандартного объекта гриды юзеров MODx.grid.User, параллельно добавляя новые методы (там же можно заменить и существующие). Делается это ради того, чтобы, используя стандартный класс гриды юзеров, записать поверх него свой функционал.
А здесь мы регистрируем xUserGrid.grid.modxUserExt в качестве компонента modx-grid-user, фактически переопределяя зарегистрированный ранее компонент на это имя в коде системы.
Для того и используется метод addLastJavascript в плагине, дабы быть уверенным, что подключение наших файлов будет уже после подключения системного modx.grid.user.js.
А здесь мы регистрируем xUserGrid.grid.modxUserExt в качестве компонента modx-grid-user, фактически переопределяя зарегистрированный ранее компонент на это имя в коде системы.
Для того и используется метод addLastJavascript в плагине, дабы быть уверенным, что подключение наших файлов будет уже после подключения системного modx.grid.user.js.
Спасибо. Не знал что можно переопределить так компонент.
В статью добавил скорректированный вариант описания того, что вообще тут происходит.
ГлавноеНу и не забывать смотреть чужие примеры. Можно было вскольз и упомянуть.уметь пользоваться «методом тыка»понимать принцип того, что делаешь.
Упомянуть о чем?)
Что-то типа «вот увидел такой-то прием и решил его развить». Или хочешь сказать, что сам допер до такого метода расширения объекта и нигде ранее не видел?
Комментарий, на который ты ссылаешься датируется 30 декабря 2016 года. Ровно 10 дней назад я опубликовал на GitHub проект, в котором использовал такой прием, до которого допер, да, сам… следовательно, если развивать твою логику, то это не я подглядел у тебя, а ты у меня?)) Однако, я не претендую на это, ибо ничего тут сверхъестественного не вижу…
Да, действительно не так сделал. Сорян. Но лучше бы сделал так… Уточняю минус в чем: при таком подходе ты переписываешь чистый MODX-овый исходник. А если на него еще что-то навешено сторонним плагином (или не одним). Ты тогда просто перетираешь эти изменения. Собственно, по этой причине я и искал решение, которое по возможности не перетирало бы сторонние изменения. Вот листинг:
Попробуй своим способом дописать еще один чуть измененный плагин. Смогут они вместе сосуществовать на одной странице?
var _prototype = Ext.ComponentMgr.types['modx-grid-user'];
UserKarmaGrid = function(config) {
this.sm = new Ext.grid.CheckboxSelectionModel();
Ext.applyIf(config,{
fields: ['id','username','fullname','email','gender','blocked','role','active','cls', 'userkarma']
,sm: this.sm
,columns: [this.sm,{
header: _('id')
,dataIndex: 'id'
,width: 50
,sortable: true
},{
header: _('name')
,dataIndex: 'username'
,width: 150
,sortable: true
,renderer: function(value, p, record){
return String.format('<a href="?a=security/user/update&id={0}" title="{1}" class="x-grid-link">{2}</a>', record.id, _('user_update'), Ext.util.Format.htmlEncode( value ) );
}
},{
header: _('user_full_name')
,dataIndex: 'fullname'
,width: 180
,sortable: true
,editor: { xtype: 'textfield' }
,renderer: Ext.util.Format.htmlEncode
},{
header: _('email')
,dataIndex: 'email'
,width: 180
,sortable: true
,editor: { xtype: 'textfield' }
},{
header: _('active')
,dataIndex: 'active'
,width: 80
,sortable: true
,editor: { xtype: 'combo-boolean', renderer: 'boolean' }
},{
header: _('user_block')
,dataIndex: 'blocked'
,width: 80
,sortable: true
,editor: { xtype: 'combo-boolean', renderer: 'boolean' }
},{
header: 'Карма'
,dataIndex: 'userkarma'
,sortable: true
,editor: { xtype: 'numberfield'}
}]
});
UserKarmaGrid.superclass.constructor.call(this,config);
};
Ext.extend(UserKarmaGrid,_prototype,{});
Ext.reg('modx-grid-user',UserKarmaGrid);
Вот такой с таким подходом одновременно на странице уживаются и userKarma, и switchUser. Попробуй своим способом дописать еще один чуть измененный плагин. Смогут они вместе сосуществовать на одной странице?
Верная мысль, Николай! В твоем случае мы берем уже зарегистрированный компонент со всеми изменениями (если таковые были) и меняем его по своему. А в моем варианте я беру напрямую объект MODx.grid.User, следовательно и изменений (если таковые были, опять же) в него не записано. Спасибо за идею! Вот теперь упомяну. ;)
Пожалуйста.
а вот такой вот вопрос нарисовался, немного по теме прошлого топика больше:
как бы так грамотно на странице юзера вывести превью его аватарки? по типу image tv
как бы так грамотно на странице юзера вывести превью его аватарки? по типу image tv
это тоже здорово, но я вообще про страницу профиля. Прости, что в этой теме)
Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.