Создание плагина. От идеи до публикации. Часть 2.

Создание диалога добавления карусели
Прежде всего определим, каким образом карусель будет вставлена в редактор. В плагине будет использоваться следующая структура: тег «carousel» содержит одно или несколько изображений (тегов img), которые в дальнейшем можно добавлять с помощью стандартного диалога вставки изображения. Диалог вставки тега «carousel» с первым изображением мы создадим, из диалога вставки изображения. Шаблон диалога представлен в файле «skin/developer/window_load_img.tpl», его мы и будем переделывать. Созданный шаблон с помощью зарегистрированного хука будет добавлен на страницу, проинициализирован. Инструменты, обеспечивающие функционал формы будут заимствованы от диалога вставки изображения.

Для начала создадим рабочие файлы в корневом каталоге плагина как представлено на рисунке. Для диалога добавления карусели нам понадобится файл шаблона window_load_img.tpl, который как и сказано выше мы скопируем из темы разработчика или Вашей текущей темы и расположим в каталоге «templates\skin\default» плагина, переименовав, по необходимости. Также понадобятся файлы локализации, содержащие возвращаемый массив с текстами, используемыми в диалоге. Рассмотрим эти файлы подробнее…
Файл шаблона, по сравнению с первоначальным немного видоизменен, в нем удалены хуки и списки выбора с вариантами расположения изображения, изменены идентификаторы тегов (в них img заменено на carousel), изменены надписи заголовка окна и надпись над кнопкой выбора изображения. Ниже приведен код файла «window_load_carouse.tpl» который довольно прозрачен и комментариев требуют лишь некоторые строки в остальном, я думаю, читатель разберется сам.
<div class="modal modal-image-upload" id="window_upload_carousel">
{* Заголовок окна *}
    <header class="modal-header">
        <h3>{$aLang.plugin.carousel.open_image_for_carousel}2</h3>
        <\a href="#" class="close jqmClose"></a>
    </header>
{* Содержимое окна *}
    <div class="modal-content">
    {* Кнопки выбора варианта загрузки изображения *}
        <ul class="nav nav-pills nav-pills-tabs">
            <li class="active js-block-upload-carousel-item" data-type="pc">
                <\a href="#">{$aLang.uploadimg_from_pc}</a>
            </li>
            <li class="js-block-upload-carousel-item" data-type="link">
                <\a href="#">{$aLang.uploadimg_from_link}</a>
            </li>
        </ul>

        <form method="POST" action="" enctype="multipart/form-data" id="block_upload_carousel_content_pc"
              onsubmit="return false;" class="tab-content js-block-upload-carousel-content" data-type="pc">
            <p><label for="img_file">{$aLang.plugin.carousel.upload_carousel_file}:</label>
                <input type="file" name="img_file" id="img_file" class="input-text input-width-full"/>
            </p>

            <p><label for="form-carousel-title">{$aLang.uploadimg_title}:</label>
                <input type="text" name="title" id="form-carousel-title" class="input-text input-width-full"/>
            </p>
            <button type="submit" class="button button-primary"
                    onclick="ls.ajaxUploadCarouselImage(
                            'block_upload_carousel_content_pc','{$sToLoad}');">{$aLang.uploadimg_submit}
            </button>
            <button type="submit" class="button jqmClose">{$aLang.uploadimg_cancel}</button>
        </form>


        <form method="POST" action="" enctype="multipart/form-data" id="block_upload_carousel_content_link"
              onsubmit="return false;" style="display: none;" class="tab-content js-block-upload-carousel-content"
              data-type="link">
            <p><label for="carousel_url">{$aLang.uploadimg_url}:</label>
                <input type="text" name="carousel_url" id="carousel_url" value="http://"
                       class="input-text input-width-full"/>
            </p>

            <p><label for="form-carousel-url-title">{$aLang.uploadimg_title}:</label>
                <input type="text" name="title" id="form-carousel-url-title" class="input-text input-width-full"/>
            </p>
            <button type="submit" class="button button-primary"
                    onclick="ls.insertCarouselToEditor(
                    jQuery('#carousel_url').val(),
                    '',
                    jQuery('#form-carousel-url-title').val());">{$aLang.uploadimg_link_submit_paste}</button>
        {$aLang.or}
            <button type="submit" class="button button-primary"
                    onclick="ls.ajaxUploadCarouselImage(
                            'block_upload_carousel_content_link',
                            '{$sToLoad}');">{$aLang.uploadimg_link_submit_load}</button>
            <button type="submit" class="button jqmClose">{$aLang.uploadimg_cancel}</button>
        </form>
    </div>
