Продолжая тему препарирования Liferay Portal и модификации его стандартных портлетов под собственные нужды, расскажу о том, как добавить продвинутые возможности форматирования в описания товаров интегрированного онлайн-магазина портала (Shopping Portlet), который используется у нас в качестве основы для каталога курсов.
	Сам по себе этот портлет достаточно "наворочен" (например, есть возможность подключения PayPal-аккаунта для перевода денег покупателей, поддержка разнообразных стандартов банковских карт, расчёт стоимости доставки и т.д.), но для наших задач он не совсем подходит, в частности, потому, что описание товара выводится как plain text без всякого форматирования, что плохо для описаний курсов. Попробуем исправить этот недостаток.
	Интерфейс интегрированных в Liferay портлетов формируется посредством jsp-страниц, расположенных в директории $SRC_ROOT/portal-web/docroot/html/portlet (или html/portlet для war-архива). Внутри этой директории все страницы "раскиданы" по каталогам соответствующих портлетов. Интересующие нас страницы находятся в подкаталоге shopping.
	Для начала, уберём вывод описания товара в списке товаров категории - это описание и так там смотрится не очень, а когда в нём появятся ещё и bb-теги, всё будет выглядеть ещё печальнее :). Ищем на странице categories.jspf такой код (строки 285-308):
...
sb = new StringBuilder();
sb.append(item.getName());
if (Validator.isNotNull(item.getDescription())) {
    sb.append("<br />");
    sb.append(item.getDescription());
}
Properties props = new OrderedProperties();
PropertiesUtil.load(props, item.getProperties());
Enumeration enu = props.propertyNames();
while (enu.hasMoreElements()) {
    String propsKey = (String)enu.nextElement();
    String propsValue = props.getProperty(propsKey, StringPool.BLANK);
    sb.append("<br />");
    sb.append(propsKey);
    sb.append(": ");
    sb.append(propsValue);
}
...
	Вырезаем отсюда всё, связанное с описанием товара (строки листинга 4-7), и, опционально, его свойства (строки 8-18). В итоге из атрибутов товара у нас в этой ячейке таблицы-списка останется только его наименование.
	Теперь перейдём собственно к добавлению bb-тегов. Для этого нам нужно немного пофиксить вывод описания товара на странице view_item.jsp. Открываем этот файл и ищем там код, выводящий описание (строки 113-117):
<c:if test="<%= Validator.isNotNull(item.getDescription()) %>">
    <br />
    <%= item.getDescription() %>
</c:if>
	Дописываем к выводу описания жутковатый replace:
<c:if test="<%= Validator.isNotNull(item.getDescription()) %>">
    <br />
    <%= item.getDescription()
            .replace("\n[*]", "[*]")
            .replace("\n[ul]", "[ul]")
            .replace("[/ul]\n", "[/ul]")
            .replace("\n", "<br/>")
            .replace("[s]", "<s>")
            .replace("[/s]", "</s>")
            .replace("[u]", "<u>")
            .replace("[/u]", "</u>")
            .replace("[i]", "<i>")
            .replace("[/i]", "</i>")
            .replace("[b]", "<b>")
            .replace("[/b]", "</b>")
            .replace("[ul]", "<ul>")
            .replace("[/ul]", "</ul>")
            .replace("[*]","<li>")
            .replace("[/url]","</a>")
            .replaceAll("(.*)\\[url=(.*)\\](.*)", "$1<a href=$2>$3") %>
</c:if>
	Что он делает? Он заменяет перечисленные ниже bb-теги на соответствующие теги html и подставляет тег <br /> вместо переносов строк. Строки листинга 4 и 5 предотвращают "расползание" вёрстки в списках (т.к. там не надо генерить <br /> вместо переносов строк между элементами). Приведённый выше пример реализует поддержку следующего набора bb-тегов:
	
		
			
				[b]Полужирный текст[/b] | 
			
				Полужирный текст | 
		
		
			
				[i]Курсив[/i] | 
			
				Курсив | 
		
		
			
				[u]Подчёркнутый текст[/u] | 
			
				Подчёркнутый текст | 
		
		
			
				[s]Зачёркнутый текст[/s] | 
			
				Зачёркнутый текст | 
		
		
			| 
				 
					Список элементов: 
					[ul] 
					[*]item1 
					 [*]item2 
					[*]item3 
					[/ul] 
			 | 
			
				Список элементов:
				
			 | 
		
		
			
				[url=www.tune-it.ru]Гиперссылка[/url] | 
			
				Гиперссылка | 
		
	
	Собственно, всё! Теперь можно заполнить тестовое описание товара в каталоге и убедиться, что наша цель достигнута:
	