Добавление статического контента на сайт

В этой статье мы рассмотрим некоторые варианты обработки контента коммерческой платформы YC.

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

Основы

Контент – это, фактически тоже, что и категория в каталоге.  Для отображения содержимого, контент использует тот же системный объект, что и категория – CategoryEntity. Однако есть несколько важных различий, которые дают возможность управлять этими специальными типами категорий.

Первое отличие, это то, что для каждого экземпляра объекта Shopесть корневой каталог контента, который определяется через Category.guid,  который в свою очередь должен соответствовать Shop.code.  Таким образом, каждый отдельный магазин имеет свою собственную иерархию контента. Структура контента очень схожа со структурой каталога категорий, однако в отличие от категории при редактировании контента будет доступна специальная вкладка для управления содержимым страницы. Содержимое контента это текст, который может быть чем угодно: простой текст, HTML, Groovyкод или другие данные, которые могут быть представлены в виде текста.

Содержимое контента хранится в виде списка атрибутов добавляемых пользователем. Названия атрибутов (коды) должны создаваться в соответствии с правилом: CONTENT_BODY_локаль_порядковый номер (например: CONTENT_BODY_ru_1, CONTENT_BODY_ru_2, CONTENT_BODY_en_1, CONTENT_BODY_en_2 и т.д.).  В YCпо умолчанию добавлены два атрибута для русского и английского языков, каждый из которых может хранить по 4000 символов. Примечательно то, что наполняя эти атрибуты текстом в редакторе YUMпользователю нет необходимости делить текст на куски и разбрасывать его по атрибутам CONTENT_BODY_en_1,  CONTENT_BODY_en_2  и т.д. Весь текст может быть добавлен в первый атрибут, после чего сервис управления контентом определит количество созданных атрибутов и разделит текст контента, если это будет необходимо.  Возможно, Вам потребуется создать еще несколько AttributeEntity объектов, если содержание контента больше 8000 символов, что может быть сделано в системе YUM в разделе управления контентом.

Итак, у нас есть представление об управлении контентом, теперь мы рассмотрим, как мы может это использовать.

Так как контент тоже, что и категория мы можем использовать SEO, название, описание и другие атрибуты, доступные для категорий каталога.  В дополнение к этому ContentService.getContentBody(contentId,locale) вернет содержание конкретного контента в виде строки. По умолчанию YCпредполагает, что содержание контента это HTML и ContentCentralView просто делает его частью страницы.  Однако это наиболее упрощенное использование.

Поскольку объекты контента наследуют переменную uitemplate можно реализовывать более сложные типы страниц.

Галерея изображений

Предположим мы бы хотели создать раздел на сайте, который будет отображать галерею изображений. Для этого мы будем использовать объект Content,  а для uitemplateукажем – imagegallery. Теперь наша категория контента будет представлена в виде галереи изображений. Все подкатегории нашей галереи будут представлены в виде отдельных изображений. Теперь, все что необходимо, это создать новый класс для отображения контента, например ImageGalleryCentralViewи добавить его на HomePage, привязав параметр imagegallery к этому классу. Внутри класса ImageGalleryCentralView мы создадим компоненту Wicketlist, где каждый элемент списка будет представлен в виде кликабельной миниатюры изображения для отдельно взятой подкатегории нашего контента, с использованием, например, lightboxили prettyphoto.

Теперь мы можем создавать объекты контента с uitemplateуказанным как – imagegallery, и добавлять подкатегории с изображениями. В итоге,  у нас есть шаблон для контента – галерея изображений, который мы можем использовать, где пожелаем.

Пример кода реализации галереи изображений

Регистрация класса ImageGalleryCentralView на HomePage:

...
/**
* Registered main panel renderers
*/
private static final Map<String, Class<? extends AbstractCentralView>> rendererPanelMap =
new HashMap<String, Class<? extends AbstractCentralView>>() {{
put(CentralViewLabel.SUBCATEGORIES_LIST, SubCategoriesCentralView.class);
put(CentralViewLabel.PRODUCTS_LIST, ProductsCentralView.class);
put(CentralViewLabel.PRODUCT, SkuCentralView.class);
put(CentralViewLabel.SKU, SkuCentralView.class);
put(CentralViewLabel.SEARCH_LIST, ProductsCentralView.class);
put(CentralViewLabel.CONTENT, ContentCentralView.class);
put(CentralViewLabel.CATEGORY, EmptyCentralView.class);
put(CentralViewLabel.DEFAULT, EmptyCentralView.class);
put("imagegallery", ImageGalleryCentralView.class);
}};
...

