| 2.12.0 (15.09.2026) |
Что изменилось после версии 2.11.1.
Что нового
- Кто проверил материал. Страница получила свойства reviewedBy и lastReviewed: кто проверил содержимое на достоверность и когда. Материал на сложную тему нередко пишет редактор или маркетолог, а за факты отвечает профильный специалист — без разметки страница выглядит написанной непрофильным автором, хотя фактически она проверена.
- Рецензентом может быть и человек, и организация — редакция, лаборатория, экспертный совет.
- Специалист с постоянным идентификатором попадает в разметку ссылкой, а не копией: все проверенные им материалы связываются между собой в одну сущность.
Как это выглядит в коде
Свойства задаются на объекте страницы, который возвращает Schema::setMainEntity():
- ->setReviewedBy($reviewer) — принимает то же, что setAuthor(): объект Person, массив полей, имя строкой или ссылку по идентификатору. Вторым аргументом задаётся тип, если проверяла организация.
- ->setLastReviewed($date) — дата разбирается так же, как дата публикации.
Оба поля необязательные, и пустое значение допустимо: незаполненное свойство инфоблока просто не попадёт в разметку, проверять его перед вызовом не нужно. Уже проставленное значение пустым вызовом не затирается.
Где именно живут эти свойства
На странице, а не на статье. По schema.org reviewedBy и lastReviewed объявлены у типа WebPage, а Article и WebPage — не родитель и потомок, а соседи: свойства друг друга они не наследуют. Поставленные на статью, эти поля стали бы неизвестными, и валидатор Google выводил бы на них предупреждение.
Что исправлено
- Метод установки даты проверки при пустом значении заводил пустое поле. В разметку оно не попадало, но сущность с ним переставала считаться пустой и могла уехать в граф бессмысленным узлом. Метод существовал с версии 2.2.0, не был описан в руководстве и не проверялся тестами — теперь описан и покрыт.
Что сделать после обновления
- Если на сайте есть материалы, проверенные профильным специалистом, — завести свойство инфоблока со ссылкой на его карточку и вызвать setReviewedBy() в шаблоне детальной страницы. На страницах списка эти свойства смысла не имеют: они описывают страницу целиком, а не отдельный материал в перечне.
- Открыть любую страницу сайта с параметром ?microdata_debug=Y под администратором и убедиться, что рецензент попал в граф отдельным узлом.
Рич-результата reviewedBy не даёт ни у Google, ни у Яндекса — это сигнал для графа знаний и для оценки экспертности материала.
Полный список изменений — в файле CHANGELOG.md внутри модуля. Руководство — в docs/MANUAL.md. |
| 2.11.1 (15.09.2026) |
Что изменилось после версии 2.10.0.
Что нового
- Способы связи организации. Новая вкладка «Контакты» в настройках модуля. Таблица контактных точек: у каждой своё назначение, телефон, почта, ссылка и часы работы. В разметку уходит contactPoint — структурированный список контактов, в дополнение к обычным полям телефона и почты. Строки добавляются и удаляются кнопками; язык подставляется сам — тот, что настроен для текущего сайта.
- Телефон и почта организации переехали на эту же вкладку со вкладки «Организация». Ключи настроек и вывод при этом не изменились: это переезд по вкладкам, чтобы всё про контакты лежало рядом. Повторить в таблице тот же номер — нормально, это разные свойства schema.org, а не дубль.
- Переключатель сайтов в настройках стал вторым рядом вкладок.
Что изменится в разметке
Пересохранять настройки не нужно.
- Дни с одинаковым временем работы идут одним узлом OpeningHoursSpecification. Раньше выводился узел на каждый день: рабочая неделя с выходным в субботу давала шесть почти одинаковых узлов, теперь два. По schema.org dayOfWeek — это множество, а не диапазон, поэтому «Пн, Ср, Пт» собирается так же, как «Пн–Пт». Обе формы валидны, изменение в сторону лаконичности.
- Каталог сайта вошёл в @id организации, веб-сайта и автора. Это касается только тех, у кого несколько сайтов делят один домен и различаются каталогом: /, /en/, /cn/. Раньше все версии получали один идентификатор — https://site.ru/#organization, — хотя название, адрес и контакты у них разные, и один идентификатор описывал несколько разных сущностей. Теперь у английской версии это https://site.ru/en/#organization.
Если сайт один или языковые версии живут на разных доменах — не меняется ничего: каталог там равен /, и разметка остаётся прежней до байта.
Что исправлено
- Сообщение об ошибке в настройках стало видно. Форма сообщала о неверном формате времени и тут же делала переход на себя — сообщение не успевал увидеть никто, а администратор считал, что всё сохранено. Теперь при ошибке перехода нет, а введённое остаётся в форме и его достаточно поправить.
- Идиома setBrand('#organization') продолжает попадать в узел организации на сайтах в подпапках: якорь достраивается по каталогу сайта.
Для разработчиков
Если вы не обращаетесь к классам модуля из своего кода, этот раздел вас не касается.
- Organization::addContactPoint() принимает телефон необязательным: контактная точка может быть и одной ссылкой на мессенджер. Прежние вызовы работают как раньше.
- Появились Schema::getSiteDir() — каталог текущего сайта, и Schema::anchorId() — идентификатор узла, принадлежащего сайту целиком.
- Два новых служебных класса: Service\ContactPoints (разбор и вывод способов связи) и Service\OpeningHours (расписание в форме schema.org). Оба закрыты от наследования, как и остальные служебные классы модуля.
- Список способов связи хранится одной настройкой contact_points строкой JSON; читать его следует через ContactPoints::stored().
- Остальной публичный API не изменился.
Что сделать после обновления
- Заполнить вкладку «Контакты», если нужен contactPoint в разметке.
- Открыть любую страницу сайта с параметром ?microdata_debug=Y под администратором: панель покажет состояние графа и базовый URL вместе с каталогом сайта.
- Если языковые версии живут в подпапках одного домена — прогнать по одной странице каждой версии через Google Rich Results Test.
Полный список изменений — в файле CHANGELOG.md внутри модуля. Руководство — в docs/MANUAL.md. |
| 2.10.0 (02.09.2026) |
Внимание: модулю требуется PHP 8.2 или выше.
Поддержка PHP 7.4 и 8.0–8.1 снята. Перед обновлением проверьте версию PHP на сайте: Настройки → Инструменты → Проверка системы. На более старой версии модуль отключится и разметка со страниц пропадёт, а в журнале событий появится запись с причиной.
Обновление накопительное: включает версии 2.8.0, 2.9.0 и 2.10.0.
Что нового
- Новость (NewsArticle) получила собственные свойства. Фабрика Schema::newsArticle(), место и дата подготовки материала (dateline), подзаголовок, реквизиты печатного выпуска.
- Разметка материала за подпиской. Один вызов setPaywalledSection('.paywall') помечает закрытую часть так, как требует документация Google: без этого платный материал выглядит для поискового робота подменой контента.
- Автомаппер новостей принимает тип статьи аргументом — для новостного раздела можно указать NewsArticle явно.
Что изменится в разметке на сайте
Все изменения — в сторону валидности. Пересохранять настройки не нужно.
- Наличие товара выводится из остатков и настроек каталога, а не всегда InStock. Отсутствующий товар больше не помечается доступным.
- Узел, уже выведенный в графе, внутри других узлов подставляется ссылкой @id. Раньше товар выводился дважды — отдельным узлом и полной копией внутри страницы.
- На страницах ошибок (404 и любой код 4xx/5xx) разметка не выводится.
- og:url больше не выводится относительным адресом.
- У хлебных крошек убрано свойство isPartOf: Google выводил на него предупреждение.
- Оценка вне объявленной шкалы не выводится вовсе — такой отзыв Google отбрасывал целиком.
- Раздел или товар с названием «0» больше не выходит с пустым названием.
Что исправлено
- Разметка перестала пропадать целиком. Один невалидный байт в одном значении — например, описание в windows-1251 из выгрузки 1С — обнулял разметку всей страницы и не оставлял следов ни в HTML, ни в журнале. Теперь негодное значение стоит своего узла, а не страницы.
- Встроенный валидатор начал работать в основном сценарии. Он проверял только верхний уровень графа, а типовой вызов кладёт товар внутрь страницы: товар без цены и фото давал ноль замечаний, а панель показывала зелёное «всё на месте».
- Значения из свойств инфоблока больше не портят разметку: массив множественного свойства не превращается в слово «Array», а незаполненное свойство не роняет страницу.
- Хлебные крошки: последний пункт цепочки (текущая страница) больше не теряется.
- Цена со скидкой, выбранный тип цены и валюта берутся из каталога корректно.
- Файлы оформления раздела админки доезжают и при обновлении, а не только при установке: раньше после обновления админка получала 404 на стиль раздела.
- Пункт меню модуля виден только тем, у кого есть право на модуль.
- Безопасность вывода: текст ответов FAQ и отзывов собирается по белому списку тегов, ссылки проверяются по схеме, JSON-LD кодируется так, что из блока нельзя выйти.
Совместимость для разработчиков
Если вы не расширяли классы модуля в своём коде, этот раздел вас не касается.
- Все сеттеры сущностей возвращают static вместо self. Наследник, объявивший у переопределённого сеттера : self, перестанет загружаться.
- Девять служебных классов (Schema, Normalizer, Validator, JsonBuilder, AutoMapper, PageHandler, Debug, ModuleOptions, Assets) стали final с закрытым конструктором: наследовать их больше нельзя. Словарь типов SchemaType намеренно оставлен открытым.
- Публичный API в остальном не изменился: методы, существовавшие в 2.7.0, работают с прежними сигнатурами. Значения из шаблона — строки из свойств инфоблока, числа, пустые значения — принимаются как раньше.
Что сделать после обновления
- Открыть любую страницу сайта с параметром ?microdata_debug=Y под администратором: панель покажет состояние графа и подскажет, каких обязательных полей не хватает.
- Зайти в настройки модуля хотя бы раз — там догоняется регистрация обработчиков для сайтов, обновлённых копированием файлов.
- Прогнать одну товарную и одну новостную страницу через Google Rich Results Test.
Полный список изменений — в файле CHANGELOG.md внутри модуля. Руководство — в docs/MANUAL.md. |
| 2.7.0 (17.08.2026) |
Важное при обновлении с 2.2.x
Обновление не требует пересохранения настроек и не ломает публичный API: все методы, существовавшие ранее, работают с прежними сигнатурами. Но вывод разметки изменится — все изменения в сторону валидности:
- Появится поле inLanguage у WebSite, WebPage и статей — Раньше язык не выводился вообще
- В хлебных крошках добавится последний пункт — текущая страница — Раньше он молча терялся
- @id товара и статьи получат якорь #product / #article — Раньше @id товара совпадал с URL страницы
- ИНН переедет из vatID в taxID, ОГРН — в identifier — По schema.org taxID — это и есть ИНН
- publisher у WebSite исчезнет, если не заполнено название организации — Раньше ссылка вела на несуществующий узел
- og:locale пропадёт для языков, где регион неизвестен — Раньше выводилось невалидное значение вида sv_SV
- Товар без числовой цены останется без блока offers — Раньше в разметку уходил текст вместо числа
Что сделать после обновления:
- Открыть любую страницу с ?microdata_debug=Y под администратором — новая панель покажет состояние графа и подскажет, чего не хватает.
- Проверить блок «Базовый URL» в панели. Если сайт за обратным прокси и в разметке http:// — поставить base_scheme = https в настройках.
- Зайти в настройки модуля хотя бы раз: там догоняется миграция настроек и регистрация кнопки в админ-панели для сайтов, обновлённых копированием файлов.
- Прогнать одну товарную и одну новостную страницу через Google Rich Results Test.
- Если сайт за обратным прокси и в разметке http:// — включить опцию «Сайт за обратным прокси» либо задать base_scheme = https.
- Если логотип был в формате SVG — заменить на PNG или WebP.
2.7.0
Устранение замечаний аудита безопасности Маркетплейса.
- Разметка больше не может закрыть тег <script>. JSON-LD кодируется с флагами JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT: < и > превращаются в \u003C / \u003E. Раньше значение с </script> — из свойства инфоблока, отзыва или выгрузки 1С — закрывало тег, и браузер исполнял остаток строки как JavaScript на публичной странице. Для поисковых роботов JSON остаётся валидным, URL по-прежнему читаемы.
- Точка вставки страхуется отдельно: GraphRegistry::render() дополнительно заменяет </ на <\/.
- Ответы FAQ чистятся выборочно. Новый Normalizer::safeHtml() оставляет разметку, разрешённую Google для acceptedAnswer.text (заголовки, списки, ссылки, выделение), и удаляет script, iframe, form, svg, событийные атрибуты on* и схемы javascript: / vbscript: / data:. Вопрос теперь всегда очищается от тегов.
- Тело отзыва и название бренда нормализуются — Review::setBody() и HasBrand::setBrand() прогоняют текст через Normalizer::text(), как это давно делали setName() и setHeadline().
- Заголовок Host больше не принимается без белого списка. Раньше при незаполненных настройках он использовался как есть, и запрос с чужим Host мог отравить кеш страницы разметкой с чужим доменом. Теперь хост принимается только при совпадении с доверенным источником; в их число добавлен SERVER_NAME из конфигурации веб-сервера, чтобы типовые сайты не остались без разметки.
- Заголовки прокси — только по явной настройке. X-Forwarded-Proto и X-Forwarded-Ssl присылает клиент, и без прокси их может подделать кто угодно, навязав http:// в канонических ссылках. Появилась опция «Сайт за обратным прокси», по умолчанию выключенная.
- SVG исключён из форматов логотипа. Такой файл исполняет вложенный скрипт при открытии по прямой ссылке из /upload/. Для Organization.logo Google и так требует растр.
- Дебаг-панель предупреждает, если базовый URL не определён и разметка поэтому не выводится.
2.6.2
Предрелизный аудит. Исправлены ошибки, часть из которых существовала с ранних версий.
- Некорректная UTF-8 в данных инфоблока больше не приводит к потере текста. Раньше очистка строки возвращала пустое значение и писала deprecation в лог PHP: название и описание товара исчезали из разметки целиком. Битые байты типичны для выгрузок 1С и импортов.
- Цена проверяется на числовой формат. Нечисловое значение больше не попадает в offers.price — такой товар выводится без блока offers. Отформатированные строки вида 1 990,00 разбираются в число. Обычные числовые цены не изменились.
- Убрана висячая ссылка: WebSite.publisher указывал на организацию даже тогда, когда сама организация в граф не добавлялась (не заполнено название).
- Дебаг-панель больше не пишет в журнал событий. Раньше каждое открытие панели дублировало предупреждения валидатора в b_event_log.
- Устранена коллизия идентификаторов автора: автор с именем «123» получал тот же @id, что и пользователь с ID 123.
- Идентификатор автора больше не зависит от наличия расширения mbstring на сервере.
- Нераспознанный автор (например, ID несуществующего пользователя) больше не порождает пустой узел Person в разметке.
- Параметр ?microdata_debug, переданный массивом, не вызывает предупреждение PHP.
2.6.1
Мультиязычность.
- Поле inLanguage больше не достраивает регион самостоятельно. Раньше запасной вариант давал невалидные теги для многих языков: sv-SV вместо sv-SE, cs-CS вместо cs-CZ, da-DA вместо da-DK. По BCP 47 голое de — валидный тег, а угаданный регион был бы неверным для австрийского или швейцарского сайта.
- Новая опция «Язык контента» (language_tag) — задаёт тег явно: ru-RU, en-GB, de-AT, zh-Hans-CN. Хранится отдельно для каждого сайта, что и нужно мультиязычному порталу.
- Нормализация языковых тегов: pt_br → pt-BR, EN-gb → en-GB, поддержка письменности и макрорегионов (zh-Hans-CN, es-419).
- Идентификаторы языков Битрикса, которые на самом деле коды стран, переводятся корректно: ua → uk-UA, by → be-BY, kz → kk-KZ, br → pt-BR, la → es-419.
- og:locale вынесен в отдельную логику: там регион обязателен по спецификации Open Graph, поэтому подставляется по таблице из 50 языков. Если язык неизвестен, тег не выводится вовсе — это лучше невалидного значения.
2.6.0
Связывание сущностей, авторы, классификация, диагностика.
- Связывание организации. Поле «Профили и справочники (sameAs)» дополнено, добавлено поле «Темы компании» (knowsAbout). Позволяет привязать сайт к карточке в Wikidata и статье в Википедии, чтобы поисковик понимал, о какой именно компании речь. Пошаговая инструкция — в руководстве.
- Автор как связанная сущность. Новый метод Schema::author() создаёт автора отдельным узлом графа с постоянным @id и регистрирует его; статьи ссылаются на него по идентификатору, а не копируют данные на каждой странице. Принимает ID пользователя Битрикса (подтягивает ФИО, фото, должность), массив полей или имя. Добавлены Person::setWorksFor() и Person::setKnowsAbout().
- Поля классификации у статьи: setSection(), setKeywords(), setWordCount(), setWordCountFromText(), setTimeRequired(), setArticleBody(). Автоматически заполняются из $arResult: количество слов из DETAIL_TEXT, рубрика из раздела, ключевые слова из TAGS.
- Вкладка «Предупреждения» в настройках модуля: последние 50 записей валидатора из журнала событий с датой, текстом и адресом страницы.
- Новое событие OnBeforeMicrodataBuild — срабатывает до генерации Open Graph, поэтому обработчик может подменить сущность так, чтобы это отразилось и на мета-тегах.
- Удаление сущностей из графа: GraphRegistry::remove() по @id или по объекту и removeByType(). Раньше документация обещала такую возможность, но API её не давал.
- Автоматический inLanguage в WebSite, WebPage и статьях — берётся из языка сайта.
- Кнопка «Микроразметка» в админ-панели на публичной части: открывает текущую страницу с дебаг-панелью.
2.5.2
- Доступ к дебаг-панели ужесточён до администратора. Раньше учитывались также права на модуль через GetGroupRight, а уровень по умолчанию для модуля с ненастроенными правами зависит от версии и настроек портала.
- Страница с открытой панелью отдаётся с заголовком Cache-Control: no-store, чтобы не осесть в кеше и не уехать посетителю.
2.5.1
- Исправлен разбор свойств инфоблока. Списочные свойства читались из VALUE вместо VALUE_ENUM: если бренд оформлен списком, в разметку уходил числовой идентификатор вместо названия. HTML-свойства сплющивались в строку вида «Текст, HTML» — слово HTML попадало прямо в разметку.
- Поддержаны множественные свойства и вложенные HTML-значения.
2.5.0
Дебаг-панель.
- Параметр ?microdata_debug=Y на публичной странице под администратором показывает: состав графа с типами и @id, список незаполненных обязательных полей, разбор определения базового URL (схема, хост, доверенные хосты), значения основных настроек, готовый JSON-LD и ссылки на валидаторы Google и Яндекса.
- Панель работает и при выключенном модуле, и при пустом графе, и в ручном режиме вывода — то есть именно тогда, когда диагностика нужнее всего.
- Внешних стилей и скриптов не подключает, в REST-ответы не попадает, при любой внутренней ошибке молча отключается.
- Validator научился отдавать находки структурой, а не только писать в журнал.
2.4.0
Восемь новых типов Schema.org.
- VideoObject — Видео-сниппет, вкладка «Видео», главы ролика
- Recipe — Карточка рецепта: время, калорийность, рейтинг
- JobPosting — Блок вакансий Google Jobs
- Course — Карточка учебного курса
- QAPage — Блок «вопрос — ответ»
- SoftwareApplication — Рейтинг и цена приложения
- Service — Страницы услуг
- HowTo — Пошаговые инструкции
- Общий разбор продолжительности: методы принимают секунды, строку «01:30:00» или готовый ISO 8601.
- Стратегия Open Graph для видео: og:type = video.other, теги плеера, длительность в секундах.
- Валидатор проверяет обязательные поля всех новых типов.
- Константы VideoObject и Service присутствовали в словаре типов с ранних версий, но классов под них не было.
2.3.0
Исправления логики и приведение в порядок.
- Хлебные крошки перестали терять последний пункт. Битрикс отдаёт текущую страницу с пустой ссылкой, и она молча отбрасывалась — цепочка в разметке всегда была короче реальной.
- Повторный вызов setMainEntity() больше не затирает страницу. Раньше вторая сущность (например, от другого компонента) стирала первую вместе с её данными.
- Событие OnAfterMicrodataBuild работает в ручном режиме. Раньше в этом режиме обработчики срабатывали уже после вывода JSON, то есть впустую.
- Исправлен маппинг реквизитов: ИНН уходит в taxID (по schema.org это и есть ИНН), ОГРН — в identifier через PropertyValue. Ключи настроек сохранены.
- @id товара и статьи получили якоря #product и #article: раньше идентификатор товара совпадал с адресом страницы.
- og:locale для языков кроме русского больше не выводит невалидный голый код языка.
- Маски исключений сверяются и с путём без query-строки: маска /search/ теперь исключает и /search/?q=....
- Убраны короткие теги <? в форме настроек — она больше не зависит от директивы short_open_tag.
- Тексты валидатора вынесены в языковые файлы.
- Снят лишний слой файлового кеша расписания работы (данные и так кешируются ядром, а суточный TTL мог отдавать устаревшее расписание).
- Добавлены тесты без внешних зависимостей: php tests/run.php.
2.2.2
Критические исправления.
- Списки в JSON больше не превращаются в объекты. При очистке пустых значений в массивах оставались дырки в индексах, и json_encode отдавал {"0":...,"2":...} вместо [...]. Такие значения sameAs, image и itemListElement валидаторы Schema.org отклоняют.
- Корректное определение протокола за обратным прокси. За nginx, Cloudflare или балансировщиком весь граф уезжал на http:// при канонической https://. Добавлена опция «Протокол в абсолютных ссылках» (base_scheme) и учёт заголовков X-Forwarded-Proto / X-Forwarded-Ssl.
- Защита от подмены заголовка Host. Хост запроса попадает в разметку, только если совпадает с доверенным источником (org_url, server_name сайта, SITE_SERVER_NAME). Без проверки запрос с чужим Host мог отравить кеш страницы разметкой с чужим доменом.
- Починена миграция настроек при обновлении. Битрикс не вызывает CModule::DoUpdate(), поэтому перенос настроек в per-site записи не выполнялся. Добавлен шаблон install/updater.php и идемпотентный запуск при открытии формы настроек.
- Восстановлена совместимость с PHP 7.4. Одна конструкция требовала PHP 8.0: на 7.4 модуль устанавливался, но падал при первом обращении.
- Базовый URL вычисляется один раз за запрос вместо пересчёта при каждом обращении.
|