Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,55 @@
"securepay",
"usn_income",
"msp3tbank",
"bannerpro",
"bannerclick",
"bannerimpression",
"bannerproImpressionQueue",
"clickout",
"CrawlerDetect",
"dayparting",
"FunnelChart",
"httpbin",
"stats_compare",
"webhook",
"SSRF",
"HMAC",
"seed_bannerpro_demo",
"tagsMode",
"htmlent",
"formit",
"сидер",
"стабы",
"плейсхолдере",
"плейсхолдера",
"чекбоксы",
"взаимоисключимо",
"thismonth",
"lastmonth",
"thisyear",
"алиасы",
"эндпоинты",
"adposition",
"adpositions",
"byad",
"byhtml",
"sortby",
"sortdir",
"getclicks",
"getreferrers",
"numberfield",
"notempty",
"pagetitle",
"matomo",
"Matomo",
"Referer",
"антифрода",
"рефереры",
"Рефереры",
"чанком",
"таймингами",
"селл",
"кликает",
"msmc",
"nbrb",
"NBRB",
Expand All @@ -314,12 +363,15 @@
"плейсхолдеры",
"Плейсхолдер",
"Плейсхолдеры",
"плейсхолдером",
"плейсхолдерами",
"плейсхолдеров",
"чанк",
"чанки",
"Чанк",
"чанка",
"чанках",
"чанков",
"рендер",
"рендере",
"рендера",
Expand Down
128 changes: 128 additions & 0 deletions docs/components/bannerpro/analytics.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
---
title: Внешняя аналитика
description: "BannerProAnalytics, GA4, Matomo, Яндекс Метрика и события bannerpro:click / bannerpro:impression"
---

# Внешняя аналитика

BannerPro вызывает серверные события MODX и браузерные события для кликов и показов. Плагин **BannerProAnalytics** отправляет эти события в GA4, Matomo и Яндекс Метрику.

## Что включено в ядро

| Уровень | Событие | Когда срабатывает |
| --- | --- | --- |
| MODX | `OnBannerProClick` | Перед redirect после клика |
| MODX | `OnBannerProImpression` | После pixel-запроса показа |
| Browser | `bannerpro:click` | При клике по ссылке баннера, если загружен `analytics.js` |
| Browser | `bannerpro:impression` | При показе баннера, если включён `impression.js` |

Серверные события работают без `BannerProAnalytics`.

## Плагин BannerProAnalytics

Плагин выключен по умолчанию. Для включения:

1. Откройте **Элементы → Плагины**.
2. Включите **BannerProAnalytics**.
3. В системных настройках включите `bannerpro_analytics_enabled`.
4. Убедитесь, что на странице уже есть GA4/GTM, Matomo или Яндекс Метрика.

Плагин подключает `assets/components/bannerpro/js/analytics.js` на событии `OnLoadWebDocument`.

## Настройки

| Ключ | По умолчанию | Роль |
| --- | --- | --- |
| `bannerpro_analytics_enabled` | `false` | Подключает клиентский мост |
| `bannerpro_analytics_ga4` | `true` | Включает `dataLayer.push` |
| `bannerpro_analytics_ga4_click` | `bannerpro_click` | Имя события клика GA4 |
| `bannerpro_analytics_ga4_impression` | `bannerpro_impression` | Имя события показа GA4 |
| `bannerpro_analytics_matomo` | `true` | Включает `_paq.push(['trackEvent', ...])` |
| `bannerpro_analytics_ym_counter` | пусто | ID счётчика Метрики |
| `bannerpro_analytics_ym_click_goal` | `bannerpro_click` | Цель клика в Метрике |
| `bannerpro_analytics_ym_impression_goal` | `bannerpro_impression` | Цель показа в Метрике |

## Что отправляется

| Система | Клик | Показ |
| --- | --- | --- |
| GA4 / GTM | `dataLayer.push({ event: 'bannerpro_click', ... })` | `event: 'bannerpro_impression'` |
| Matomo | `_paq.push(['trackEvent', 'BannerPro', 'click', ...])` | `action = impression` |
| Яндекс Метрика | `ym(ID, 'reachGoal', 'bannerpro_click', params)` | `bannerpro_impression` |

Для GA4 через GTM создайте триггеры на события `bannerpro_click` и `bannerpro_impression`.

## Параметры события

