Pull to refresh

Допиливаем Django-admin бензопилой. Часть II — WYSIWYG

Reading time5 min
Views10K
Итак обещанное продолжение прошлого поста. В результате мы прикрутим TinyMCE к flatpages и превратим лист/форму созданной модели с картинками в простой файловый менеджер для вставки картинок. Для этого понадобится совсем немного Javascript и единственная строчка в 'admin.py'.

Во-первых хочу сразу оговориться, что доволен тем фактом, что разделил эту статью на две части — появилась возможность почитать комментарии (на хабре), учесть некоторые ошибки и на основе этого сразу сделать небольшое предупреждение. Если вы просто хотите прикрутить thumbnails или WYSIWYG-редактор в ваше Django-приложение, то эта статья не для вас: возьмите готовый плагин и не парьтесь — наверняка получите более мощное и функциональное решение. Хотя для каждого простого случая добавлять зависимости сомнительного происхождения (аля djangosnippets) в проект — тоже не вариант.

Моя цель состоит в другом — на простых примерах показать как с помощью кастомного JS можно безболезненно решать подобные задачи и другие, намного более сложные, решение которых «в лоб» возможно потребовало бы грязных низкоуровневых хаков в коде Django.

Итак, в результате первой части у нас имеется модель для дополнительных картинок, которые мы намерены использовать для flatpages, и настроенную админку, которая теперь умеет отображать thumbnails картинок в списке объектов и на форме редактирования.



Для начала подключим TinyMCE. Предпочитаю именно этот редактор — дело вкуса и привычки. Тем более, что сейчас вместе с ним поставляется плагин для jQuery, там что довольно неплохо вписывается в набор используемых мною JS библиотек. Сам этот плагин вместе с файликом 'base.js' мы уже подключили в прошлой части глобально, добавив его в 'admin/base_site.html', что довольно удобно в случае, если WYSIWYG вам нужен для нескольких моделей. Код инициализации и настройки TinyMCE будет находиться в 'base.js'. Приведём его к следующему виду:

function tinyDjangoBrowser(field_name, url, type, win) {
    var managerURL = window.location.toString()
            + '../../../shop/pagepicture/?type=' + type;

    tinyMCE.activeEditor.windowManager.open({
        file: managerURL,
        title: 'Кликните на эскиз нужной картинки',
        width: 800,
        height: 450,
        resizable: 'yes',
        inline: 'yes',
        close_previous: 'no',
        popup_css : false
    }, {
        window: win,
        input: field_name
    });

    return false;
}

$().ready(function() {
    var tinymceOptions = {
        script_url: '/media/lib/tiny_mce/tiny_mce.js',

        theme: 'advanced',
        plugins: 'safari,pagebreak,layer,table,advhr,advimage,advlink,iespell,inlinepopups,insertdatetime,preview,media,searchreplace,print,contextmenu,fullscreen,noneditable,visualchars,nonbreaking,xhtmlxtras',

        theme_advanced_buttons1: 'bold,italic,underline,strikethrough,|,justifyleft,justifycenter,justifyright,justifyfull,formatselect,fontselect,fontsizeselect',
        theme_advanced_buttons2: 'search,replace,|,bullist,numlist,|,outdent,indent,blockquote,|,undo,redo,|,link,unlink,anchor,image,cleanup,help,code,|,insertdate,inserttime,preview,|,forecolor,backcolor',
        theme_advanced_buttons3: 'tablecontrols,|,hr,removeformat,visualaid,|,sub,sup,|,charmap,iespell,media,advhr,|,print,|,fullscreen',
        theme_advanced_buttons4: 'insertlayer,moveforward,movebackward,absolute,|,cite,abbr,acronym,del,ins,attribs,|,visualchars,nonbreaking,pagebreak',
        theme_advanced_toolbar_location: 'top',
        theme_advanced_toolbar_align: 'left',
        theme_advanced_statusbar_location: 'bottom',
        theme_advanced_resizing: true,

        // Drop lists for link/image/media/template dialogs
        template_external_list_url: 'lists/template_list.js',
        external_link_list_url: 'lists/link_list.js',
        external_image_list_url: 'lists/image_list.js',
        media_external_list_url: 'lists/media_list.js',

        doctype: '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">',
        content_css: '/media/css/admin/editor.css',

        file_browser_callback: 'tinyDjangoBrowser'
    };

    $('textarea#id_content').tinymce(tinymceOptions);
});


Вот собственно и всё. Теперь на форме редактирования flatpages у нас будет TinyMCE в почти максимальной конфигурации. В том числе с возможностью вставлять изображения и редактировать их свойства.



Однако вставлять картинки по их URL это пол-дела, необходимо обеспечить возможность прямо из диалога вставки картинки, без лишних телодвижений залить на сервер новую картинку, просмотреть имеющиеся и выбрать нужную. Именно такую функциональность обеспецивают File/Image managers для популярных WYSIWYG редакторов. Их отличительной особенностью является то, что для работы им необходима серверная часть — так называемый «коннектор», который позволяет осуществлять необходимые действия с файловой системой, а менеджеру останется лишь отображать необходимую информацию и возвращать параметры выбранной картинки диалоговому окну.



Вобщем-то всю эту функциональность нам уже предоставляет сама Django, точнее наша готовая модель для управления картинками. Остаётся только заставить TinyMCE использовать её же в качестве менеджера файлов. Для этого нам и нужна функция 'tinyDjangoBrowser()' и опция TinyMCE 'file_browser_callback'. Всё что делает эта функция — открывает страницу управления моделью с картинками в popup-окне. Этого уже достаточно для того чтобы выбрать/удалить/закачать картинки. Дело остаётся за малым — страница со списком объетов должна различать была ли она открыта просто так или из TinyMCE-окна, и в последнем случае возвращать параметры выбранной картинки. Для этого декорируем админку для этой модели c помощью небольшого скрипта 'tiny_django_browser.js'.

$().ready(function() {
    if (window.parent) {
        $.getScript('/media/lib/tiny_mce/tiny_mce_popup.js', function() {
            $('#changelist tbody a.image-picker').click(function(e) {
                var url = $(this).attr('href');
                var win = tinyMCEPopup.getWindowArg("window");
                var alt = $(this).find('img').attr('alt');

                win.document.getElementById(tinyMCEPopup.getWindowArg("input")).value = url;
                win.document.getElementById('alt').value = alt;

                if (typeof(win.ImageDialog) != "undefined") {
                    if (win.ImageDialog.getImageData) win.ImageDialog.getImageData();
                    if (win.ImageDialog.showPreviewImage) win.ImageDialog.showPreviewImage(url);
                }

                tinyMCEPopup.close();
                return false;
            });
        });
    }
});


Дело за малым, подключаем этот скрипт к админке, FlatPictureAdmin на данный момент будет выглядеть следующим образом:

class PagePictureAdmin(admin.ModelAdmin):
    class Media:
        js = ['js/admin/display_thumbs.js', 'js/admin/tiny_django_browser.js']
    list_display = ['get_thumbnail_html', '__unicode__', 'description']
    list_display_links = ['__unicode__']
admin.site.register(PagePicture, PagePictureAdmin)


Вот и всё. Теперь картинка вместе с описанием в alt-аттрибуте будет вставляться по клику на thumbnail.



В следующий раз постараюсь подобрать пример более сложной и редкой функциональности. Надеюсь, что удастся почерпнуть какие-то идеи и предложения из ваших комментариев.
Tags:
Hubs:
Total votes 37: ↑31 and ↓6+25
Comments31

Articles