Поддержать Проект

Обратная связь

[MODX] Guru
  • Информация
  • Разработчикам
  • Дополнения
    • DocLister
    • DLMenu
    • DLCrumbs
    • DLBuildMenu
    • DLLastViews
    • DLSiblings
    • DLRequest
    • DLglossary
    • DLSitemap
    • DocInfo
    • FormLister
    • Wayfinder
    • phpthumb
    • LikeDislike
    • eForm
    • Ditto
    • multiTV
    • AjaxMegaSearch
    • AjaxSearch
    • WebLoginPE
    • Breadcrumbs
    • CodeMirror
    • AnythingRating
    • Easy Newsletter
    • FirstChildRedirect
    • OpenGraphTags
    • ddTypograph
    • TagSaver
    • BlackList
    • CfgTv
    • ModxAccount
    • Forgot Manager Login
    • GetField
    • if
    • Jot
    • ListChild
    • ListIndexer
    • ManagerManager
    • ddMMEditor
    • MaxiGallery
    • MemberCheck
    • ddGetMultipleField
    • MetaX
    • MODxBB и phpBB
    • Yams
    • Personalize
    • PHx
    • Reflect
    • tagLinks
    • TransAlias
    • TvTagCloud
    • UltimateParent
    • WebSignup
    • WebLogin
    • countViews
    • thumb
    • imageCaptor
    • optimizeJPG
    • Preview Next
    • Shopkeeper
    • SiteMap
    • Sass
    • Selector
    • SimpleGallery
      • Вывод изображений
      • Вывод списка галерей
      • Генерация превью при загрузке
      • Расширение функционала, часть 1
      • Расширение функционала, часть 2
      • Расширение функционала, часть 3
      • Переход с MaxiGallery
      • Переход с EvoGallery
      • Переход с PssGallery
      • Переход с MultiPhotos
      • Подключение редактора
    • SimpleTube
    • SimpleFiles
    • Star Rating
    • MinifyX
    • adminNav
    • SimplePolls
    • CResource
    • MODxAPI
    • customTables
    • HtmlInLine
    • HtmlMinModxEvo
    • SHKUserProfile
    • PickDocsInTree
    • evoSearch
    • editDocs
    • PageBuilder
    • HybridAuth
    • Compare
    • alterTitle
  • Виджеты
  • Уроки
  • Разработчики
  • Готовые примеры
  • Блог
  • Конфиги
  • HTML коды
© [MODX] Guru
  • SimpleGallery

SimpleGallery: расширение функционала, часть 2

  • Дополнения
  • SimpleGallery
  • Расширение функционала, часть 2
354

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.yes}}
{{sgLang.save}}
{{sgLang.cancel}}
{{sgLang.yes}}
{{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.