Интеграция модуля с RedsolutionCMS
Как сделать так, чтобы ваш модуль распознавался установщиком, настраивался через веб интерфейс и участвовал в сборке проекта.
Итак, вы решили добавить свой модуль в Redsolution CMS. Первое время мы ещё будем работать над API установщика, поэтому следите за изменениями в документации.
Организация
Для того чтобы ваш модуль распознавался как подключаемый в Redsolution CMS, надо, чтобы его название содержало redsolutioncms. Если вы не хотите публиковать модуль с таким названием, то можете опубликовать установщик отдельно, прописав в его зависимостях свой модуль.
В списке все модули поделены на категории. Категорию для своего модуля можно задать, если в его документации встретится ссылка вида /classifiers/<classifier>. Пока мы задали лишь несколько категорий:
frontpage— приложения, которые могут быть главной страницей;content— приложения, которые так или иначе связаны с контентом на сайте;utilities— вспомогательные приложения;templates— шаблоны.
Приложения для главной страницы и шаблоны это особые категории, так как при выборе модулей можно выбрать только один шаблон и хотя бы одно приложение для главной страницы.
Как уже упоминалось, для того чтобы CMS после загрузки модуля поняла, как настраивать модуль, необходимо указать точку входа. Сделать это можно в setup.py:
setup(
entry_points={
'redsolutioncms': ['myapp = myapp.redsolution_setup', ],
},
)
Redsolution CMS ищет точку входа с названием redsolutioncms. В примере выше CMS попытается импортировать myapp.redsolution_setup. Если в этом модуле есть импортируемый модуль urls, то CMS будет считать модуль настраиваемым и предложит ссылку для настройки на третьем шаге.
Структура настройщика
Теперь о самом модуле myapp.redsolution_setup. Мы рассмотрим пример с настройщиком для приложения обратной связи redsolutioncms.django.feedback. Мы сделаем сразу интерфейс для настройки.
Модели
Для моделей есть базовый класс redsolutioncms.models.BaseSettings и менеджер redsolutioncms.models.BaseSettingsManager. Метод get_settings у менеджера возвращает или создаёт настройки по умолчанию. Из модуля настройки можно импортировать настройки самой Redsolution CMS:
from redsolutioncms.models import CMSSettings
cms_settings = CMSSettings.objects.get_settings()
Для создания модели настройщика определитесь, какие настройки вы хотите видеть. Допустим, мы решили добавить одну настройку для модуля обратной связи: это флаг, определяющий отображение отдельной страницы с обратной связью. В модуле это переменная DIRECT_TO_TEMPLATE в settings.py.
Текст модели и её менеджера для настройщика модуля будет выглядеть примерно так:
from django.db import models
from django.utils.translation import ugettext_lazy as _
from redsolutioncms.models import CMSSettings, BaseSettings, BaseSettingsManager
class FeedbackSettingsManager(BaseSettingsManager):
def get_settings(self):
if self.get_query_set().count():
return self.get_query_set()[0]
else:
feedback_settings = self.get_query_set().create()
return feedback_settings
class FeedbackSettings(BaseSettings):
use_direct_view = models.BooleanField(
verbose_name=_('Use dedicated view to render feedback page'),
default=True
)
objects = FeedbackSettingsManager()
Админка
Для интерактивной настройки мы решили использовать уже готовые классы админки Django. Для того чтобы дать пользователю выбрать значение флага, нужно создать класс ModelAdmin и прописать его в urls.py настройщика.
Текст admin.py:
from django.contrib import admin
from feedback.redsolution_setup.models import FeedbackSettings
from redsolutioncms.admin import CMSBaseAdmin
class FeedbackSettingsAdmin(CMSBaseAdmin):
model = FeedbackSettings
Да, такой короткий. И текст urls.py, в котором создаётся на лету объект админки и пользователю показывают страницу изменения свойств объекта:
from django.conf.urls.defaults import patterns, url
from feedback.redsolution_setup.admin import FeedbackSettingsAdmin
admin_instance = FeedbackSettingsAdmin()
urlpatterns = patterns('',
url(r'^$', admin_instance.change_view, name='feedback_index'),
)
Однако создание модели это половина работы. Модели настройщиков хранят значения настроек, на этапе создания интегрируются друг с другом и предоставляют интерактивный интерфейс. Конечным же продуктом будут файлы проекта. Зачастую изменения надо вносить только в settings.py и urls.py. Изменения вносятся на этапе сборки.
Сборка
За сборку настроек отвечает файл make.py, он обязательный. Сборка проходит в три этапа.
- Premake. На этом этапе подготавливаются начальные значения переменных, создаются папки, Redsolution CMS создаёт базовый шаблон и так далее. В premake располагайте те функции, которые должны отработать прежде всех остальных.
- Make. Основной этап сборки. Redsolution CMS создаёт проект, рендерит шаблоны, затем остальные модули делают то же самое. Некоторые из модулей копируют медиа файлы в папку проекта на этой стадии.
- Postmake. Заключительная стадия. Она введена для тех операций, которые предполагают почти собранный проект. На этой стадии Redsolution CMS генерирует карты сайта для модулей, обрабатывает начальные данные приложений и так далее. Некоторые приложения копируют медиа файлы и на этой стадии.
Для записи настроек в файлы проекта созданы методы объектов CMSSettings.
render_to
cms_settings.render_to(file_name, template_name, dictionary=None, mode='a+')
Генерирует контент в заданный файл по указанному шаблону. Параметры:
file_name— имя файла, в который ведётся запись;template_name— путь к шаблону;dictionary— словарь для отрисовки шаблона;mode— режим записи. Для перезаписи передайтеmode='w'.
В этой функции file_name может быть списком, при записи этот список будет соединён функцией os.path.join в путь до файла. Например:
cms_settings = CMSSettings.objects.get_settings()
feedback_settings = FeedbackSettings.objects.get_settings()
cms_settings.render_to('settings.py', 'feedback/redsolutioncms/settings.pyt', {
'feedback_settings': feedback_settings,
})
copy_file
cms_settings.copy_file(dst, src, mode='wb')
Копирует или дописывает контент указанного файла. Параметры: dst — файл куда писать (здесь не работает трюк со списком), src — исходный файл, mode — режим записи. Эта функция может пригодиться, если вам нужно скопировать, например, начальные данные:
cms_settings.copy_file(
join(cms_settings.project_dir, 'fixtures', 'initial_data.json'),
join(dirname(__file__), 'fixtures', 'project_data', 'initial_data.json'),
mode='a',
)
copy_dir
cms_settings.copy_dir(dst, src, merge=True)
Функция для копирования каталогов. Параметры: dst — каталог куда записывать, src — исходный каталог, merge — режим перезаписи. Если merge=True, только новые файлы дописываются в каталог; если merge=False, то целевой каталог полностью замещается исходным. Этой функцией удобно пользоваться для копирования стилей или медиа файлов:
cms_settings = CMSSettings.objects.get_settings()
cms_settings.copy_dir(
os.path.join(cms_settings.project_dir, 'media',),
os.path.join(os.path.dirname(__file__), 'templates', 'classic', 'media'),
merge=True
)
Пример make класса
В заключение приведём пример, который вы можете использовать как основу для одного из установщиков:
from redsolutioncms.make import BaseMake
from redsolutioncms.models import CMSSettings
from feedback.redsolution_setup.models import FeedbackSettings
from os.path import dirname, join
import shutil
class Make(BaseMake):
def make(self):
super(Make, self).make()
cms_settings = CMSSettings.objects.get_settings()
feedback_settings = FeedbackSettings.objects.get_settings()
cms_settings.render_to('settings.py', 'feedback/redsolutioncms/settings.pyt', {
'feedback_settings': feedback_settings,
})
cms_settings.render_to('urls.py', 'feedback/redsolutioncms/urls.pyt', {
'feedback_settings': feedback_settings,
})
def postmake(self):
super(Make, self).postmake()
cms_settings = CMSSettings.objects.get_settings()
feedback_settings = FeedbackSettings.objects.get_settings()
feedback_media_dir = join(dirname(dirname(__file__)), 'media')
project_media_dir = join(cms_settings.project_dir, 'media')
try:
shutil.rmtree(join(project_media_dir, 'feedback'))
except OSError:
pass
if 'redsolutioncms.django.server.config' not in cms_settings.installed_packages:
shutil.copytree(
join(feedback_media_dir, 'feedback'),
join(project_media_dir, 'feedback'),
)
make = Make()