Интеграция модуля с RedsolutionCMS ================================== Итак, вы решили добавить свой модуль в Redsolution CMS. Первое время мы ещё будем работать над API установщика, поэтому следите за изменениями в документации. Организация ----------- Для того, чтобы ваш модуль распознавался как подключаемый в Redsolution CMS надо, чтобы его название содержало ``redsolutioncms``. Если вы не хотите публиковать модуль с таким названием, то можете опубликовать установщик отдельно, прописав в его зависимостях свой модуль. В списке все модули поделены на категории. Категорию для своего модуля можно задать, если в его документации встретится ссылка вида: :: http://www.redsolutioncms.org/classifiers/ Пока мы задали лишь несколько категорий: * ``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``. Текст модели и её менеджера для настройщика модуля будет выглядеть примерно так .. code-block:: python # -*- 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`` .. code-block:: python from django.contrib import admin from feedback.redsolution_setup.models import FeedbackSettings from redsolutioncms.admin import CMSBaseAdmin class FeedbackSettingsAdmin(CMSBaseAdmin): model = FeedbackSettings Да, такой короткий :) И текст ``urls.py``, в котором создается на лету объект админики и пользователю показывают страницу изменения свойств объекта .. code-block:: python # -*- 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. .. function:: cms_settings.render_to(file_name, template_name, dictionary=None, mode='a+') Генерирует контент в заданный файл по указанному шаблону :param file_name: имя файла, в который ведется запись :param template_name: путь к шаблону :param dictionary: словарь для отрисовки шаблона :param 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, }) .. function:: cms_settings.copy_file(dst, src, mode='wb') Копирует или дописывает контент указанного файла :param dst: файл куда писать (здесь не работает трюк со списком) :param src: исходный файл :param 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', ) .. function:: cms_settings.copy_dir(dst, src, merge=True) Функция для копирования каталогов :param dst: каталог куда записывать :param src: исходный каталог :param 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-класса для одного из последних написанных установщиков: .. code-block:: python 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()