Webasyst · Плагин · Внедрение
Правим лендинг прямо на странице
Плагин, который позволяет редактировать содержимое секций лендинга прямо на странице — так, как это делает посетитель. Кликнул по заголовку, поменял текст, добавил пункт в список, заменил картинку, нажал «Сохранить». Данные уходят в стандартный блок Webasyst. Никакой отдельной админки, никакого конструктора, никакого параллельного хранилища.
С чего всё началось
Это не история одного клиента. Это задача, с которой я сталкиваюсь постоянно: страницы собираются как лендинги, из секций. Заголовок, список услуг, блок статистики, кнопки, картинки — набор примерно один и тот же, меняется содержание. Я верстаю такие секции для клиентов и давно вынес данные отдельно — в блок Webasyst. Один шаблон секции можно переиспользовать на разных страницах, а содержание живёт своей жизнью.
Это удобно. Один блок — один массив данных, одна точка правки. Шаблон не трогаем, меняем только данные. Для разработчика — почти идеальная схема.
Проблема начинается там, где за данные берётся контент-менеджер. Чтобы поменять заголовок, нужно зайти в админку, найти нужный блок, открыть его и увидеть перед собой PHP-массив. Внутри — секции, вложенные массивы, строки, кавычки, запятые. Найти нужное поле, аккуратно поправить текст. Одна лишняя кавычка — и страница падает с белым экраном. Одна пропущенная запятая — и Smarty ругается так, что лучше не читать.
Формально это работает. Фактически — каждый раз маленький стресс. И каждый раз вопрос: «а можно я просто на странице поменяю?»
Я получал это снова и снова — от разных клиентов, на разных проектах. Не потому, что они не могли разобраться. А потому, что не хотели рисковать. И я их понимал. В какой-то момент стало очевидно: это не частная проблема одного сайта, а стандартный сценарий, которому нужен стандартный инструмент.
Что мы хотели получить
Мы сформулировали для себя несколько условий. Не «сделать редактор», а именно то, каким он должен быть, чтобы им реально пользовались.
Во-первых, контент-менеджер должен видеть страницу как посетитель. Не абстрактную форму с полями, не таблицу, не дерево — а ту же самую страницу, только с возможностью править текст прямо на месте. Это принципиально: человек работает с тем, что видит, а не с тем, что мы ему описали словами.
Во-вторых, изменения должны сохраняться в тот же блок Webasyst. Не в новую таблицу, не в отдельный JSON, не в файл. В тот самый массив, который уже читает шаблон. Иначе мы создаём вторую правду о контенте, и рано или поздно они разойдутся.
В-третьих, вёрстка не должна ломаться. Редактор работает только с теми полями, которые мы явно разметили. Всё остальное — иконки, декоративные элементы, структура — остаётся нетронутым. Контент-менеджер не может случайно удалить секцию или сломать разметку, потому что он физически не может до неё добраться.
В-четвёртых, должен быть откат. Если что-то пошло не так — вернуть предыдущую версию. Это не «nice to have», это условие, без которого мы бы не стали это внедрять на боевой сайт.
И в-пятых, доступ только для авторизованных. Обычный посетитель видит обычную страницу. Никаких публичных переключателей — редактор включается сам, если ты админ.
Как это работает для клиента
Мы написали для клиента короткую инструкцию. Она занимает пол-экрана, и это лучшая характеристика решения.
Создаётся страница услуг в Shop-Script — как обычная страница. Заполняются мета-теги,
название, при необходимости текст. Из страницы убирается вызов блока — он там не нужен
и только мешает визуальному редактору страницы. Вместо этого добавляется один дополнительный параметр страницы: landing=services.design, где services.design — идентификатор блока, в котором лежат данные секций. Именно данные, а не сами секции: шаблоны секций остаются в теме и переиспользуются, а блок хранит только содержание. Это позволяет свободно менять порядок секций на странице, убирать ненужные и добавлять новые — без правки шаблона и без дублирования вёрстки.
И это же даёт переиспользование на другом уровне: один и тот же шаблон секции может встречаться на разных страницах с разными данными. Секция «заголовок с кнопкой» верстается один раз, а на каждой странице наполняется своим содержанием. Блок при этом у каждой страницы свой — правки одной страницы не задевают другую. Получается конструктор, но без конструктора: вёрстка живёт отдельно, данные живут отдельно, а страница собирается из того, что уже есть.
На этом работа в админке заканчивается. Всё остальное происходит на самой странице.
Админ заходит на страницу, и секции становятся редактируемыми. Заголовок — просто заголовок, по которому можно кликнуть и писать. Список — список, в котором можно добавить или удалить пункт. Картинка — картинка, которая спрашивает новый URL. Цифры, кнопки, подписи — всё на своих местах, всё редактируется там, где оно находится.
Внизу — панель с кнопками «Сохранить» и «Откатить», а также с возможностью посмотреть и вручную поправить код секции. Последнее — для тех случаев, когда нужно сделать что-то нестандартное. Но это уже продвинутый сценарий, а не обязательный.
Формулировка, которую мы дали клиенту и которая, кажется, лучше всего объясняет суть: работа со страницей в админке заканчивается на мета-тегах и названии. Всё остальное — на самой странице, в визуальном редакторе.
Отдельно подчеркну: редактор — это удобство, а не единственный способ. Данные по-прежнему можно править напрямую в блоке, как раньше. Мы ничего не отняли, мы добавили слой, которым можно пользоваться, а можно и не пользоваться.
Как это работает под капотом
Теперь про технику. Постараюсь без перечней и без путей — просто расскажу, как устроено.
Вся магия держится на разметке. В шаблоне секции помечаются специальными атрибутами: где начинается секция, какое поле за что отвечает, где список, где картинка, где HTML. Это не автогенерация — мы размечаем секции руками, зато получаем полный контроль. Редактор знает ровно столько, сколько мы ему сказали, и не лезет дальше.
На стороне браузера работает JS-модуль. Он не запускается сам по себе — тема вызывает его явно и передаёт настройки: какие секции редактировать, какой блок сохранять, куда стучаться. Дальше модуль собирает данные из DOM, сопоставляет их с эталоном, который лежит в атрибуте секции в виде JSON, и превращает в PHP-массив. Тот самый массив, который потом ляжет в блок.
Сборка данных — самая тонкая часть. Нужно не потерять поля, которые мы не редактируем, но которые есть в исходных данных. Например, кнопки: у них есть URL, цвет, привязка к форме — всё это не текст, и в DOM его нет. Мы решаем это наследованием эталона: если поле не переопределено в разметке, оно берётся из исходного JSON. И удаляем корень массива только тогда, когда все его элементы размечены. Иначе — оставляем как есть, чтобы ничего не потерять.
Отдельная история — как данные ложатся в блок. Блок Webasyst — это не просто хранилище, а Smarty-шаблон, и для парсера кавычки, слэши и фигурные скобки — служебные символы. Одинарная кавычка внутри строки разрывает строку, обратный слэш сам является символом экранирования, а фигурная скобка может начать выражение, которое парсер попытается выполнить. Поэтому значение собирается вручную, с учётом правил Smarty: кавычки и слэши экранируются, фигурные скобки заменяются на HTML-сущности. Тогда парсер их не видит, а браузер отображает как обычные символы. Плюс — никаких лишних запятых в конце, потому что на них Smarty падает. Всё это выглядит мелочью, пока не начнёшь отлаживать белый экран на боевом сайте.
На стороне сервера — два обработчика: сохранение и откат. Оба принимают запрос, проверяют CSRF, валидируют имя блока и имя секции. Никакой кириллицы в ключах, никаких пробелов, никаких точек с запятой — только латиница, цифры и подчёркивания. Это не паранойя, это опыт: кириллица в ключе секции однажды стоила нам нескольких часов отладки.
Перед записью фрагмент проверяется на опасные конструкции — скриптовые теги, открывающие PHP-теги и вызовы системных функций. Проверяется парность скобок и кавычек. Проверяется, что в новом тексте остался вызов глобалов — если он потерялся, мы не сохраняем, потому что это верный признак того, что мы снесли что-то важное.
Сама замена секции в блоке — это поиск диапазона от начала секции до следующей секции, до маркера «не удалять» или до вызова глобалов. Последняя секция — самый неприятный случай: если искать только до следующей секции, можно случайно съесть хвост блока. Поэтому мы явно ищем маркеры конца.
Бэкап делается один раз за сессию и не плодит дубликаты, даже если сохранять несколько раз подряд. Откат возвращает предыдущую версию и расходует бэкап — повторно откатиться к тому же состоянию нельзя, но можно откатиться к более раннему, если он есть.
Отдельно про ошибки. Если что-то пошло не так — не прошла валидация, потерялся вызов глобалов, не сошлись скобки — редактор не сохраняет и говорит, что именно не так. Без технических подробностей, но с достаточной конкретикой, чтобы человек понял, что делать. Молчаливых отказов нет: либо сохранено, либо объяснено, почему нет.
Как выглядит разметка секции
Чтобы редактор знал, что и где можно править, секция в шаблоне размечена атрибутами. Вот пример — заголовок с описанием и списком услуг.
<section data-section="services"
data-template="services"
data-wa-json="...">
<h1 data-wa-field="title">Замена под ключ</h1>
<p data-wa-field="description">Короткое описание услуги</p>
<ul data-wa-field="list" data-wa-type="list">
<li><span>Первый пункт</span></li>
<li><span>Второй пункт</span></li>
</ul>
<picture data-wa-field="image" data-wa-image data-wa-src="...">
<img src="..." alt="">
</picture>
</section>
Что означают атрибуты:
data-section — уникальный ключ секции. По нему редактор понимает, с какой секцией работает, и находит её данные в блоке. Только латиница, цифры и подчёркивания — никакой кириллицы.
data-template — имя шаблона. Справочная информация: если один шаблон переиспользуется, здесь видно, какой именно.
data-wa-json — исходные данные секции в виде JSON. Это эталон, с которым редактор сверяется при сборке. Если поле не редактируется, оно берётся отсюда — чтобы ничего не потерять.
data-wa-field — путь до поля внутри секции. Именно это поле редактор делает редактируемым и сохраняет обратно.
data-wa-type="list" — маркер списка. Говорит редактору, что это не одно значение, а набор строк, которые можно добавлять и удалять.
data-wa-image и data-wa-src — изображение. Редактор понимает, что здесь картинка, и позволяет заменить её по URL.
data-wa-html — явный HTML-режим. Если он стоит, содержимое сохраняется как HTML, а не как простой текст. Нужен там, где важна разметка — ссылки, выделения, переносы.
data-wa-item — элемент массива с индексом. Используется для сложных списков, где у каждого элемента несколько полей: например, карточка с заголовком, текстом и картинкой.
data-wa-icon — нередактируемая иконка. Редактор её не трогает: она часть вёрстки, а не данных.
Разметка ставится руками — под конкретную тему. Это не автогенерация, зато редактор знает ровно то, что ему разрешили, и не может случайно изменить то, что не должен.
Что это дало клиенту
Самое главное — самостоятельность. Контент-менеджер перестал бояться блока. Он открывает страницу, правит то, что нужно, сохраняет. Если что-то не так — откатывает. Всё.
Правка текста перестала быть тикетом. Она стала действием на десять секунд. Мы перестали быть службой «поменяйте слово в заголовке», и это, пожалуй, самый ценный результат.
Появилась защита от ошибок. Валидация, проверки, бэкап — всё это работает в фоне, и человеку не нужно об этом думать. Он просто не может сломать то, что мы защитили.
И, что важно, ничего не сломалось в привычных сценариях. Прямая правка блока по-прежнему работает. Мы не заставили клиента переучиваться, мы дали ему альтернативу.
Что это дало нам
Для студии это не просто ещё один кейс. Это инструмент, который можно тиражировать.
Во-первых, мы сняли с себя поток мелких правок. Часы, которые уходили на «поменяйте слово», теперь уходят на задачи, которые действительно требуют разработчика. Это экономия, которую легко посчитать.
Во-вторых, у нас появился продуктовый кейс. Не «мы сделали сайт», а «мы сделали инструмент, который решает класс задач». Такие кейсы продают компетенцию, а не часы.
В-третьих, это тиражируемое решение. Плагин можно предлагать другим клиентам на Webasyst — как готовую услугу внедрения, как лицензию студии, как часть пакета поддержки. Мы не привязаны к одному проекту.
И в-четвёртых, это готовый актив. Не «мы умеем», а «вот инструмент, вот как он работает, вот что он даёт». С таким заходом разговор с клиентом начинается не с оценки, а с демонстрации.
Технические заметки для тех, кому интересно
Если коротко: Webasyst, PHP, Smarty, MySQL, vanilla JS. Плагин живёт внутри приложения site — это стандартное приложение Webasyst, которое есть на любом сайте, независимо от того, магазин это, блог или простая визитка. Поэтому решение не привязано к Shop-Script и ставится на любой проект. Изменений в ядре не требует. Привязка к странице — через дополнительный параметр, данные хранятся в стандартном блоке. Бэкапы — в отдельной таблице, по одной записи на пару «блок + пользователь». Запросы на сохранение и откат защищены от подделки. Редактор включается только для авторизованных, никаких публичных переключателей.
Фигурные скобки в контенте хранятся как HTML-сущности — иначе Smarty их съест. Это ровно та деталь, которая отличает рабочее решение от «вроде работает, но иногда падает».
Никаких экзотических зависимостей, никаких сборщиков, никакого фреймворка. Всё на том, что уже есть в Webasyst.
Напоследок
Этот кейс — про то, как небольшая надстройка решает большую организационную проблему. Клиент перестаёт зависеть от студии в контенте, студия перестаёт тратить время на мелочи, а решение можно тиражировать.
Если у вас сайт на Webasyst и похожая боль — покажем, как это работает, оценим вашу тему и скажем, сколько займёт внедрение.