</div>

Далее созданный шаблон необходимо вывести в браузер. Для этого создадим хук, зарегистрируем его и с помощью него выведем созданный нами шаблон. Файл класса хука назовем HookCarousel.class.php и располагаться он будет в «classes/hooks» – эти каталоги также необходимо будет создать. В нашем случае класс хука имеет простой вид и содержит два метода – метод регистрации – RegisterHook(), который и производит регистрацию хука в случае если текущий пользователь зарегистрирован. И метод Carousel(), возвращающий обработанный (скомпилированный) шаблон. Сам класс выглядит следующим образом:
<?php
class PluginCarousel_HookCarousel extends Hook {

    public function RegisterHook() {
        if ($oUserCurrent = $this->User_GetUserCurrent() && (Router::GetAction()=="topic" )) {
            $this->AddHook('template_body_end', 'Carousel');
        }
    }

    public function Carousel() {
        return $this->Viewer_Fetch(Plugin::GetTemplatePath(__CLASS__) . 'window_load_carousel.tpl');
    }

}

Для того что бы привязать открытие формы к нажатию кнопки необходимо скорректировать метод beforeInsert в ее настройках, задаваемых, напомню, в файле carouselScript.js:
beforeInsert: function() {
    ls.blocks.initSwitch('upload-carousel');
    jQuery("#window_upload_carousel")
        .jqm()
        .jqmShow()
}

Первая строка инициализирует таб-панели на форме, переключающие варианты загрузки изображений, следующая цепочка jQuery инициализирует и выводит окно.
Ну вот, зачин для окна сделали, теперь остается с помощью JavaScript реализовать его функционал.

Разработка функционала диалогового окна
В приведенном ниже шаблоне видно, что для загрузки изображения используется функция ls.ajaxUploadCarouselImage. Эта функция появилась заменой имени в стандартной ls.ajaxUploadCarouselImage, которая находиться в файле main.js движка. Код этой функции мы и будем использовать, добавив его в наш «carouselScript.js» и удалив по ненужности хуки.
ls.ajaxUploadCarouselImage = function(form, sToLoad) {
    ls.ajaxSubmit('upload/image/', form, function(data) {
        if (data.bStateError) {
            ls.msg.error(data.sMsgTitle, data.sMsg);
        } else {
            $.markItUp({replaceWith: "<carousel>\n" + data.sText + "\n\n</carousel>"});
            $('#window_upload_carousel')
                .find('input[type="text"], input[type="file"]').val('')
                .end()
                .jqmHide();
        }
    });
};

Для вставки ссылки на изображение в текст используется функция ls.insertCarouselToEditor (см. листинг шаблона window_load_carouse.tpl), она будет являться полной копией метода ls.topic.insertImageToEditor, реализованного в файле topic.js.
ls.insertCarouselToEditor = function(sUrl, sAlign, sTitle) {
    sAlign = sAlign == 'center' ? 'class="image-center"' : 'align="' + sAlign + '"';
    $.markItUp({replaceWith: '<img src="' + sUrl + '" title="' + sTitle + '" ' + sAlign + ' />'});
    $('#window_upload_carousel')
        .find('input[type="text"]').val('')
        .end()
        .jqmHide();
    return false;
};


