Множество команд сталкиваются с решением вопроса по управлению контентом на различных платформах — от веб-сайтов до мобильных приложений и интернет-магазинов. При этом, как правило, отсутствует единый инструмент, позволяющий эффективно создавать, изменять, хранить и передавать контент для отображения на всех платформах.
Кто-то приходит к самостоятельной разработке инструмента по управлению контентом с нуля. Другие смотрят в сторону готовых CMS решений (content management system/систем управления контентом), которые зачастую позволяют сэкономить время и затраты.
Меня зовут Лена. Мы в команде Enablement Platform провели пилот по сравнению нескольких популярных решений для CMS, накопленный материал показался интересным и полезным для веб-разработчиков, в результате появилась эта статья.
Примечание. Миссия Платформы
Соединять клиентов, индустрии и партнеров, обеспечивая коммерциализацию размещенных на платформе продуктов и предоставляя клиентам лучший опыт взаимодействия с Экосистемой МТС.
Поделюсь с какими проблемами мы столкнулись, какой подход выбрали для управления контентом и доставке данных до витрин. Если бы такой материал мы встретили раньше, он бы сэкономил нам много времени.
Оглавление
1. Вступление
2. Требования и критерии выбора
3. Классы CMS
4. Ситуация на рынке Headless CMS
5. Сравнение Headless CMS
6. UI систем и примеры моделирования
7. Заключение
1. Вступление
Решения класса CMS существенно различаются зрелостью и функциональностью. То, что отлично подходит для простого персонального блога или небольшого сайта, может не справиться с потребностями сложных корпоративных витрин. Для снятия этих ограничений возможностей конфигурирования недостаточно и может потребоваться существенная доработка CMS. Например, ограничения в моделировании данных, подходы к управлению и отображению различных типов контента, механизмы к ограничения доступности контента и т. д. Большинство CMS ориентированы на относительно простую модель данных с ограниченным типом связей между сущностями.
В МТС есть множество различных пользовательских витрин, которые позволяют клиентам узнать о продуктах компании:
- сайте;
- приложении;
- интернет-магазине;
- лендингах и т. п. (например, premium.mts.ru, kion.ru, music.mts.ru).
И на текущий момент в экосистеме МТС применяются различные инструменты для управления контентом. Возникла идея — проанализировать текущий рынок CMS и провести «пилотирование» выбранных инструментов. По результатам пилота оценить возможность оптимизации и унификации подходов к управлению контентом и передаче данных витринам.
Примечание
Ранее проведенные внутренние обзоры коллег, которые мы исследовали в рамках этой задачи, уже устарели. Зачастую функциональность, отмеченная как отсутствующая в продукте, была со временем реализована. Встречались в обзорах продукты, которые заброшены и больше активно не развиваются.
Даже официальные обзоры на сайтах CMS, где решения сравнивают себя с конкурентами, содержат ошибки и неточности.
Поэтому сделать выбор только на основании описаний или обзоров, статей (в том числе на Хабре) и тематических рейтингов не удалось.
Даже официальные обзоры на сайтах CMS, где решения сравнивают себя с конкурентами, содержат ошибки и неточности.
Поэтому сделать выбор только на основании описаний или обзоров, статей (в том числе на Хабре) и тематических рейтингов не удалось.
Задача вносила корректировку к требованиям. В первую очередь нам были интересны решения, которые позволяют работать не с простым контентом (как, например, статьи на сайте), а со сложной иерархичной структурой данных, связанными сущностями, вложенными компонентами и т. д.
2. Требования и критерии выбора
При выборе CMS учитывались общие требования к функциональности, возможности кастомизации и специфические потребности команды, обусловленные используемым ИТ-ландшафтом.
Основные критерии выбора:
- Open Source решение и лицензии без дополнительных ограничений, в том числе на использование в коммерческих целях — возможность кастомизации под потребности компании, использование в составе других продуктов;
- Headless CMS — ключевое внимание на управление данными, уровень представления реализуется внешними витринами и зависит от их специфики и потребностей;
- максимально близкое команде продукта по стеку — backend предпочтительно C#, СУБД MongoDB и PostgreSQL;
- поддержка GraphQL API — у нас активно внедряется данный подход межсистемного взаимодействия, поддержка GraphQL API упростит интеграцию решения в общий ИТ-ландшафт;
- возможность создавать свою модель данных — постоянно запускаются новые продукты, у каждой группы продуктов свои особенности и атрибутивный состав, поэтому важно иметь гибкие возможности по моделированию;
ПримечаниеПоддержка иерархичной модели данных, вложенных элементов, в одной родительской сущности можно создать несколько дочерних.
- поддержка SSO (Single sign-on) — данный механизм является стандартом в компании, позволяет пользователю не тратить время на авторизацию в каждой системе;
- работа с мультимедийным контентом: управление изображениями в различных разрешениях, видео контентом, бинарными документами и т. п.;
- наличие локализации — для перевода контента на различные языки;
- наличие версионирования контента — для отслеживания истории изменений и возможности отката к прошлым версиям;
- возможность управления workflow — управлением возможными статусами у сущностей, включая поддержку отложенной публикации контента;
- удобство использования;
- импорт контента из внешних источников — для упрощения процесса миграции на новую систему;
- производительность ~2000 RPS;
- информирование внешних систем об изменении данных;
- возможность генерации API по созданной модели — для сокращения трудозатрат на разработку;
- ролевая модель пользователей и API — для разграничения прав доступа к управлению контентом;
- мультитенантность — возможность управления контентом в одной CMS для нескольких команд, где у каждой из них свое пространство.
3. Классы CMS
Традиционные CMS
Позволяют как управлять данными, так и отвечают за их представление, поддерживают темы и т. д. На базе таких решений вы можете создать полностью готовый сайт с дизайном, фронтом и данными. Легко и просто наполнять его.
Типичные представители: WordPress, Joomla, Drupal.
Headless CMS
Предоставляет UI для управления моделью данных, UI для управлениям самими данными и основное — API реализующее набор CRUD операций на данными.
Именно через API происходит потребление этих данных внешними витринами и каналами.
Выделяют следующие типы решений:
- API Driven — используют СУБД для хранения данных (может использовать объектное хранилище для бинарных файлов);
- Git-based — используют Git репозиторий для хранения контента, как правило подходят для для простых задач, с небольшой частотой изменений.
Некоторые решения не предоставляет UI для управления моделями, модель описывается программным кодом.
4. Ситуация на рынке Headless CMS
Нас интересовали именно Headless-решения, потому что они позволяют моделировать контент с целью дальнейшего отображения на различных платформах. В нашем случае это вышеупомянутые витрины данных (сайт, мобильное приложение, интернет-магазин, лендинги и т.п.)
Бэкенд при данном подходе существует отдельно от фронта, что дает возможность использовать один бэкенд для хранения и передачи контента для отображения на всех вышеуказанных витринах данных. Это позволяет централизовать ведение данных по продуктам, а задачу их отображения и верстки оставить на стороне витрины. Потенциально мы даем необходимую гибкость продуктовым командам.
На рынке сейчас представлено большое количество Headless CMS. Когда мы делали выбор, какие из систем хотим «пощупать» (установить и попробовать смоделировать данные на примере нескольких продуктов), в первую очередь обращали внимание на популярность системы.
Поэтому первой начали рассматривать Strapi, которая является лидером на рынке и занимает первые места в многочисленных обзорах. В процессе столкнулись с серьезными багами и ограничениями, необходимостью глобальной доработки (включая бэкенд и модель СУБД). Несмотря на то, что в нашей команде есть JS-разработчики, трудно было переключить их от бизнес-задач к полноценной доработке бэка системы для продолжения пилота.
Мы решили посмотреть альтернативные Headless CMS на стеке .NET C#. При этом уже понимали, какие возможности нужны в первую очередь и на чем нужно сфокусироваться:
- поддержка использования компонент данных (набор элементов разных типов);
- массивы компонент;
- различные типы связи;
- набор UI контроллов для удобного наполнения данными.
Под наши запросы подошли Squidex и Orchard Core.
В процессе рассматривались и тестировались другие системы, но детально не анализировались, так как не удовлетворяли ключевым критериям. Как правило, были ограничения в моделировании данных.
Отпал ряд решений, которые не предоставляют UI для управления моделью, и модель задается прямо в коде.
В обзоре ниже представлены ключевые характеристики и особенности выбранных Headless CMS.
5. Сравнение Headless CMS
Squidex
Сайт | squidex.io |
Сообщество/поддержка | 2k stars Watchers 100 Forks 402 Contributors 60
Примечание
|
Open source решение | Да. |
Лицензия и ее классификация по SAM OSS лицензии | SAM 2.0 (mts.ru) | MIT license. |
Версия на момент проведения анализа | 7.7.0 (01 Jul 2023) |
Репозиторий | github.com/Squidex/squidex |
Стек (frontend) | TypeScript / Angular / webpack / Node.js (development only — используется для сборки фронта)
Примечание
NodeJS нужна для разработки, если вы планируете дорабатывать UI админки на Angular. Все скрипты UI пакуются через webpack, а он использует NodeJS.
|
Стек (backend) | C# / .NET 7 / ASP.NET Core |
СУБД | MongoDB |
Наличие GraphQL API | Есть, можно настроить скрытие определенных полей в API. Есть возможность фильтрации данных, фильтр передается строкой, формат Odata. История запросов GraphQL пользователя (playground) хранится в локальном storage браузера. Нельзя фильтровать по атрибутам, значение которых подтягивается из справочника (тип Reference) за исключением поля Id. |
Удобство создания и управления моделью данных, включая поддержку вложенности элементов | Можно несколько раз использовать 1 компонент. Можно вкладывать компонент в компонент или на поле настраивать выбор из нескольких разных компонентов. Очень удобно выбирать значения из справочников, потому что не только можно выбрать существующую запись, но и сразу создать недостающую: Можно неограниченное количество раз добавлять компонент или выбрать несколько значений из справочника. Удобство настройки представления контента для его заполнения (можно задать наименования полей, подсказки при вводе и т. п.). При создании сущности сразу можно посмотреть JSON-схему и внести правки в структуру данных через нее: |
Поддержка SSO-авторизации | |
Особенности работы с мультимедийным контентом:
|
1. Изображение загружается с исходными параметрами, есть возможность тегировать изображения. В API есть возможность передать точные размеры и тип файла. 2. Да. |
Наличие локализации | Да. Можно управлять обязательностью полей для страны. Если у языка не поставить is_Optional, если поле обязательное, его нужно будет внести обязательно для данной локализации. Можно копировать значения контента из одного языка в другие. |
Поддержка версионирования. Рассматривали:
|
Сохраняется история изменений, можно сравнить с другими версиями и откатиться к нужной: По умолчанию каждое редактирование формы приводит к созданию новой версии, а мы хотим осознанно управлять этим процессом (не всегда редактирование должно приводить к созданию новой версии), нужна кастомизация. |
Возможность управления flow (статусы у сущностей).
|
Да, можно настраивать создавать новые статусы и условия перехода в них, а также определять последовательность шагов. Есть отложенная публикация, очень удобный интерфейс c графиком запланированных публикаций Scheduled Publishing — Squidex. Можно одновременно сделать несколько версий одной сущности, например, 1 черновик, другая опубликована. |
Удобство использования | Самый удобный UI из трех, все интуитивно понятно. Можно настроить правила валидации для каждого поля. Можно управлять представлением при работе с контентом (какие поля отображать и т. д.). При внесении контента можно развернуть и посмотреть значения внутри нескольких компонентов (в Strapi можно было раскрыть только 1 за раз). В отличии от Strapi корректно работает функция клонирования записей. Можно смотреть сколько запланированных публикаций контента в календаре. В отличии от Strapi можно сразу опубликовать запись, а можно сначала сохранить как черновик (там обязательно сначала черновик, потом публикация). При редактировании схемы данных надо нажимать кнопку сохранения после изменения данных в каждом блоке (настройки каждого поля) — иначе изменения не сохранятся. На форме просмотра/редактирования записи нет кнопки, чтобы создать новую, нужно возвращаться к списку (неудобно при заполнении справочников), но так реализовано и в Strapi. Если элементы компонента свернуты, то совсем непонятно, что за сущность. |
Особенности | В структуре JSON есть излишняя вложенность (с тем же столкнулись и в Orchard):
В GraphQL также:
Причина — реализация партиционирования (partitioning, используется для локализации).
Примечание
The Content API usually returns one object per schema field where the keys are the languages (or iv for non-localized) fields and the values are the actual field value.
Key is iv, which stands for «invariant». docs.squidex.io/02-documentation/concepts/localization#how-to-use-the-api Для REST можно использовать заголовок X-Flatten Header, которые сделает плоскими данным с единственным значением. Для GraphQL можно использовать объект flatData:
|
Импорт контента из внешних источников | При создании новой схемы в UI есть возможность импорта JSON-схемы,
пример
sq content export myschema --format=JSON sq content import myschema myschema_2020-05-20-09-14-34.json --format=JSON Импорт/экспорт данных с помощью CLI (подробнее тут: docs.squidex.io/02-documentation/developer-guides/automation-tools): |
Хранение бинарных файлов (assets)/мультимедиа (S3 compatible) или интеграция с такой системой | AssetStore:
|
Нефункциональные требования.
|
Можно написать свой плагин для кэша. Есть механизмы кэширования данных. |
Расширяемость (дорабатываемость) системы.
|
Предусмотрена расширяемость с помощью плагинов. |
Публикация событий об изменении контента | Да. |
Возможность генерации API по созданной модели | Генерация OpenAPI спецификации по модели и базовым методам. Встроенный GraphQL браузер и документация по API. |
Ролевая модель пользователей и API | Есть различные роли и можно добавлять свои: |
Мультитенантность | Можно создавать пространства для различных команд: |
Strapi CMS
Сайт | strapi.io |
Сообщество/поддержка | 54.9k stars Watchers: 661 Forks: 6.7k Contributors 895
Примечание
Данные с github.com/strapi/strapi
|
Open source решение | Да. |
Лицензия и ее классификация по SAM OSS лицензии | SAM 2.0 (mts.ru) | MIT Expat для Публичной версии. |
Версия на момент проведения анализа | 4.11.7 (19 Jul 2023) + плагины — установлена 21.07.2023 |
Репозиторий | github.com/strapi/strapi |
Стек (frontend) | JS/TypeScript Angular |
Стек (backend) | JS/TypeScript, Nodejs |
СУБД | PostgreSQL MySQL/MariaDB SQLite |
Наличие GraphQL API | Поддержка GraphQL с помощью плагина GraphQL | Strapi Market. История запросов GraphQL пользователя (playground) хранится в локальном storage браузера. |
Удобство создания и управления моделью данных, включая поддержку вложенности элементов | В рамках коллекции можно использовать несколько раз 1 компонент. Можно вкладывать компонент в компонент. Можно настроить дефолтные значения для компонента, который встраивается в коллекцию (в нашем случае применимо, например, для стоимости звонков по миру, которая почти везде одинакова). Удобно настраивается связь между сущностями, можно управлять типом связи 1 к 1, 1 к* и т. п.). Допускается использование при создании атрибута в его наименовании служебного имени ID, но при этом возникает ошибка, которую нельзя исправить без правок на уровне базы данных (надо вновь добавленное поле «ID» переименовать). Нельзя сконфигурировать порядок полей при связи таблиц, выбор из списка значений будет по первому полю. Если ошибиться с порядком полей при создании объекта, исправить можно можно только через пересоздание объекта. |
Поддержка SSO-авторизации | Есть плагин. |
Особенности работы с мультимедийным контентом:
|
1. Изображения сохраняются сразу в трех размерах.
Примечание
Есть несколько плагинов, которые можно применить, если дефолтные настройки нас не устраивают:
2. Да. |
Наличие локализации | Да. |
Поддержка версионирования. Рассматривали:
|
Нет. Можно было бы создавать новые версии, используя функцию дублирования, но с учетом ее некорректной работы (не все поля дублировались, только простые, без компонентов) это проблематично. Для реализации версионирования нужны доработки. Есть плагин New Community Plugin: Content Versioning (strapi.io), который позволяет иметь одновременно несколько версий (черновик и опубликованная, несколько черновиков + бонусом логирование изменений) — его не тестировали. |
Возможность управления flow (статусы у сущностей).
|
Для ввода доп.статусов можно использовать плагин Review Workflows. Также по ссылке есть пример, как использовать внешний автоматизатор n8n. Отложенную публикацию можно настроить Scheduled Publication — Strapi Developer Documentation. |
Удобство использования | Не очень дружелюбный интерфейс бесплатной версии (не критично):
|
Особенности | 1. Есть два режима запуска:
2. Слабая защита от «дурака» в UI можно изменить модель так, что система перестанет стартовать, и потребуется исправлять ошибки модели в исходных файлах проекта. Разработчикам (если они увидят нашу статью) было бы здорово обратить внимание на валидацию данных на UI (ограничение на количество символов в названиях объектов, запрет создания поля с именем «ID» и т. п.). |
Импорт контента из внешних источников | Есть плагин. |
Хранение бинарных файлов (assets)/мультимедиа (S3 compatible) или интеграция с такой системой | 1. В виде файлов в public/uploads на веб-сервере. 2. Коннекторы к объектным облачным хранилищам. |
Нефункциональные требования.
|
Есть плагин для кэширования данных. |
Расширяемость (дорабатываемость) системы.
|
Магазин плагинов. Кастомные типы данных, для сложных случаев можно использовать объект с типом JSON (потребуется реализовать UI для управления данными). |
Публикация событий об изменении контента | Да. |
Возможность генерации API по созданной модели | GraphQL playground с документаций по API (после установки плагина). |
Ролевая модель пользователей и API | Есть различные роли и можно добавлять свои: |
Мультитенантность | Через разработку, из коробки решения нет, но есть возможности настройки, которые не тестировались: Strapi-v4 | refine Есть плагин (также не тестировали): strapi-plugin-multi-tenant — npm Package Overview — Socket |
Orchard Core
Сайт | orchardcore.net |
Сообщество/поддержка | 6.7k stars Watchers 339 Forks 2.2k Contributors 242 This project is supported by the .NET Foundation. |
Open source решение | Да. |
Лицензия и ее классификация по SAM OSS лицензии | SAM 2.0 (mts.ru) | BSD-3-Clause license. |
Версия на момент проведения анализа | 1.5.0 (04 Nov 2022) — была актуальна по состоянию на март 2023 |
Репозиторий | github.com/OrchardCMS/OrchardCore |
Стек (frontend) | JS/ASP.NET Core |
Стек (backend) | ASP.NET Core |
СУБД | SQL Server MySQL PostgreSQL SQLite using a document abstraction (YesSql) |
Наличие GraphQL API | Есть. Можно настроить скрытие определенных полей в GraphQL. |
Удобство создания и управления моделью данных, включая поддержку вложенности элементов | Можно добавить Part в Content Type несколько раз (если выбрать настройку reusable). Не удалось добавить несколько элементов в одном Part; нельзя вложить один part в другой, только простые поля. Можно сделать подстановку элементов из справочника (в том числе выбрать несколько значений), но в списке непонятно, что за элементы, т. к. выводится id, который не видно в UI. |
Поддержка SSO-авторизации | Есть плагин OpenId. |
Особенности работы с мультимедийным контентом:
|
1. Можно менять через appsettings.json. 2. Да. |
Наличие локализации | Да. |
Поддержка версионирования. Рассматривали:
|
Судя по всему функция версионирования реализована через данный модуль Audit Trail module (Lombiq Technologies: OCORE-23) by domonkosgabor · Pull Request #6679 · OrchardCMS/OrchardCore · GitHub, но возможности не тестировались. |
Возможность управления flow (статусы у сущностей).
|
Возможность самостоятельно управлять статусами, а также событиями, по которой происходит их смена; допустима даже отложенная публикация/архивация через заранее заданное время Workflows — Orchard Documentation (orchardcore.net). Отложенная публикация есть. |
Удобство использования | Можно настроить правила валидации для формы. Пробовали разные комбинации настроек, но все созданные сущности имеют одно наименование в списке: Не работают массовые операции, даже если выделить несколько записей и попробовать удалить, удаляется одна: |
Особенности | 1. Может работать в трех режимах, в том числе как Headless CMS:
2. Субъективно кажется, что структура в JSON усложняется неоправданно (добавляется формат поля), например:
|
Импорт контента из внешних источников | Попробовали сделать импорт, структура по JSON не создается, импорт прошел успешно, но при этом результат никуда не сохранился. Похоже, только импорт данных в уже созданную структуру. |
Хранение бинарных файлов (assets)/мультимедиа (S3 compatible) или интеграция с такой системой |
|
Нефункциональные требования.
|
Можно написать свой плагин для кэша. Есть механизмы кэширования данных. |
Расширяемость (дорабатываемость) системы.
|
Предусмотрена расширяемость с помощью плагинов. |
Публикация событий об изменении контента | Да. |
Возможность генерации API по созданной модели | Встроенный GraphQL браузер. |
Ролевая модель пользователей и API | Есть различные роли и можно добавлять свои: |
Мультитенантность | Материалы про настройку: Tenants — Orchard Core Documentation |
6. UI систем и примеры моделирования
Squidex
Панель управления Squidex включает два ключевых раздела:
Работа с созданием структуры данных проводится в разделе «Sсhemas», как выглядит управление данными в этом разделе можно посмотреть на скрине ниже:
Для моделирования доступны следующие типы данных:
Особенность, которую не обнаружили в других CMS, тип данных Components — позволяет к одному блоку привязать сразу несколько несколько схем данных.
Например, можно настроить заполнение в одном месте всех групп, в состав которых входит продукт. При этом может заполняться либо одна группа, либо другая, либо обе, в зависимости от кейса.
На этапе внесения сотрудник выбирает — о какой из сущностей хочет внести информацию в данный момент:
При этом сущности Category и Group могут иметь различный атрибутивный состав:
Доступно много возможностей для настройки валидации полей, причем перечень настроек зависит от типа данных. Ниже пример для текстового поля:
Для типа данных можно выбрать более подходящий «редактор», который предлагает подходящий интерфейс и дополнительные ограничения и валидации. Например, для текстового поля можно выбрать следующие варианты:
Удобно вносить текстовые данные в «MarkDown». В редакторе можно встраивать в текст гиперссылки, списки и т. п. И сразу просматривать результат:
Что касается моделирования сложной структуры данных, инструмент позволяет моделировать данные даже для прайс-листов. Например:
Внутри прайс-листа элементы группируются (группы — «В пределах пакета»/«После исчерпания пакета»/«Не включено в пакет в примере выше»).
При этом у элементов есть значения (стоимость) и различные единицы измерения (за сообщение/за минуту и т. п.).
Сами группы и единицы измерения — это не просто текст, это значения, которые должны выбираться из справочников.
Примечание
Данный пример взят, чтобы более наглядно проиллюстрировать возможности системы.
Моделирование подобных структур данных возможно при добавлении в схему данных компонентов (тип Components).
Наш компонент в разделе «Sсhemas» имеет следующую структуру:
Структура данных для продукта в Squidex
{
"id": "6a2abfe1-cf0c-4453-b449-3886171668c5",
"created": {
"value": "2023-06-21T06:05:12.000Z"
},
"createdBy": "subject:6437d117ed1a95c0fc81c021",
"lastModified": {
"value": "2023-06-22T05:03:11.000Z"
},
"lastModifiedBy": "subject:6437d117ed1a95c0fc81c021",
"version": {
"value": "10"
},
"status": "Published",
"statusColor": "#4bb958",
"scheduleJob": null,
"isDeleted": false,
"data": {
"title": {
"iv": "МТС Music"
},
"slug": {
"iv": "mts-music"
},
"channel": {
"iv": [
"323377a9-c720-43a4-aab0-7df9b7ad4f65"
]
},
"region": {
"iv": null
},
"validFrom": {
"iv": "2023-05-20T00:00:00Z"
},
"validTo": {
"iv": "9999-12-31T00:00:00Z"
},
"icon": {
"iv": [
"299224cf-20e5-4936-9ad5-073721ce7938"
]
},
"mainImage": {
"iv": [
"e0fa12c6-ddcb-48c3-ae95-15a9afa2c92a"
]
},
"otherMedia": {
"iv": null
},
"description": {
"iv": "Персональные подборки под любое настроение в приложении МТС Music"
},
"shortDescription": {
"iv": "Месяц музыки бесплатно без рекламы"
},
"addInformation": {
"iv": [
{
"schemaId": "0b417452-e2df-486f-8f92-09aae9ad2d03",
"title": "Почему стоит подключить:",
"text": "*Два месяца бесплатно\n* Без рекламы\n* Можно скачивать и слушать офлайн\n* Персональные рекомендации\n* Бесплатный трафик для абонентов МТС\n* Более 65 млн треков в высоком качестве"
}
]
},
"productSpecId": {
"iv": "93c20eac-e70b-4364-9626-fdf36148745e"
},
"advantages": {
"iv": [
{
"schemaId": "a518c845-f85f-4342-9e71-675d4f6a7ad0",
"title": "Выбирайте готовый плейлист",
"description": "В дороге, дома, на работе или тренировке",
"icon": null,
"image": [
"4036fcb7-e4aa-496f-a60c-eb502d036982"
]
},
{
"schemaId": "a518c845-f85f-4342-9e71-675d4f6a7ad0",
"title": "Детский режим",
"description": "С аудиосказками, песнями и подборками из мультфильмов",
"icon": null,
"image": [
"70debf41-5106-47d7-871c-057ac378e4eb"
]
}
]
},
"faq": {
"iv": [
{
"schemaId": "f51d088d-6758-41c5-9d5a-674a5786a899",
"question": "Если у меня уже была подписка МТС Music, будет ли доступено два месяца бесплатно?\n ",
"answer": "Нет. Предложение доступно только новым пользователям, которые никогда не подключали платную подписку МТС Music."
},
{
"schemaId": "f51d088d-6758-41c5-9d5a-674a5786a899",
"question": "Зачем подключать подписку МТС Music?\n ",
"answer": "По подписке вы получаете полный доступ ко всему функционалу приложения и сайта, в том числе к поиску и офлайн-режиму прослушивания музыки."
},
{
"schemaId": "f51d088d-6758-41c5-9d5a-674a5786a899",
"question": "Что будет, если отключить подписку в пробный период?\n ",
"answer": "При отключении услуги до истечения 30 дней и повторном её подключении услуга будет предоставляться на платной основе, бесплатные дни предоставляться не будут.\n\nПосле отключения платной подписки прослушивание своих сохранённых треков и плейлистов в режиме офлайн становится недоступным."
},
{
"schemaId": "f51d088d-6758-41c5-9d5a-674a5786a899",
"question": "Сколько будет стоить подписка после двух месяцев бесплатного доступа?\n ",
"answer": "169 рублей в месяц."
},
{
"schemaId": "f51d088d-6758-41c5-9d5a-674a5786a899",
"question": "Как оплачивать МТС Music после пробного периода?\n ",
"answer": "Для абонентов ПАО «МТС» списание средств за подписку будет производиться с лицевого счёта абонента.\n\nАбоненты других операторов мобильной связи смогут оплатить подписку, привязав банковскую карту с помощью сервиса МТС Деньги, который будет открываться непосредственно в приложении МТС Music – дополнительная авторизация не потребуется. Списания будут производиться с БК в соответствии с графиком списания подключённой услуги, безакцептно. История списаний за пользование сервисом будет отображаться в истории платежей по банковской карте абонента. Управлять подпиской пользователь сможет в приложении МТС Music, в разделе «Моя музыка» —> «Профиль»."
},
{
"schemaId": "f51d088d-6758-41c5-9d5a-674a5786a899",
"question": "Условия использования",
"answer": "*Если в течение 30 дней пробного бесплатного периода вы не пользовались сервисом, услуга будет предоставляться на платной основе, бесплатные дни предоставляться не будут.\n\n* Под «первые 30 дней для новых пользователей в подарок» понимается скидка в размере 100% стоимости первых по времени 30 дней пользования сервисом.\n\n* Подписка продлевается автоматически на месяц, если автопродление не отключено до окончания текущего периода, до 24:00. Неиспользованная часть бесплатного пробного периода аннулируется после приобретения подписки. Вы можете управлять своими подписками после покупки и отключить автопродление в настройках своей учётной записи.\n\n* Обращаем ваше внимание, что для онлайн-прослушивания музыки необходимо наличие доступа к сети интернет. В случае если включённая в основной и дополнительный пакет квота интернет-трафика будет превышена, вы сможете прослушивать только сохранённые в приложении композиции, онлайн-прослушивание музыки будет недоступно.\n\n* Бесплатный интернет-трафик при прослушивании музыки в приложении МТС Music не тарифицируется только для абонентов МТС.\n\n* Бесплатный интернет-трафик предоставляется только при прослушивании музыки в приложении МТС Music, загрузка прочего содержимого оплачивается согласно условиям тарифного плана.\n\n* Сервис доступен абонентам сети мобильной связи любого оператора на всей территории Российской Федерации и предназначен для смартфонов и планшетов на базе операционных систем Android (4.1 и выше), iOS (7.0 и выше), а также ПК.\n\n* Услугу предоставляет ПАО «МТС» при поддержке ООО «Стрим» и ООО «Яндекс.Медиасервисы».\n\n* Все цены указаны с учётом НДС."
}
]
},
"price": {
"iv": [
{
"schemaId": "7766d9e4-5d1a-4853-8ff3-0896544b2851",
"currentPrice": "0",
"oldPrice": "169",
"unit": [
"c229c4a8-2d3e-4634-9ccc-45778b357193"
],
"title": "Триальный период",
"shortDescription": "30 дней бесплатно, далее 169 ₽/мес",
"description": null
}
]
}
},
"schemaName": "contentspecifiction",
"schemaDisplayName": "contentspecifiction",
"referenceFields": [
{
"fieldId": 1,
"name": "title",
"properties": {
"isRequired": false,
"isRequiredOnPublish": false,
"isHalfWidth": false,
"fieldType": "String",
"createEnum": false,
"editor": "Input",
"inlineEditable": false,
"isEmbeddable": false,
"isUnique": false,
"contentType": "Unspecified",
"label": "Наименование"
},
"isLocked": false,
"isHidden": false,
"isDisabled": false,
"_links": {},
"canAddField": false,
"canDelete": false,
"canDisable": false,
"canEnable": false,
"canOrderFields": false,
"canHide": false,
"canLock": false,
"canShow": false,
"canUpdate": false,
"partitioning": "invariant",
"nested": []
}
],
"_links": {
"self": {
"href": "/api/content/mts/contentspecifiction/6a2abfe1-cf0c-4453-b449-3886171668c5",
"method": "GET"
},
"previous": {
"href": "/api/content/mts/contentspecifiction/6a2abfe1-cf0c-4453-b449-3886171668c5/9",
"method": "GET"
},
"draft/create": {
"href": "/api/content/mts/contentspecifiction/6a2abfe1-cf0c-4453-b449-3886171668c5/draft",
"method": "POST"
},
"status/Archived": {
"href": "/api/content/mts/contentspecifiction/6a2abfe1-cf0c-4453-b449-3886171668c5/status",
"method": "PUT",
"metadata": "#eb3142"
},
"status/Draft": {
"href": "/api/content/mts/contentspecifiction/6a2abfe1-cf0c-4453-b449-3886171668c5/status",
"method": "PUT",
"metadata": "#8091a5"
},
"delete": {
"href": "/api/content/mts/contentspecifiction/6a2abfe1-cf0c-4453-b449-3886171668c5",
"method": "DELETE"
},
"patch": {
"href": "/api/content/mts/contentspecifiction/6a2abfe1-cf0c-4453-b449-3886171668c5",
"method": "PATCH"
},
"update": {
"href": "/api/content/mts/contentspecifiction/6a2abfe1-cf0c-4453-b449-3886171668c5",
"method": "PUT"
}
},
"canDelete": true,
"canDraftCreate": true,
"canDraftDelete": false,
"canCancelStatus": false,
"canUpdate": true,
"statusUpdates": [
{
"status": "Archived",
"color": "#eb3142"
},
{
"status": "Draft",
"color": "#8091a5"
}
]
}
А вот пример запроса и ответа в GraphQL API.
Запрос
{
queryContentspecifictionContents(
filter: "data/productSpecId/iv eq '93c20eac-e70b-4364-9626-fdf36148745e' and status eq 'Published' and data/channel/iv eq '323377a9-c720-43a4-aab0-7df9b7ad4f65' and data/validFrom/iv gt 2023-05-19T00:00:00Z"
) {
id
status
version
flatData {
title
validFrom
validTo
productSpecId
channel {
id
... on Channel {
flatData {
externalid
title
}
}
}
description
shortDescription
addInformation {
title
text {
text
}
}
advantages {
title
description
icon {
type
metadata
url
}
}
icon {
type
metadata
url
tags
}
mainImage {
type
metadata
url
tags
}
otherMedia {
type
metadata
url
tags
}
faq {
question {
text
}
answer {
text
}
}
price {
title
description
shortDescription
oldPrice
currentPrice
unit {
id
flatData {
title
alias
titleInGenitiveCase
fullTitle
}
}
}
}
}
}
Ответ
{{
"data": {
"queryContentspecifictionContents": [
{
"id": "6a2abfe1-cf0c-4453-b449-3886171668c5",
"status": "PUBLISHED",
"version": 10,
"flatData": {
"title": "МТС Music",
"validFrom": "2023-05-20T00:00:00Z",
"validTo": "9999-12-31T00:00:00Z",
"productSpecId": "93c20eac-e70b-4364-9626-fdf36148745e",
"channel": [
{
"id": "323377a9-c720-43a4-aab0-7df9b7ad4f65",
"flatData": {
"externalid": "741ba712-3069-48e3-928c-8631e5ed186e",
"title": "Personal"
}
}
],
"description": "Персональные подборки под любое настроение в приложении МТС Music",
"shortDescription": "Месяц музыки бесплатно без рекламы",
"addInformation": [
{
"title": "Почему стоит подключить:",
"text": {
"text": "*Два месяца бесплатно\n* Без рекламы\n* Можно скачивать и слушать офлайн\n* Персональные рекомендации\n* Бесплатный трафик для абонентов МТС\n* Более 65 млн треков в высоком качестве"
}
}
],
"advantages": [
{
"title": "Выбирайте готовый плейлист",
"description": "В дороге, дома, на работе или тренировке",
"icon": null
},
{
"title": "Детский режим",
"description": "С аудиосказками, песнями и подборками из мультфильмов",
"icon": null
}
],
"icon": [
{
"type": "UNKNOWN",
"metadata": {},
"url": "http://127.0.0.1/api/assets/mts/299224cf-20e5-4936-9ad5-073721ce7938/",
"tags": [
"type/svg",
"image"
]
}
],
"mainImage": [
{
"type": "IMAGE",
"metadata": {
"pixelWidth": 934,
"pixelHeight": 937,
"imageQuality": 100,
"description": "JFIF File"
},
"url": "http://127.0.0.1/api/assets/mts/e0fa12c6-ddcb-48c3-ae95-15a9afa2c92a/",
"tags": [
"type/jpg",
"image",
"image/medium"
]
}
],
"otherMedia": null,
"faq": [
{
"question": {
"text": "Если у меня уже была подписка МТС Music, будет ли доступено два месяца бесплатно?\n "
},
"answer": {
"text": "Нет. Предложение доступно только новым пользователям, которые никогда не подключали платную подписку МТС Music."
}
},
{
"question": {
"text": "Зачем подключать подписку МТС Music?\n "
},
"answer": {
"text": "По подписке вы получаете полный доступ ко всему функционалу приложения и сайта, в том числе к поиску и офлайн-режиму прослушивания музыки."
}
},
{
"question": {
"text": "Что будет, если отключить подписку в пробный период?\n "
},
"answer": {
"text": "При отключении услуги до истечения 30 дней и повторном её подключении услуга будет предоставляться на платной основе, бесплатные дни предоставляться не будут.\n\nПосле отключения платной подписки прослушивание своих сохранённых треков и плейлистов в режиме офлайн становится недоступным."
}
},
{
"question": {
"text": "Сколько будет стоить подписка после двух месяцев бесплатного доступа?\n "
},
"answer": {
"text": "169 рублей в месяц."
}
},
{
"question": {
"text": "Как оплачивать МТС Music после пробного периода?\n "
},
"answer": {
"text": "Для абонентов ПАО «МТС» списание средств за подписку будет производиться с лицевого счёта абонента.\n\nАбоненты других операторов мобильной связи смогут оплатить подписку, привязав банковскую карту с помощью сервиса МТС Деньги, который будет открываться непосредственно в приложении МТС Music – дополнительная авторизация не потребуется. Списания будут производиться с БК в соответствии с графиком списания подключённой услуги, безакцептно. История списаний за пользование сервисом будет отображаться в истории платежей по банковской карте абонента. Управлять подпиской пользователь сможет в приложении МТС Music, в разделе «Моя музыка» —> «Профиль»."
}
},
{
"question": {
"text": "Условия использования"
},
"answer": {
"text": "*Если в течение 30 дней пробного бесплатного периода вы не пользовались сервисом, услуга будет предоставляться на платной основе, бесплатные дни предоставляться не будут.\n\n* Под «первые 30 дней для новых пользователей в подарок» понимается скидка в размере 100% стоимости первых по времени 30 дней пользования сервисом.\n\n* Подписка продлевается автоматически на месяц, если автопродление не отключено до окончания текущего периода, до 24:00. Неиспользованная часть бесплатного пробного периода аннулируется после приобретения подписки. Вы можете управлять своими подписками после покупки и отключить автопродление в настройках своей учётной записи.\n\n* Обращаем ваше внимание, что для онлайн-прослушивания музыки необходимо наличие доступа к сети интернет. В случае если включённая в основной и дополнительный пакет квота интернет-трафика будет превышена, вы сможете прослушивать только сохранённые в приложении композиции, онлайн-прослушивание музыки будет недоступно.\n\n* Бесплатный интернет-трафик при прослушивании музыки в приложении МТС Music не тарифицируется только для абонентов МТС.\n\n* Бесплатный интернет-трафик предоставляется только при прослушивании музыки в приложении МТС Music, загрузка прочего содержимого оплачивается согласно условиям тарифного плана.\n\n* Сервис доступен абонентам сети мобильной связи любого оператора на всей территории Российской Федерации и предназначен для смартфонов и планшетов на базе операционных систем Android (4.1 и выше), iOS (7.0 и выше), а также ПК.\n\n* Услугу предоставляет ПАО «МТС» при поддержке ООО «Стрим» и ООО «Яндекс.Медиасервисы».\n\n* Все цены указаны с учётом НДС."
}
}
],
"price": [
{
"title": "Триальный период",
"description": null,
"shortDescription": "30 дней бесплатно, далее 169 ₽/мес",
"oldPrice": "169",
"currentPrice": "0",
"unit": [
{
"id": "c229c4a8-2d3e-4634-9ccc-45778b357193",
"flatData": {
"title": "₽/мес",
"alias": null,
"titleInGenitiveCase": "рублей в месяц",
"fullTitle": null
}
}
]
}
]
}
}
]
},
"extensions": {
"tracing": {
"version": 1,
"startTime": "2023-06-26T05:20:49.9570693Z",
"endTime": "2023-06-26T05:20:50.0001375Z",
"duration": 43068200,
"parsing": {
"startOffset": 1100,
"duration": 43300
},
"validation": {
"startOffset": 45100,
"duration": 1028900
},
"execution": {
"resolvers": []
}
}
}
} }
Strapi
А теперь посмотрим, как выглядит Strapi.
Панель управления:
Ключевыми разделами Strapi CMS являются:
- Content-Type Builder — инструмент создания объектов, который позволяет создавать таблицы для хранения данных и связывать их между собой, а также настраивать их отображение (порядок полей при заполнении и прочее) для пользователя;
- Сontet Manager — раздел для заполнения созданных объектов данными.
С чем ведется работа в Content-Type Builder:
- коллекции «COLLECTION TYPES» — набор данных разных типов, который может включать множество записей;
- компоненты — объект данных, упрощающий внесение данных в коллекции. Например, когда нужно создать подряд несколько однотипных записей внутри коллекции;
- диночные типы «SINGLE TYPES» — набор данных разных типов. В отличие от коллекции может включать только одну запись. Полезно для создания простых страниц. Например, для создания раздела «Контакты» на сайте.
Подробнее про создание объектов в Strapi CMS можно почитать тут.
Наполнять контент можно данными следующих типов:
В отличии от Squidex нельзя настроить отдельное наименование поля и подсказку для отображения пользователю при внесении:
Гораздо меньше настроек по валидации и формату ввода данных (также на примере текстового поля):
Моделирование сложных структур данных и данных с несколькими элементами в составе производится с помощью компонентов (как и в Squidex):
Что касается моделирования сложной структуры данных, рассмотрим их заведение (как и для Squidex) на этом примере:
Напомним, что внутри прайс-листа элементы группируются. При этом группы и единицы измерения — это не просто текст, а значения, которые должны выбираться из справочников.
Моделирование подобных структур данных возможно при добавлении в схему данных компонентов (тип Components).
Наш компонент имеет следующую структуру:
В отличие от Squidex на этом экране нет функции перетаскивания и нельзя изменить очередность заполнения полей и компонентов на форме.
Есть кнопка конфигурирования представления
и только после клика по ней происходит переход к виду, где можно менять очередность блоков:
Так будет выглядеть внесение информации в разделе «Сontet Manage» UI Strapi для первого элемента данных:
Подсказка
Выбор значения из списка для справочников (коллекций) проходит по умолчанию по первому полю, которое было создано для данного справочника. Т. е. если для коллекции Group при ее создании первым полем было «title», то и при внесении контента из списка выбираются значения по этому полю. Для других справочников первым в списке оказалось поле «slug», из-за чего на форме видим такие некрасивые значения. Изменить это можно только полностью пересоздав коллекцию и заново заполнив ее значения.
Поэтому не повторяйте наших ошибок, следите, чтобы поле «title» всегда шло первым, если используете данную CMS.
Поэтому не повторяйте наших ошибок, следите, чтобы поле «title» всегда шло первым, если используете данную CMS.
В отличие от Squidex при внесении данных нельзя добавить новую запись в справочник, например, в перечень групп, только выбрать существующую из списка значений.
Остальные заполняются аналогично — путем добавления нового экземпляра компонента:
Ниже можно посмотреть, как будет выглядеть данная информация в GraphQL API.
Запрос
query {
tariffs(
filters: {
tp: { title: { eq: "Тарифище" } }
and: [
{ region: { title: { eq: "Краснодар" } } }
{ vaidFrom: { gte: "2010-08-25T10:15:30Z" } }
{ validTo: { lte: "2050-08-25T10:15:30Z" } }
]
}
) {
data {
id
attributes {
detailInformation {
group {
data {
attributes {
title
iconURL
}
}
}
sub_gr {
data {
attributes {
title
slug
}
}
}
title {
data {
attributes {
slug
title
}
}
}
value
unit {
data {
attributes {
title
slug
}
}
}
}
}
}
}
}
Ответ
{
"data": {
"tariffs": {
"data": [
{
"id": "6",
"attributes": {
"detailInformation": [
{
"group": {
"data": {
"attributes": {
"title": "SMS и MMS, за сообщение",
"iconURL": "//contents.mts.ru/b-icon_envelope.svg"
}
}
},
"sub_gr": {
"data": {
"attributes": {
"title": "В пределах включенного пакета SMS",
"slug": "SMS-v-predelah-vklyuchennogo-paketa"
}
}
},
"title": {
"data": {
"attributes": {
"slug": "SMSRussiaOperators",
"title": "SMS на номера любых операторов России"
}
}
},
"value": "3.70",
"unit": {
"data": {
"attributes": {
"title": "₽ за сообщение",
"slug": "rub-msg"
}
}
}
}
]
}
}
]
}
}
}
Orchard Core
В Orchard Core столкнулись с ограничениями в моделировании данных и прочими сложностями, которые представлены в сравнительном анализе (таблица выше). Поэтому покажем только базовые элементы интерфейса. Примеры моделирования данных будут приведены только для Strapi и Squidex.
В основном работа ведется с Типами (Parts) и Частям контента.
Части контента — это что-то вроде компонентов в других системах:
Подробнее вопрос терминологии системы раскрывается тут.
Можно встроить в контент следующие типы данных:
Перечень доступных настроек очень ограничен.
7. Заключение
Выбор и применение современных CMS является важным вопросом для команд, которые хотят эффективно управлять контентом на различных платформах. Решение в каждом случае необходимо принимать исходя из собственных потребностей и ИТ-ландшафта.
Сводная таблица со сравнением CMS по ключевым для нашей команды критериям.
Критерий | Strapi CMS | Squidex | Orchard Core |
Сообщество/поддержка | Большое, частые обновления | Среднее, тесное взаимодействие с пользователями | Среднее, поддержка .NET Foundation |
Open source решение | |||
Стек:
|
Angular + Node.js (JS/TypeScript) | Angular (TypeScript) + C# | JS + ASP.NET Core |
СУБД | SQL (PostgreSQL, MySQL/MariaDB, SQLite) | NoSQL (MongoDB) | SQL (SQL Server, MySQL, PostgreSQL, SQLite) |
Удобство использования | Среднее | Высокое | Низкое |
Контроль над ошибками в UI и возможность «сломать» систему | Низкий | Высокий | Средний |
Гибкость системы при моделировании данных | Средняя | Высокая | Низкая |
Поддержка GraphQL API | С помощью плагина GraphQL | Strapi Market | ||
Наличие локализации | |||
Поддержка версионирования | Есть плагин | Через модуль | |
Возможность управления flow | Есть плагин | ||
Импорт контента из внешних источников | Есть плагин | ||
Хранение бинарных файлов (assets)/мультимедиа (S3 compatible) или интеграция с такой системой | |||
Расширяемость системы | Есть свой магазин плагинов | Предусмотрена расширяемость с помощью плагинов | Предусмотрена расширяемость с помощью плагинов |
Публикация событий об изменении контента | |||
Ролевая модель пользователей и API | |||
Мультитенантность из коробки |
— решение из коробки соответствует критерию/поддерживает данный функционал.
— решается частично либо с установкой расширений/плагинов.
— отсутствует.
По результатам пилотных задач на наш взгляд самой удобной оказалась CMS Squidex, как при моделировании схемы данных, так и при её наполнении.
В Strapi постоянно сталкивались с различными багами. Есть замечания к пользовательскому интерфейсу, не все операции интуитивны и понятных даже для специалистов с опытом работы с другими CMS.
В UI Orchard Core были большие сложности с моделированием контента. В CMS не оказалось ряда критичных для нас функций (к которым мы уже привыкли после Strapi и Squidex), например, создание нескольких экземпляров одного компонента при внесении контента или возможность вложить компонент в компонент.
У Squidex интуитивно понятный UI, гибкие возможности по созданию модели данных. Мы не столкнулись с какими-либо ограничениями при моделировании.
Это решение отвечает всем исходным требованиям, которые мы предъявляли к Headless CMS.
Из-за особенностей предметной области у нас есть дополнительные требования по регионализации и версионированию контента, которые не поддерживаются ни одной из систем, но базовая функциональность и стек Squidex являются хорошей основной для последующей кастомизации и доработок.
Будем рады, если статья оказалась вам полезной. В комментариях поделитесь, интересны ли вам другие возможности CMS или, возможно, хотелось бы поподробнее узнать про какую-то функциональность. Если тема будет интересна, обязательно расскажем подробнее в будущих статьях.
CloudMTS поможет гибко управлять и разворачивать системы любого масштаба: от сайтов-визиток и простых CMS до высоконагруженных e-commerce-проектов. Сервис позволяет быстро выстраивать отказоустойчивую систему и масштабировать виртуальные машины под разный уровень нагрузки.