Содержимое ImageGalleryCentralView:

import org.apache.lucene.search.BooleanQuery;
import org.apache.wicket.AttributeModifier;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.image.ContextImage;
import org.apache.wicket.markup.html.link.BookmarkablePageLink;
import org.apache.wicket.markup.html.link.ExternalLink;
import org.apache.wicket.markup.html.list.ListItem;
import org.apache.wicket.markup.html.list.ListView;
import org.apache.wicket.request.mapper.parameter.PageParameters;
import org.apache.wicket.spring.injection.annot.SpringBean;
import org.yes.cart.constants.ServiceSpringKeys;
import org.yes.cart.domain.entity.Category;
import org.yes.cart.domain.i18n.I18NModel;
import org.yes.cart.service.domain.ContentService;
import org.yes.cart.web.page.component.AbstractCentralView;
import org.yes.cart.web.support.constants.WebParametersKeys;
import org.yes.cart.web.support.entity.decorator.CategoryDecorator;
import org.yes.cart.web.util.WicketUtil;
import org.yes.cart.web.page.HomePage;
import java.util.ArrayList;
import java.util.List;
public class ImageGalleryCentralView extends AbstractCentralView {

// ----------------------------------- MARKUP IDs BEGIN --------------------------- //
private final static String GALLERY_LIST = "galleryList";
private final static String GALLERY_IMAGE_ZOOM = "galleryImageZoom";
private final static String GALLERY_IMAGE = "galleryImage";
private final static String GALLERY_NAME = "galleryName";
private final static String GALLERY_DESC = "galleryDesc";
private final static String MAIN_IMAGE = "mainImage";
private final static String MAIN_NAME = "mainName";
private final static String MAIN_DESC = "mainDesc";
// ----------------------------------- MARKUP IDs BEGIN --------------------------- //
@SpringBean(name = ServiceSpringKeys.CONTENT_SERVICE)
protected ContentService contentService;
/**
* This is an internal constructor used by HomePage class. It disregards the
* categoryId value.
*
* @param id panel id
* @param categoryId ignored
* @param booleanQuery     boolean query.
*/
public GalleryCentralView(String id, long categoryId, BooleanQuery booleanQuery, List<Long> shopCategories) {
super(id, categoryId, booleanQuery);
final String ctxPath = WicketUtil.getHttpServletRequest().getContextPath();
final String selectedLocale = getLocale().getLanguage();
final String lang = getLocale().getLanguage();
final CategoryDecorator categoryDecorator = getDecoratorFacade().decorate(
getCategory(), WicketUtil.getHttpServletRequest().getContextPath(), getI18NSupport());
final Category parentCategory = contentService.getById(categoryDecorator.getParentId());
final CategoryDecorator categoryParentDecorator = getDecoratorFacade().decorate(
parentCategory, WicketUtil.getHttpServletRequest().getContextPath(), getI18NSupport());
final String i18nName = categoryDecorator.getName(lang);
final String i18nParentName = categoryParentDecorator.getName(lang);
add(new Label("heading", i18nName));
add(new Label("breadcrumb", i18nName));
add(new Label("body", categoryDecorator.getDescription(lang)).setEscapeModelStrings(false));
add(new BookmarkablePageLink<HomePage>("homeLink", HomePage.class));
final PageParameters pageParameters = new PageParameters().add(WebParametersKeys.CONTENT_ID, categoryParentDecorator.getCategoryId());
add(new BookmarkablePageLink<HomePage>("categoryLink", HomePage.class, pageParameters).add(
new Label("breadcrumbParent", i18nParentName)));
// All child content - use this one as it is cached and includes availability!
final List<Category> categories = contentService.getChildContent(categoryId);
CategoryDecorator main = null;
final List<CategoryDecorator> cats = new ArrayList<CategoryDecorator>();
if (categories.size() > 1) {
main = getDecoratorFacade().decorate(categories.get(0), ctxPath, getI18NSupport());
for (final Category category : categories.subList(1, categories.size())) {
cats.add(getDecoratorFacade().decorate(category, ctxPath, getI18NSupport()));
}
} else if (categories.size() == 1) {
main = getDecoratorFacade().decorate(categories.get(0), ctxPath, getI18NSupport());
}
final String contentBody;
if (getCategoryId() > 0l) {
contentBody = contentService.getContentBody(main.getCategoryId(), lang);
} else {
contentBody = "";
}
setVisible(cats.size() > 0);
add(
new ListView<CategoryDecorator>(GALLERY_LIST, cats) {
@Override
protected void populateItem(final ListItem<CategoryDecorator> categoryListItem) {
final CategoryDecorator category = categoryListItem.getModelObject();
final I18NModel nameModel = getI18NSupport().getFailoverModel(category.getDisplayName(), "");
categoryListItem.add(
new ExternalLink(GALLERY_IMAGE_ZOOM, category.getDefaultImage("as", "is")).add(
new ContextImage(GALLERY_IMAGE, category.getDefaultImage("344", "231")).add(
new AttributeModifier("alt", nameModel.getValue(selectedLocale)))
).add(new AttributeModifier("data-rel", "prettyPhoto")
).add(new AttributeModifier("class", "thumb")
).add(new AttributeModifier("title", nameModel.getValue(selectedLocale))));
categoryListItem.add(new Label(GALLERY_NAME, nameModel.getValue(selectedLocale)));
categoryListItem.add(new Label(GALLERY_DESC, category.getDescription(selectedLocale)).setEscapeModelStrings(false));
}
}
);
add(new ContextImage(MAIN_IMAGE, main != null ? main.getDefaultImage("160", "208") : "")
.add(new AttributeModifier("alt", main != null ? main.getName(selectedLocale) : "")));
add(new Label(MAIN_NAME, main != null ? main.getName(selectedLocale) : ""));
add(new Label(MAIN_DESC, main != null ? contentBody : "").setEscapeModelStrings(false));
}
}