PS
Как оказалось параметры созданной кнопки вставки карусели: openWith и closeWith оказались не нужны, поэтому их смело можно заремить.И осталось нам на самом деле не много:
1. Добавить скрипты карусели из bootstrap-а.
2. Организовать вывод карусели через замену тега carousel требуемым фреймворком кодом.
3. Сходить за пивом.


Добавление скриптов
Функционал по добавлению в текст тегов карусели мы реализовали, осталось добавить скрипт карусели из bootstrap, немного поднастроить типограф, что бы он пропускал новые теги и, самое главное, создать саму карусель при выводе статьи.
Прежде всего советую ознакомиться с функционалом карусели bootstrap-a и его html-кодом на twitter.github.com/bootstrap и загрузить файлы карусели предварительно установив галочки на странице загрузки только напротив «Сarousel» и «Transitions» в разделе «1. Choose components» и напротив «Сarousel» в «2. Select jQuery plugins».
Из загруженного пакета нам понадобятся файлы: bootstrap.min.css, который мы поместим в каталог css нашего плагина, и файл bootstrap.min.js в каталог js соответственно.
Еще раз напомню, при загрузке пакета bootstrap мы выбирали только компонент carousel и загруженые файлы не содержат «лишних» стилей и скриптов.
Теперь осталось подключить скопированные файлы, но мы это уже делали в главном классе плагина — Plugin.Carouse.class.php. Теперь он (класс) выглядит следующим образом:
class PluginCarousel extends Plugin {

    /** Инициализация плагина */
    public function Init() {
        parent::Init();

        /** Для добавления кнопки в редатктор запустим скрипт, корректирующий markitup */
        $this->Viewer_AppendScript(dirname(__FILE__) . '/js/language/' . Config::get('lang.current') . '.js');
        $this->Viewer_AppendScript(dirname(__FILE__) . "/js/carouselScript.js");
        $this->Viewer_AppendScript(dirname(__FILE__) . "/js/bootstrap.min.js");

        /** Добавление стилей */
        $this->Viewer_AppendStyle(dirname(__FILE__) . "/css/style.css");
        $this->Viewer_AppendStyle(dirname(__FILE__) . "/css/bootstrap.min.css");

    }

}


Продолжение
Часть 3

5 комментариев

avatar
Почему вы добавляете скрипты через Viewer_AppendScript, а не через конфиги, как это принято в LS?
Файлы js и css должны лежать внутри папки /templates/skin/defualt
avatar
Если что, поправьте, могу быть неправ, я динозавр oO
avatar
Вообще-то допустим и тот, и другой вариант. Но вот такой подход, кстати, на мой взгляд, более предпочтителен, чем через конфиг. Он позволяет более интеллектуально обрабатывать добавляемые скрипты и стили. Напр., гибко определять местонахождение файлов, выполнять какой-то препроцессинг, перемещать файлы в runtime-зону и т.д.
avatar
Делал всё как у вас написано. Кнопка появляется. Но при нажатии на нее не появляется окно «window_load_carousel.tpl», а просто появляются тэги carousel в редакторе. Что делать? У меня altocms-0.9.7.1 на денвере. До 3-й части статьи дошел: каруселька работает, хотя и криво, думаю из-за bootstrap.min.css — кнопки на сайте показываются в другом стиле.
Вообще-то я это делаю для обучения, чтобы понять как устроен livestreet. На самом деле я хочу сделать загрузку на сайт видеофайла при помощи кнопки видео аналогично с загрузкой изображений. Если подскажете, как это сделать, буду очень благодарен. Я знаю что есть плагин LiteVideo, но хотелось бы самому сделать плагин, так как в LiteVideo используется не помню какой плэер, а я хочу показывать видео при помощи тэга video в html5.
avatar
В файле HookCarousel.class.php закомментировал if и закрывающую скобку
        //if ($oUserCurrent = $this->User_GetUserCurrent() && (Router::GetAction()=="topic" )) {
            $this->AddHook('template_body_end', 'Carousel');
        //}

и заработало, только ещё в файле «window_load_carousel.tpl» надо убрать слэши перед «a» в тэге a href.
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.