| Ключ | Что содержит |
| --- | --- |
| `ad_id` | ID баннера |
| `ad_name` | Название |
| `ad_url` | URL перехода |
| `ad_type` | `image` или `html` |
| `product_id` | ID товара MiniShop3 или `0` |
| `position` | ID позиции |
| `position_name` | Имя позиции |
| `adposition` | ID связи баннер + позиция |
| `ip` | IP посетителя, только сервер |
| `recorded` | Запись создана в БД |
| `duplicate` | Дубликат за сутки |
| `referrer` | HTTP Referer, только клик |
| `redirect_url` | URL после обработки чанка, только клик |

## Свой браузерный обработчик

```javascript
document.addEventListener('bannerpro:impression', function (event) {
console.log(event.detail)
})
```

Если `analytics.js` загрузился после `impression.js`, показы попадают в `window.bannerproImpressionQueue` и обрабатываются при инициализации моста без двойного учёта в GA4/Matomo/Метрике.

## QA без реальных счётчиков

Демо-страница: ресурс `bannerpro-test.html`, шаблон `core/elements/templates/demo/bannerpro_test.tpl`, секция **19**.

Подготовка из корня MODX:

```bash
php core/elements/demo/seed_bannerpro_demo.php
php clear_cache.php
```

Сидер включает `bannerpro_analytics_enabled=1`, плагин **BannerProAnalytics**, демо-позиции и баннеры.

На стенде подключаются `analytics-mock.js` (стабы `dataLayer`, `_paq`, `ym()`) и `analytics.js`. Кнопки **Симулировать показ/клик** вызывают `bannerproAnalytics.trackImpression/Click`.

::: warning
На демо-странице шаблон может подключать `analytics.js` даже при выключенном плагине (пометка «стенд»). На продакшене используйте только плагин + `bannerpro_analytics_enabled`.
:::

### Ловушка Fenom `{extends}`

Если демо-шаблон наследует layout через `{extends 'layout.tpl'}`, убедитесь, что блок с баннерами не перекрывается родительским шаблоном. Иначе события аналитики не сработают на видимых баннерах.

## Свой серверный плагин

Создайте плагин MODX и подпишите его на `OnBannerProClick` или `OnBannerProImpression`.

```php
if ($modx->event->name === 'OnBannerProClick') {
$modx->log(modX::LOG_LEVEL_INFO, 'Banner click: ' . $scriptProperties['ad_name']);
}
```

## Webhook как альтернатива