Страница Wicket HTML:

<html xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.4-strict.dtd">
<body>
<wicket:panel>
<!-- masthead -->
<div id="masthead">
<span class="head">
<wicket:container wicket:id="heading">[Content name]</wicket:container>
</span>
<ul class="breadcrumbs">
<li><a wicket:id="homeLink"><wicket:message key="home">[Home page]</wicket:message></a></li>
<li>/ <a wicket:id="categoryLink"><wicket:container wicket:id="breadcrumbParent">[Content name]</wicket:container></a></li>
<li>/ <wicket:container wicket:id="breadcrumb">[Content name]</wicket:container></li>
</ul>
</div>
<!-- ENDS masthead -->

<!-- page content -->
<div id="gallery-content">
<div class="featured portfolio-list">
<figure wicket:id="galleryList">
<a wicket:id="galleryImageZoom"><img wicket:id="galleryImage"/></a>
<div class="product-grid">
<h5>
<wicket:container wicket:id="galleryName">
[Name]
</wicket:container>
</h5>
<div class="description"><wicket:container wicket:id="galleryDesc">[Description]</wicket:container></div>
</div>
</figure>
</div>
</div>
<!-- ENDS page content -->
<!-- sidebar -->
<aside id="gallery-sidebar">
<div class="block">
<h4><wicket:container wicket:id="mainName">[Name]</wicket:container></h4>
<ul>
<li>
<img wicket:id="mainImage"/>
</li>
</ul>
</div>
<div class="block justify">
<wicket:container wicket:id="mainDesc">[Description]</wicket:container>
</div>
</aside>
<div class="clearfix"></div>
<!-- ENDS sidebar -->
</wicket:panel>
</body>
</html>

Динамические данные

Предположим, мы хотим использовать некоторые динамические данные. Мы можем вставить Groovyкод в содержимое нашего контента. Создать еще одну страницу отображения контента, которая использует GStringTemplateEngine для разбора содержимого контента как groovyкод, и рендерить HTMLкоторый будет содержать динамические данные, такие как товары, категории и другие.

Пример разбора Groovy кода:

...
final GStringTemplateEngine templateEngine = new GStringTemplateEngine(classLoader);
...
final String contentBody = contentService.getContentBody(contentId,locale);
// Map contain all object that are rendered within template
final Map<String, Object> model = ... ;
...
final Writable writable = templateEngine.createTemplate(contentBody).make(model);
final StringWriter stringWriter = new StringWriter();
writable.writeTo(stringWriter);
stringWriter.close();
final String htmlToRender = stringWriter.toString();
...

Другие решения

Создание новых страниц отображения контента довольно простое и позволяет создавать конкретные шаблоны. Как было показано на примере, если мы знаем GUIDконтента,  мы можем получить его содержимое с помощью ContentService.getContentBody(). Также возможно создание специализированного контента с конкретно определенными GUID для использования в шаблонах страниц (например «Контакты»). Эти элементы контента могут быть использованы в шаблонах как коллаж, что позволит изменять отдельные части страницы, либо для добавления дополнительных разделов к существующим страницам (Поиск, Страницы категорий и товаров).

