Итак обещанное продолжение прошлого поста. В результате мы прикрутим 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'. Приведём его к следующему виду:
Вот собственно и всё. Теперь на форме редактирования flatpages у нас будет TinyMCE в почти максимальной конфигурации. В том числе с возможностью вставлять изображения и редактировать их свойства.

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

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

В следующий раз постараюсь подобрать пример более сложной и редкой функциональности. Надеюсь, что удастся почерпнуть какие-то идеи и предложения из ваших комментариев.
Во-первых хочу сразу оговориться, что доволен тем фактом, что разделил эту статью на две части — появилась возможность почитать комментарии (на хабре), учесть некоторые ошибки и на основе этого сразу сделать небольшое предупреждение. Если вы просто хотите прикрутить 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.

В следующий раз постараюсь подобрать пример более сложной и редкой функциональности. Надеюсь, что удастся почерпнуть какие-то идеи и предложения из ваших комментариев.
