null

Liferay 7.2: встраиваем портлет в тему портала

Эта статья является обновлением моей предыдущей статьи, посвящённой встраиванию портлетов в тему Liferay 6.2, т.к. в новой версии портала подход и синтаксис немного изменились, и старые способы попросту не работают.

Два способа встроить портлет в тему

Итак, в Liferay 7.2 у вас есть два способа встроить портлет в шаблон темы.

Способ 1. Вставка веб-контента по имени класса и действию.

<@liferay_portlet["runtime"]
  portletProviderAction=portletProviderAction.ADD
  portletProviderClassName="com.liferay.journal.model.JournalArticle"
  defaultPreferences="${defaultPortletPreferences}"
/>

Способ 2. Вставка веб-контента по имени портлета:

<@liferay_portlet["runtime"]
  portletName="com_liferay_journal_content_web_portlet_JournalContentPortlet"
  defaultPreferences="${defaultPortletPreferences}"
/>

В примере специально приведена вставка одного и того же портлета - отображения сетевого контента. То есть результат в обоих случаях будет одинаков. Разберём каждый способ подробнее.

 

Встраивание портлета по имени класса и типу действия

Встроить портлет в тему теперь можно, объявив имя класса (entity type) и тип действия (action). Портал сам найдёт подходящий портлет с наивысшим рангом (service.ranking). В примере ниже встраивается портлет переключения языков.

<@liferay_portlet["runtime"]
  defaultPreferences="${languagePortletPreferences}"
  portletProviderAction=portletProviderAction.VIEW
  portletProviderClassName="com.liferay.portal.kernel.servlet.taglib.ui.LanguageEntry"
/>

Здесь следует пояснить, как именно портал ассоциирует портлеты с типами сущностей. Обратимся к к исходникам портлета SiteNavigationLanguagePortlet.

com.liferay.site.navigation.language.web.internal.portlet.SiteNavigationLanguagePortlet.java
com.liferay.site.navigation.language.web.internal.portlet.SiteNavigationLanguageViewPortletProvider.java

В одном пакете с классом портлета лежит класс - SiteNavigationLanguageViewPortletProvider. Он занимается сопоставлением типа сущности, действия и портлета.

Согласно конвенции об именовании, приведённой на сайте Liferay, этот класс должен называться SiteNavigationLanguageEntryViewPortletProvider (тип сущности + действие + PortletProvider). Но как мы видим на практике, разработчики сделали выбор в пользу коротких имён классов и не соблюдают собственные рекомендации.

Требования к провайдеру (PortletProvider):

1. Класс провайдера должен наследоваться от BasePortletProvider и реализовывать один из интерфейсов: AddPortletProvider, BrowsePortletProvider, EditPortletProvider, ManagePortletProvider, PreviewPortletProvider или ViewPortletProvider, указывающих на тип действия.

2. В аннотации к классу должен быть указан тип сущности, это делается через параметр model.class.name, и сервис (класс интерфейса *PortletProvider), совпадающий с тем, что реализует ваш класс.

@Component(
  immediate = true,
  property = {"model.class.name=CLASS_NAME"},
  service = INTERFACE.class
)

3. Класс должен реализовывать методы для получения уникального идентификатора портлета и страницы - getPortletId() и getPlid(ThemeDisplay). Тема использует этим методы для определения, какой портлет и на какую страницу должен быть вставлен.

Посмотрим как эти требования реализованы в коде SiteNavigationLanguageViewPortletProvider.java

@Component(
    immediate = true,
    property = "model.class.name=com.liferay.portal.kernel.servlet.taglib.ui.LanguageEntry",
    service = ViewPortletProvider.class
)
public class SiteNavigationLanguageViewPortletProvider
    extends BasePortletProvider implements ViewPortletProvider {

    @Override
    public String getPortletName() {
        return SiteNavigationLanguagePortletKeys.SITE_NAVIGATION_LANGUAGE;
    }

    @Override
    public PortletURL getPortletURL(
            HttpServletRequest httpServletRequest, Group group)
        throws PortalException {
        return PortletURLFactoryUtil.create(
            httpServletRequest, getPortletName(), PortletRequest.RENDER_PHASE);
    }

    /**
     * @deprecated As of Judson (7.1.x)
     */
    @Deprecated
    @Override
    protected long getPlid(ThemeDisplay themeDisplay) throws PortalException {
        return themeDisplay.getPlid();
    }
}

В случае, если вы захотите определить свой собственный портлет для переключения языков, то есть портлет с таким же типом сущности и действием (VIEW), вам понадобится убедить Liferay использовать именно ваш портлет вместо дефолтного. Для этого используются ранги. Чем выше ранг, тем приоритетнее портлет, и выбран будет тот, у кого ранг выше. Для этого нужно добавить в аннотацию @Component свойство service.ranking с бОльшим значением, чем у дефолтных портлетов.

property={"service.ranking:Integer=20"}

Однако в документации не сказано, какое значение ранга стоит у портлетов по умолчанию. А в коде портлета мы не видим данного параметра в аннотации @Component.

Встраивание портлета по его имени

Второй способ аналогичен привычному нам из Liferay 6.2, с небольшими изменениями в синтаксисе. Он подходит для любых портлетов, в том числе не являющихся OSGI модулями (и не имеющих аннотации @Component). В общем виде он выглядит так:

<@liferay_portlet["runtime"]
  instanceId="INSTANCE_ID"
  portletName="PORTLET_NAME"
/>

Способ формирования ИД портлета не изменился, он был подробно изложен в предыдущей статье, и я не буду его копировать. Если портлет может иметь инстансы, нужно указать ИД инстанса. В противном случае этот параметр просто не указывается.

Ниже несколько примеров.

<@liferay_portlet["runtime"]
  defaultPreferences="${defaultPortletPreferences}"
  portletName="com_liferay_asset_publisher_web_portlet_AssetPublisherPortlet"
/>

<@liferay_portlet["runtime"]
  portletName="stocksportlet_WAR_stocksportlet"
  defaultPreferences="${defaultPortletPreferences}"
/>

Настройки отображения (defaultPreferences)

Мы рассмотрели два способа встраивания портлета в тему, но ничего не сказали о параметре defaultPreferences. Принцип остался таким же, как в Liferay 6.2, это переменная для передачи настроек портлета. Немного поменялся способ её заполнения, но тут всё должно стать очевидно из примера.

<#assign defaultPortletPreferences = freeMarkerPortletPreferences.getPreferences(
    "portletSetupPortletDecoratorId", "barebone"
)
/>

<#assign languagePortletPreferences = freeMarkerPortletPreferences.getPreferences({
        "displayStyle": "ddmTemplate_LANGUAGE-SHORT-TEXT-FTL",
        "portletSetupPortletDecoratorId": "barebone"
}) />