Заключение

YC предлагает Вам простой подход, где Вы принимаете решения. Расширяйте функциональность платформы по мере необходимости и затрачивайте минимум времени на разработку. После того как все необходимые шаблоны будут созданы, Вы можете использовать их при работе с контентом в менеджере YUM, просто указав параметр для uitemplate для нужных контент категорий, что не требует никакой дополнительно разработки.

Надеемся, эта статься дала Вам общее представление, о том, как управлять контентом в Вашей YCплатформе.

 

Реклама

Ссылка на несуществующую страничку или как правильно обработать 404 ошибку

404

    Давайте исходить из простой предпосылки — Покупатель всегда прав и не надо пугать покупателя страшными страничками 404, если что-то пошло не так или он набрал несуществующую ссылку (*). Есть несколько выходов из этой ситуации.

Перенаправить на домашнюю страничку

    Направление на домашнюю страничку самый популярный способ решения 404 проблемы. И надо сказать, что это лучше чем ничего.

Перенаправить на каталог или продуктовую страничку

    Можете это сделать автоматически ? Скорее всего — нет, ибо у Вас не инструмента для анализа URL по которому пришел пользователь и вычленения полезной информации — названия каталога, продукта , кода продукта и т.п. А в Yes Cart есть поисковый движок, которому можно «скормить» URL, который вызвал 404 ошибку и проанализировать, может можно извлечь каталог или продукт из нее и перенаправить туда покупателя.

Перенаправить на страничку с распродажами или акционными товарам

    Это сладкое слово «халява» еще никто не отменял. Перенаправить на страничку с распродажами или акционными товарам.

Компенсируйте неудобство

    Выдайте промо код, который дает незначительную скидку и покупатель вернется ибо есть стимул.

А что если продукт продан ?

    И на этот вопрос в Yes Cart есть ответ — показать страничку продукта, но не давать добавить в корзину. Предложить вариант — «Уведомить меня, когда продукт станет доступным».

    В дополнение показать покупателю сходные продукты и/или ассоциированные продукты (перекрестная продажа, аксессуары, и т.п)

Брошенная корзина: почему покупатели уходят?

Корзина

Брошенная корзина

    Вопросов на повестке дня — два:

  • Почему покупатели бросают корзину и не совершают заказ?
  • Как уменьшить % брошенных корзин?

1. Покупатель не готов совершить покупку.

    По исследованиям специалистов из seewhy.com 16% мужчин и 26% женщин просто не готовы совершить покупку в текущий момент. Поэтому:

2. Разрешите покупку без регистрации.

    Согласно исследованиям Forrester Research 23% покупателей не завершили процесс покупки, поскольку от них требовали зарегистрироваться на сайте. Если предварительная регистрация не является обязательной для Вашего бизнеса (торговля оружием, контентом для взрослых и т.п.), то покупка без предварительной регистрации должна существовать на Вашем сайте.

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

3. Проведите тестирование процесса создания заказа.

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

4. Заслужите доверие.

    Обязательно расположите на своем сайте все имеющиеся признаки обеспечения безопасности покупки:

  • SSL
  • PCI DSS
  • другие атрибуты безопасной покупки.
  • etc

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

    Не забудьте расположить сертификаты и подсказки по безопасности в тех местах сайта, где покупатель чувствует некоторую неуверенность, например при вводе cvv кода.

5. Проверяйте пользовательский ввод сразу.

    Проверяйте, ту информацию, которую вводить покупатель сразу, не дожидаясь отправки формы. Это
позволит покупателю видеть свои ошибки и сразу их корректировать, не дожидаясь отправки формы. По ОБС(*) фактам, такие простые усовершенствования дают увеличение конверсии на 22%.

6. Объясните покупателю, что такое CVV.

    В случае оплаты платежной картой, поясните на примере, как покупатель может найти cvv код на карте. В обчных магазинах cvv код не требуется, поэтому покупатель не знает где код расположен.

7. Сделайте большую кнопку оплаты.

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

8. Покупатель всегда должен иметь возможность обратится в службу поддержки.

    Телефоны, скайп, чат, icq — все эти каналы связи должны быть доступны покупателю, для обращения в службу поддержки. Необходимо ожидать следующие вопросы

  • Наявность товара ?
  • Характеристики товара ?
  • Как оплатить ?
  • Доставка ?

9. Скорость работы.

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

10. Выясните email покупателя как можно ранее.

    Email жизненно необходим, если Вы желаете выслать письмо в случае брошенной корзины. Около 11% покупателей обычно реагируют на такие письма.