Интеграция модуля с RedsolutionCMS

Итак, вы решили добавить свой модуль в Redsolution CMS.

Первое время мы ещё будем работать над API установщика, поэтому следите за изменениями в документации.

Организация

Для того, чтобы ваш модуль распознавался как подключаемый в Redsolution CMS надо, чтобы его название содержало redsolutioncms. Если вы не хотите публиковать модуль с таким названием, то можете опубликовать установщик отдельно, прописав в его зависимостях свой модуль.

В списке все модули поделены на категории. Категорию для своего модуля можно задать, если в его документации встретится ссылка вида:

http://www.redsolutioncms.org/classifiers/<classifier>

Пока мы задали лишь несколько категорий:

  • frontpage - приложения, которые могут быть главной страницей
  • content - приложения, которые так или иначе связаны с контентом на сайте
  • utilities - вспомогательные приложения
  • templates - шаблоны

Приложения для главной страницы и шаблоны особые категории, т.к. при выборе модулей можно выбрать только один шаблон и хотя бы одно приложение для главной страницы.

Как уже упоминалось, для того, чтобы CMS после загрузки модуля поняла, как настраивать модуль, необходимо указать “точку входа”. Сделать это можно в setup.py:

setup(
    ...
    entry_points={
        'redsolutioncms': ['myapp = myapp.redsolution_setup', ],
    },
    ...
)

RedsolutionCMS ищет точку входа с названием redsolutioncms. В примере выше CMS попытается импортировать myapp.redsolution_setup.. Если в этом модуле есть импортируемый модуль urls, то CMS будет считать модуль настраиваемым, предложит ссылку для настройки на третьем шаге.

Структура настрйощика

Теперь о самом модуле myapp.redsolution_setup. Мы рассмотрим пример с настройщиком для приложения обратной связи redsolutioncms.django-simple-feedback.

Мы сделаем сразу интерфейс для настройки.

Модели

Для моделей есть базовый класс redsolutioncms.models.BaseSettings и менеджер redsolutioncms.models.BaseSettingsManager. Метод get_settings у менеджера возвращает или создает настройки по умолчанию. Из модуля настройки можно импортировать настройки самой RedsolutionCMS

from redsolutioncms.models import CMSSettings

cms_settings = CMSSettings.objects.get_settings()

Для создания модели настройщика, определитесь, какие настройки вы хотите видеть. Допустим, мы решили добавить одну настройку для модуля обратной связи - это флаг, определяющий отображение отдельной страницы с обратной связью. В модуле это переменная DIRECT_TO_TEMPLATE в settings.py.

Текст модели и её менеджера для настройщика модуля будет выглядеть примерно так

# -*- coding: utf-8 -*-
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()

            # а здесь можно интегрироваться с другими настройщиками
            # например, добавить SEO поля для новостей можно так:
            # django-seo integration
            # cms_settings = CMSSettings.objects.get_settings()
            #if 'redsolutioncms.django-seo' in cms_settings.installed_packages:
            #    try:
            #        from seo.redsolution_setup.models import SeoSettings
            #    except ImportError:
            #        pass
            #else:
            #    seo_settings = SeoSettings.objects.get_settings()
            #    seo_settings.models.get_or_create(model='easy_news.models.News')

            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, в котором создается на лету объект админики и пользователю показывают страницу изменения свойств объекта

# -*- coding: utf-8 -*-
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, он обязательный. Сборка проходит в три этапа.

1. Premake На этом этапе подготавливаются начальные значения переменных, создаются папки, RedsolutionCMS создает базовый шаблон и т.п. В premake располагайте те функции, которые дожлны отработать прежде всех остальных.

2. Make Основной этап сборки. RedsolutionCMS создает проект, рендерит шаблоны, затем остальные модули делают то же самое. Некоторые из модулей копируют медиа-файлы в папку проекта на этой стадии.

3. Postmake Заключительная стадия. Она введена для тех операций, которые предполагают почти собранный проект. На этой стадии RedsolutionCMS генерирует sitemaps для модулей, обрабатывает начлаьные данные приложений и т.п. Некоторые приложения копируют медиа-файлы и на этой стадии.

Для записи настроек в файлы проекта созданы методы объектов CMSSettings.

cms_settings.render_to(file_name, template_name, dictionary=None, mode='a+')

Генерирует контент в заданный файл по указанному шаблону

Parameters:
  • 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,
})
cms_settings.copy_file(dst, src, mode='wb')

Копирует или дописывает контент указанного файла

Parameters:
  • 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',
)
cms_settings.copy_dir(dst, src, merge=True)

Функция для копирования каталогов

Parameters:
  • 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')

#       WARNING! Silently delete media dirs
        try:
            shutil.rmtree(join(project_media_dir, 'feedback'))
#            no such directory
        except OSError:
            pass

        if 'redsolutioncms.django-server-config' not in cms_settings.installed_packages:
#           copy files to media directory
            shutil.copytree(
                join(feedback_media_dir, 'feedback'),
                join(project_media_dir, 'feedback'),
            )

make = Make()