Для внешней аналитики без клиентских счётчиков настройте [webhook при клике и показе](settings#webhook-при-клике). Сервер отправит POST JSON на ваш endpoint.

## Диагностика

| Симптом | Что проверить |
| --- | --- |
| `analytics.js` не подключился | Плагин `BannerProAnalytics`, `bannerpro_analytics_enabled`, контекст `web` |
| GA4 не получает событие | На странице есть `dataLayer`, имя события совпадает с GTM-триггером |
| Matomo не получает событие | На странице есть `_paq` |
| Метрика не получает цель | `bannerpro_analytics_ym_counter` и цель JavaScript-события |
| Показы не приходят | `bannerpro_track_impressions`, `BannerProImpression`, видимость баннера в viewport |
123 changes: 123 additions & 0 deletions docs/components/bannerpro/development/events.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
---
title: События MODX
description: "События BannerPro для разработчиков: OnBannerProClick, OnBannerProImpression и connector actions"
---

# События MODX

BannerPro вызывает события после клика и после фиксации показа. Используйте их для своей аналитики, CRM, антифрода или логирования.

## События

| Событие | Где вызывается | Когда |
| --- | --- | --- |
| `OnBannerProClick` | `bannerpro_invoke_click_event()` | После обработки клика, перед redirect |
| `OnBannerProImpression` | `bannerpro_invoke_impression_event()` | После pixel-запроса показа |

Код событий лежит в `core/components/bannerpro/include/events.php`.

## Payload

| Ключ | Тип | Описание |
| --- | --- | --- |
| `ad` | `byAd\|null` | Объект баннера |
| `ad_id` | int | ID баннера |
| `ad_name` | string | Название |
| `ad_url` | string | URL перехода |
| `ad_type` | string | `image` или `html` |
| `product_id` | int | ID товара MiniShop3 или `0` |
| `position` | int | ID позиции |
| `position_name` | string | Имя позиции |
| `adposition` | int | ID связи `bannerpro_ads_positions` |
| `ip` | string | IP посетителя |
| `recorded` | bool | Запись создана в таблице статистики |
| `duplicate` | bool | Событие повторилось в тот же день |
| `referrer` | string | HTTP Referer, только для клика |
| `redirect_url` | string | URL после обработки чанка, только для клика |

## Пример плагина

```php
<?php

if ($modx->event->name !== 'OnBannerProClick') {
return;
}

$adName = (string) ($scriptProperties['ad_name'] ?? '');
$positionName = (string) ($scriptProperties['position_name'] ?? '');

$modx->log(
modX::LOG_LEVEL_INFO,
'BannerPro click: ' . $adName . ' / ' . $positionName
);
```

Подпишите плагин на `OnBannerProClick`.

## HTTP webhook

Параллельно с MODX-событиями компонент может отправлять POST JSON на внешний URL. Настройки: [Системные настройки](../settings#webhook-при-клике).

| Тип | Настройка URL | Поле `event` |
| --- | --- | --- |
| Клик | `bannerpro_webhook_url` | `click` |
| Показ | `bannerpro_webhook_impression_url` | `impression` |

Подпись: заголовок `X-BannerPro-Signature` (HMAC-SHA256 от тела), если задан `bannerpro_webhook_secret`.

Компонент шлёт webhook клика после `OnBannerProClick`, до редиректа. Timeout 2 с, ошибки не блокируют переход. Webhook показа: fire-and-forget после `OnBannerProImpression`, ответ `204` не ждёт завершения запроса.

URL проверяется на SSRF: запрещены `localhost`, loopback и private/reserved IP в hostname.

### Payload клика

`event`, `ad_id`, `position_id`, `adposition`, `referrer`, `ip`, `click_id`, `timestamp` (ISO8601), `redirect_url`, `recorded`, `duplicate`.

### Payload показа

`event`, `ad_id`, `position_id`, `adposition`, `ip`, `timestamp` (ISO8601), `recorded`, `duplicate`.

## Клиентские события

`impression.js` отправляет `bannerpro:impression`. `analytics.js` перехватывает клики и может отправлять `bannerpro:click`.

```javascript
document.addEventListener('bannerpro:impression', function (event) {
console.log(event.detail.adposition)
})
```

## Connector actions

Админка обращается к `assets/components/bannerpro/connector.php` методом `POST`. Параметр `action` выбирает обработчик.

| Группа | Actions |
| --- | --- |
| Баннеры | `ads_getlist`, `ads_get`, `ads_create`, `ads_update`, `ads_remove`, `ads_enable`, `ads_disable`, `ads_getclicks`, `ads_duplicate`, `ads_bulk_enable`, `ads_bulk_disable`, `ads_bulk_remove`, `ads_bulk_assign_positions`, `ads_create_from_template` |
| Шаблоны | `ad_templates_getlist` |
| Позиции | `positions_getlist`, `positions_create`, `positions_update`, `positions_remove`, `positions_duplicate` |
| Связи | `adpositions_getlist`, `adpositions_add`, `adpositions_remove`, `adpositions_sort`, `adpositions_update_weight` |
| Статистика | `stats_summary`, `stats_by_day`, `stats_by_weekday`, `stats_top_ads`, `stats_export`, `stats_purge`, `stats_compare`, `clicks_getreferrers` |
| Журнал | `audit_getlist` |
| Метки | `tags_suggest` |
| Настройки | `settings_integrations_get`, `settings_integrations_update` |
| MiniShop3 | `resource_getlist` |

Connector требует сессию `mgr`. Мутации проверяют `bannerpro_save`, `bannerpro_remove` или `bannerpro_stats`.

## Модель данных

| Класс xPDO | Таблица |
| --- | --- |
| `byAd` | `bannerpro_ads` |
| `byPosition` | `bannerpro_positions` |
| `byAdPosition` | `bannerpro_ads_positions` |
| `byClick` | `bannerpro_clicks` |
| `byImpression` | `bannerpro_impressions` |
| `byAdTemplate` | `bannerpro_ad_templates` |
| `byAudit` | `bannerpro_audit` |

Поле `byAdPosition.id` попадает в плейсхолдер `adposition` и используется в ссылке клика.

Поля баннера `byAd`: `category_id`, `max_clicks`, `max_impressions`, `show_hours` (JSON), `target_resource_id`, `target_parent_id`, `tags` (JSON). Позиция `byPosition`: `context_key`.
Loading