2009-05-05

Интеграция виртуальных сайтов в Zope3

Андрей Орлов  2009-05-05 00:25

Статья описывает способ настройки Zope3, позволяющий создать несколько виртуальных сайтов и интегрировать их между собой в единое целое так, чтобы они имели общие компоненты, обеспечивающие возможность поиска, создания новостных лент или общее меню разделов. Рассказывается о принципе работы адаптера пространства имен vh, адаптера IAbsoluteURL и способе их настройки, позволяющим вычислять URL объекта, принадлежащего другому виртуальному сайту.

Интеграция виртуальных сайтов в Zope3

Интеграция виртуальных сайтов в Zope3

Виртуальные сайты в Zope3 создаются при помощи адаптера пространства имен vh, который задает базовый URL и корневой объект виртуального сайта. Обычно, виртуальные сайты работают независимо друг от друга и представляют только объекты, которые содержаться в их подразделах. Но иногда оказывается целесообразным включать в представления объекты других виртуальных сайтов и ссылки на них. Такая возможность необходима для реализации общего поиска, новостных лент, меню разделов и сайтов, а также много других сервисов, позволяющих интегрировать сайты между собой. Основная проблема, с которой приходится при этом столкнуться, это вычисление ссылок на объекты (посредством адаптера IAbsoluteURL) и обработка обращения к ссылкам на ресурсы (задаваемые адаптером пространства имен resource.

Вычисление URL при помощи адаптации к интерфейсу IAbsoluteURL, производится рекурсивным подъемом по дереву объектов до корня виртуального сайта, где подъем завершается подстановкой базового URL. Если объект находится в подразделе другого виртуального сайта, то его корень никогда не достигается и приемлемого результата получить не удается.

Благодаря возможностям компонентной модели Zope3, эта проблема легко решается введением специального интерфейса, позволяющего получить базовый URL объекта, который может сохранятся, например, в аннотации. Для этого интерфейса определяется специальный адаптер к интерфейсу IAbsoluteURL, который возвращает URL, полученный из аннотации.

Другая проблема - ресурсы регистрируемые директивой resource - которые доступны только при обращении к объекту с интерфейсом ISite, что не всегда удобно. Это также легко решается определением контейнера с ресурсами для другого интерфейса.

Настройка виртуального хостинга

Пусть в Zope3 создана следующая структура разделов:

/Site
/Site/news
/Site/sites/MySite
/Site/sites/MySite/articles

Здесь:

/Site Корень сайта site.org, и в нем создан SiteManager, содержащий все необходимые утилиты;
/Site/sites/MySite
Корень сайта mysite.org, в котором SiteManager создавать не обязательно.

Оба сайта работают как единое целое: результаты поиска содержат объекты из обоих сайтов, в скине отображаются ссылки на объекты обеих сайтов и есть ряд других общих сервисов.

Для такой структуры разделов виртуальный хостинг настраивается внесением в конфигурацию прокси сервера двух правил переписывания:

RewriteMap      lowercase               int:tolower

RewriteCond    ${lowercase:%{HTTP_HOST}}    site.org
RewriteRule    ^(/.*)$  http://localhost80/++skin++skin/Site/++vh++http:site.org/++$1 [P]

RewriteCond    ${lowercase:%{HTTP_HOST}}    mysite.org
RewriteRule    ^(/.*)$  http://localhost80/++skin++skin/Site/sites/MySite/++vh++http:mysite.org/++$1 [P]

После этого для любого объекта, находящегося в подразделах виртуального сайта, URL можно получить адаптацией к интерфейсу IAbsoluteURL:

<a tal:attributes="href context/@@absolute_url"> ... </a>

Но для объектов вне подразделов виртуального сайта этот URL будет неправильный:

  • Для сайта site.org URL-ы всех объекты, находящиеся в /Site/sites/MySite, будут принадлежать сайту site.org, а не mysite.org;
  • Для сайта mysite.org URL-ы всех объектов, находящихся вне /Site/sites/MySite, будут указывать на несуществующие объекты в mysite.org.

Чтобы URL таких объектов рассчитывался правильно, нужно доработать адаптер к интерфейсу IAbsoluteURL, выполнив следующие шаги:

  1. Создать аннотации для хранения базового адреса;
  2. Создать адаптер объектов с такой аннотацией к интерфейсу IAbsoluteURL;
  3. Связать аннотацию с объектами /Site и /Site/sites/MySite.

Так как mysite.org не имеет SiteManager, то ссылки на ресурсы, вычисляемые для виртуального сайта mysite.org будут неверными, так как контейнер ресурсов будет вызываться для объекта /Site/sites/MySite. Чтобы ссылки заработали, контейнер ресурсов нужно связать с объектом /Site/sites/MySite.

Обратите внимание, что такая настройка не требует вмешательства в исходный код Zope3 и не будет конфликтовать со стандартным поведением Zope3 вне наших виртуальных хостов.

Создание аннотации BaseURL

Подробно о создании и использовании аннотаций можно прочитать в статье про аннотации, здесь же дадим только краткие выдержки из кода.

Аннотация BaseURL нужна, чтобы явно указать URL какого-либо объекта. Интерфейс этой аннотации может быть следующий:

class IBaseURL(Interface) :
    baseurl = URL(title=u"BaseURL")

Аннотация реализуется классом BaseURL:

from persistent import Persistent
class BaseURL(Persistent) :
    baseurl = ""

Аннотация доступна для объектов, предоставляющих интерфейс IBaseURLAvailable, и вызывается через специальный адаптер:

def BaseURLAdapter(context) :
    annotations = IAnnotations(context)

    try :
        bu = annotations['baseurlannotation']
    except KeyError :
        bu = annotations['baseurlannotation'] = BaseURL()

    return bu

Код, регистрирующий редактор и адаптер baseurl:

<adapter
    factory=".baseurladapter.BaseURLAdapter"
    provides=".interfaces.IBaseURL"
    for=".interfaces.IBaseURLAvailable"
    trusted="true"
    />

Упомянутый здесь, интерфейс IBaseURLAvailable используется для указания возможности привести некий объект к интерфейсу IBaseURL. Назначить интерфейс IBaseURLAvailable объектам /Site и /Site/sites/MySite можно, указав его статически директивой class или динамически, воспользовавшись продуктом ng.kit.interfaceswitcher.

Адаптация к IAbsoluteURL

Адаптер к интерфейсу IAbsoluteURL позволяет вычислить URL объекта, выполняя подъем по дереву объектов к корню сайта. Условием завершения рекурсии является достижения корня виртуального сайта, определенного адаптером пространства имен vh, или достижение корня иерархии Zope3-объектов.

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

Таким особым случаем является адаптация объекта, предоставляющего интерфейс IBaseURLAvailable, так как достижение этого объекта является новой точкой окончания рекурсии: URL может быть получен непосредственно из интерфейса IBaseURL, и не требует дальнейших вычислений.

Код нового адаптера к IAbsoluteURL:

class BaseURLRoot(object) :
    def __init__(self, context, request) :
        self.context = context
        self.request = request

    def __str__(self):
        return IBaseURL(self.context).base_url

    __call__ = __str__

    def breadcrumbs(self):
        return ({'name':'', 'url': IBaseURL(self.context).base_url}, )

Регистрация адаптера:

<view
     for=".interfaces.IBaseURLAvailable"
     name="absolute_url"
     factory=".baseurlroot.BaseURLRoot"
     type=".interfaces.Skin"
     permission="zope.Public"
     allowed_interface="zope.traversing.browser.interfaces.IAbsoluteURL"
     />

 <view
     for=".interfaces.IBaseURLAvailable"
     factory="absolute_url"
     type=".interfaces.Skin"
     permission="zope.Public"
     provides="zope.traversing.browser.interfaces.IAbsoluteURL"
     />

Обратите внимание, что адаптер регистрируется для скина Skin, и поэтому будет доступен только при работе производных от него скинов, что исключает возможные конфликты с другим кодом.

Доопределение контейнера ресурсов

Контейнер ресурсов это особый вид c пустым именем, при траверсе которого выполняется обращение к списку зарегистрированных ресурсов. URL такого ресурса выглядит вот так:

http://mysite.org/@@/style.css

По умолчанию контейнер ресурсов связан с интерфейсом ISite, и поэтому в нашем случае этот URL не будет работать, так как /Site/sites/MySite не представляет ISite. Но вид можно связать и с этим объектом, раз он используется в качестве корневого объекта виртуального сайта: в нашем случае меткой корневого объекта является интерфейс IBaseURLAvailable:

<browser:page
    name=""
    for=".interfaces.IBaseURLAvailable"
    class="zope.app.publisher.browser.resources.Resources"
    permission="zope.Public"
    allowed_interface="zope.publisher.interfaces.browser.IBrowserPublisher"
    layer=".interfaces.Skin"
    />

С такой декларацией ссылка на ресурс, указанная в начале раздела, будет работать.

Заключение

Вышеописанный метод позволяет интегрировать между собой в единое целое несколько виртуальных хостов, используя для них общие утилиты поиска и подобных им. Доработка компонентов путем создания дополнительных адаптеров для новых частных случаев является удобным способом настройки продуктов Zope3. Но этот способ доступен только при точном следовании концепции компонентного программирования.

Официальный сайт Zope3 Московская группа изучения реактивного движения The Dream Bot Site noooxml