SimpleGallery: расширение функционала, часть 2
- Дополнения
- SimpleGallery
- Расширение функционала, часть 2
SimpleGallery: расширение функционала, часть 2
В этой статье речь пойдет о том, как изменить интерфейс SimpleGallery под конкретную задачу. При этом файлы плагина останутся без изменений — а значит, работа не пропадет после обновления SimpleGallery. Дальше много текста и говнокода.
Задача была такая: cайт для производителя мебели, состоящий, по сути, из галерей с фотками мебели, которые были реализованы с помощью SimpleGallery. Когда сайт уже был готов, заказчик внезапно пожелал, чтобы с фотографией выводились:
- цена;
- размеры;
- материал.
Кроме этого, для некоторых фотографий нужно показывать значок «Наша работа». Ну и пусть поле «Описание» редактируется с помощью TinyMCE (: А чтобы было красиво, основные поля и дополнительные поля будут редактироваться в отдельных вкладках.
Для решения задачи нужно изменить форму редактирования картинки, добавив туда дополнительные поля, а также внести изменения в контроллер и класс, отвечающий за работу с базой данных (модель).
Так как выборки и сортировки по новым полям не нужны, было решено не создавать дополнительные поля в таблице, а воспользоваться полем sg_properties, в котором хранятся параметры картинки в формате json. А для флажка «Наша работа» пригодится существующее поле sg_add.
Расширение классов
Так как было решено не добавлять дополнительные поля в таблицу, то достаточно внести изменения в контроллер. А именно изменить метод edit. Для этого создадим файл custom.class.php в папке assets/plugins/simplegallery/lib/:
<?php
namespace SimpleGallery;
require_once(MODX_BASE_PATH . 'assets/plugins/simplegallery/lib/controller.class.php');
//расширяем класс sgController
class customController extends sgController
{
public function edit()
{
$out = array();
$id = isset($_REQUEST['sg_id']) ? (int)$_REQUEST['sg_id'] : 0;
if ($id) {
$fields = array(
'sg_title' => $_REQUEST['sg_title'],
'sg_description' => $_REQUEST['sg_description'],
'sg_add' => $_REQUEST['sg_add']
);
$fields['sg_isactive'] = isset($_REQUEST['sg_isactive']) ? 1 : 0;
$row = $this->data->edit($id);
$_data = $row->toArray();
//получили значения дополнительных полей
$addProperties = array(
'price' => $_REQUEST['price'],
'msize' => $_REQUEST['msize'],
'material' => $_REQUEST['material']
);
//добавили их в поле sg_properties
$_data['sg_properties'] = array_merge($_data['sg_properties'], $addProperties);
//отправили все поля на сохранение
$_data = array_merge($_data, $fields);
$out['success'] = $row->fromArray($_data)->save();
} else {
$out['success'] = false;
}
return $out;
}
}
Теперь в настройки плагина SimpleGallery вписываем имя нашего класса (SimpleGallery\customController) и создаем еще один плагин на событие OnManagerPageInit, который этот класс загрузит:
$e = $modx->event;
if ($e->name == "OnManagerPageInit") {
if (isset($invokedBy) && $invokedBy=="SimpleGallery") include_once(MODX_BASE_PATH.'assets/plugins/simplegallery/lib/custom.class.php');
}
На этом с серверной частью все.
Изменение интерфейса
Здесь возникает две подзадачи. Во-первых, нужно изменить форму редактирования, во-вторых, внести изменения в обработку этой формы.
Если посмотреть папку assets/plugins/simplegallery/js/tpl/, то можно обнаружить там файлы с расширением *.handlebars. Это шаблоны для js-шаблонизатора Handlebars, в которые вынесены формы SimpleGallery + шаблон для превьюшки.
Синтаксис Handlebars достаточно простой и работа с такими шаблонами не сильно отличается от работы с чанками. Однако простое изменение файла editForm.handlebars ничего не даст — шаблоны нужно компилировать. Этот аспект я рассматривать не буду, скажу лишь, что потребуется установить где-нибудь nodejs и прочитать небольшую инструкцию в файле readme.txt, который можно найти в папке с шаблонами.
Создадим файл editFormMebel.handlebars:
![]()
ID {{data.sg_id}} {{sgLang.file}} {{data.sg_image}} {{sgLang.size}} {{data.sg_properties.width}}x{{data.sg_properties.height}}, {{bytesToSize data.sg_properties.size}} {{sgLang.createdon}} {{data.sg_createdon}} {{sgLang.save}}
{{sgLang.cancel}}
{{sgLang.save}}
{{sgLang.cancel}}
Разметка, конечно, не очень, но не важно. Компилируем:
handlebars editFormMebel.handlebars -f editFormMebel.js -m
Эта команда скомпилирует шаблон в файл editFormMebel.js и минифицирует его.
Теперь создадим еще одну форму, для редактирования описания с помощью TinyMCE. Эта форма будет показываться в окошке, если кликнуть по ссылке «Редактировать». Файл rteForm.handlebars:
{{sgLang.save}}
{{sgLang.cancel}}
Компилируем:
handlebars rteForm.handlebars -f rteForm.js -m
Файлы editFormMebel.js и rteForm.js копируем в assets/plugins/simplegallery/js/tpl/
Теперь добавим методы для вызова окошка с формой редактирования описания, для инициализации TinyMCE, а также изменим метод sgHelper.edit(). Создаем в папке assets/plugins/simplegallery/js/plugin/ файл editForm.js:
(function ($) {
sgHelper.rteForm = function (textarea) {
//готовим данные для передачи в шаблон
var context = {
textarea: textarea.val(),
modxTheme: sgConfig._modxTheme,
modxSiteUrl: sgConfig._modxSiteUrl,
sgLang: _sgLang
};
//получаем заполненный шаблон
var rteForm = $(Handlebars.templates.rteForm(context));
//создаем окошко
rteForm.window({
modal: true,
title: "Редактирование",
collapsible: false,
minimizable: false,
maximizable: false,
resizable: false,
onOpen: function () {
//когда окошко создано, нужно назначить обработчики для кнопок в форме
$('#rteCancel').click(function (e) {
rteForm.window('close', true);
});
$('#rteSave').click(function (e) {
//берем то, что наредактировали и сохраняем обратно в поле "Описание"
var content = tinyMCE.activeEditor.getContent();
textarea.val(content);
rteForm.window('close', true);
});
//цепляем редактор к полю в форме
sgHelper.initRTE();
},
onClose: function () {
//если окошко закрывается, то удаляем его вообще
//после закрытия окна с редактором нужно восстановить оверлей
var mask = $('.window-mask');
sgHelper.destroyWindow(rteForm);
$('body').append(mask);
}
})
};
sgHelper.edit = function (image) {
var data = image.data('properties');
var context = {
data: data,
modxTheme: sgConfig._modxTheme,
modxSiteUrl: sgConfig._modxSiteUrl,
sgLang: _sgLang
};
//получаем форму редактирования
var editForm = $(Handlebars.templates.editFormMebel(context));
editForm.window({
modal: true,
title: sgHelper.escape(this.stripText(data.sg_title, 80)),
doSize: true,
collapsible: false,
minimizable: false,
maximizable: false,
resizable: false,
onOpen: function () {
//создаем вкладки
$('#editTabs').tabs({
border: false,
onSelect: function (title, index) {
if (index) {
var tab = $('#editTabs').tabs('getTab', index);
$(tab).show();
editForm.window('resize');
}
}
});
//обработчики кнопок
//показываем окошко с редактором
$('.btn-rte').click(function (e) {
sgHelper.rteForm($('textarea', '#sgEdit'));
});
$('.sgEditCancel').click(function (e) {
editForm.window('close', true);
});
$('.sgEditSave').click(function (e) {
$.post(
sgConfig._xtAjaxUrl + '?mode=edit',
$('#sgForm,#sgAdd').serialize(),
function (data) {
data = sgHelper.getData(data);
if (data.success) {
editForm.window('close', true);
sgHelper.update();
} else {
$.messager.alert(_sgLang['error'], _sgLang['save_fail']);
}
})
})
},
onClose: function () {
sgHelper.destroyWindow(editForm);
}
});
}
})(jQuery);
Инициализацию TinyMCE я вынес в отдельный файл, assets/plugins/simplegallery/js/plugin/tinymce.js:
(function ($) {
sgHelper.initRTE = function () {
tinyMCE.init({
theme: 'advanced',
skin: 'cirkuit',
skin_variant: '',
mode: 'exact',
elements: 'rteField',
width: '100%',
height: '500',
language: 'ru',
element_format: 'xhtml',
schema: 'html4',
paste_text_use_dialog: true,
document_base_url: sgConfig._modxSiteUrl,
relative_urls: true,
remove_script_host: true,
convert_urls: true,
force_br_newlines: false,
force_p_newlines: true,
forced_root_block: 'p',
//valid_elements: mce_valid_elements,
popup_css_add: '/assets/plugins/tinymce/style/popup_add.css',
theme_advanced_source_editor_height: 400,
accessibility_warnings: false,
theme_advanced_toolbar_location: 'top',
theme_advanced_statusbar_location: 'bottom',
theme_advanced_toolbar_align: 'ltr',
theme_advanced_font_sizes: '80%,90%,100%,120%,140%,160%,180%,220%,260%,320%,400%,500%,700%',
content_css: '/assets/plugins/tinymce/style/content.css',
formats: {
alignleft: {selector: 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li,table,img', classes: 'justifyleft'},
alignright: {selector: 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li,table,img', classes: 'justifyright'},
alignfull: {selector: 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li,table,img', classes: 'justifyfull'}
},
apply_source_formatting: true,
remove_linebreaks: false,
convert_fonts_to_spans: true,
plugins: 'visualblocks,autolink,inlinepopups,autosave,save,advlist,style,fullscreen,advimage,paste,advlink,media,contextmenu,table,youtubeIframe',
theme_advanced_buttons1: 'undo,redo,|,bold,forecolor,backcolor,strikethrough,formatselect,fontsizeselect,pastetext,pasteword,code,|,fullscreen,help',
theme_advanced_buttons2: 'image,media,youtubeIframe,link,unlink,anchor,|,justifyleft,justifycenter,justifyright,|,bullist,numlist,|,blockquote,outdent,indent,|,table,hr,|,visualblocks,styleprops,removeformat',
theme_advanced_buttons3: '',
theme_advanced_buttons4: '',
theme_advanced_resize_horizontal: false,
external_link_list_url: '/assets/plugins/tinymce/js/tinymce.linklist.php',
template_external_list_url: '/assets/plugins/tinymce/js/get_template.php',
template_popup_width: 550,
template_popup_height: 350,
theme_advanced_blockformats: 'p,h1,h2,h3,h4,h5,h6,div,blockquote,code,pre',
theme_advanced_styles: 'left=justifyleft;right=justifyright',
theme_advanced_disable: '',
theme_advanced_resizing: true,
fullscreen_settings: {
theme_advanced_buttons1_add_before: 'save'
},
plugin_insertdate_dateFormat: '%d-%m-%Y',
plugin_insertdate_timeFormat: '%H:%M:%S',
entity_encoding: 'named',
file_browser_callback: 'mceOpenServerBrowser',
paste_text_sticky: true,
setup: function (ed) {
ed.onPostProcess.add(function (ed, o) {
// State get is set when contents is extracted from editor
if (o.get) {
o.content = o.content.replace('<p>{' + '{', '{' + '{');
o.content = o.content.replace('}}</p>', '}}');
o.content = o.content.replace(/<p>\[([[!\~\^])/g, '[$1');
o.content = o.content.replace(/([\]\!\~\^])\]<\/p>/g, '$1]');
}
});
},
onchange_callback: false,
valid_elements: "*[*]"
})
}
})(jQuery);
Здесь я просто взял код инициализации TinyMCE из редактирования документа. Да, нужно учитывать, чтобы это все работало, на странице должен быть включен TinyMCE.
Теперь создаем в папке assets/plugins/simplegallery/js/ файл custom.json:
{
"scripts": {
"editFormMebelTemplate": {
"version": "1.0.0",
"src": "assets/plugins/simplegallery/js/tpl/editFormMebel.js"
},
"editForm": {
"version": "1.0.0",
"src": "assets/plugins/simplegallery/js/plugin/editForm.js"
},
"sgTinyMCE": {
"version": "0.0.0",
"src": "assets/plugins/simplegallery/js/plugin/tinymce.js"
},
"rteForm": {
"version": "1.4.1",
"src": "assets/plugins/simplegallery/js/tpl/rteForm.js"
}
}
}
Если есть файл custom.json, то SimpleGallery загружает из него скрипты дополнительно к скриптам, описанным в файле scripts.json. Аналогично загружаются и css-файлы.
На этом работа закончена. Итак, измененная форма:
Вкладка с дополнительными полями:
Редактор:
Редактор описания мне понадобился сегодня для другого сайта, поэтому я решил положить его в коробку (: Так что если кому-то вдруг нужно, но разбираться лень, то достаточно переименовать файл _custom.json.