CMS WebSource: development

Актуальная версия CMS: 0.973.

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

Документы со статусом deprecated описывают устаревший API. Документы со статусом removed описывают удалённый API, который нельзя рекомендовать для нового кода.

Создание темы

Путь: development/themes/creating-theme

Версия документа: 0.973

Статус: stable

Открыть документ

---
title: Создание темы
slug: creating-theme
doc-id: creating-theme
lang: ru
version: v0.95
section: development
type: themes
status: stable
order: 10
description: Создание минимальной рабочей темы для CMS WebSource.
---

# Создание темы

Тема CMS WebSource определяет HTML-структуру сайта, шаблоны страниц и записей, стили, JavaScript и локализацию.

В этом руководстве создадим минимальную рабочую тему и подключим её через административную панель.

## Структура темы

Темы хранятся в папке:

```text
resource/themes
```

Создайте отдельную папку темы:

```text
resource/themes/example
```

Рекомендуемая начальная структура:

```text
example/
  assets/
    css/
      style.css
    js/
      script.js
  includes/
    header.php
    footer.php
  template/
    page/
      default.php
    post/
    404.php
  index.php
  theme-info.json
```

Для распознавания темы обязательны только:

- `theme-info.json`
- `index.php`

Остальные файлы добавляются в зависимости от устройства сайта.

Название папки может содержать латинские буквы, цифры, `_` и `-`.

## Описание темы

Создайте файл `theme-info.json` в корне темы:

```json
{
    "theme-name": "Example",
    "theme-author": "Author",
    "theme-version": "1.0.0"
}
```

Все три поля обязательны:

| Поле | Описание |
|---|---|
| `theme-name` | Отображаемое название темы. |
| `theme-author` | Автор темы. |
| `theme-version` | Версия темы. |

Версия добавляется к URL ресурсов темы и помогает браузеру обновлять CSS, JavaScript и изображения после изменения файлов.

## Общий header

Создайте файл `includes/header.php`:

```php
<!DOCTYPE html>
<html lang="<?php echo CurrentLanguage::htmlLang(); ?>">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <?php
    echo PageData::getSeoHead();
    echo PageData::getFaviconHead();
    echo CurrentTheme::getCSS();
    ?>
</head>
<body>
    <header>
        <a href="<?php echo CurrentLanguage::url('/'); ?>">Главная</a>
    </header>
```

`CurrentTheme::getCSS()` автоматически сформирует подключения существующих файлов стилей темы.

## Общий footer

Создайте файл `includes/footer.php`:

```php
    <footer>
        <p>Example</p>
    </footer>
    <?php echo CurrentTheme::getJS(); ?>
</body>
</html>
```

`CurrentTheme::getJS()` подключает JavaScript темы и создаёт объект `window.wsSite` с данными текущей страницы и окружения сайта.

## Главная страница

Корневой файл `index.php` используется для главной страницы сайта:

```php
<?php include CurrentTheme::header(); ?>

<main>
    <h1><?php echo PageData::getTitle(); ?></h1>
    <div><?php echo PageData::getContent(); ?></div>
</main>

<?php include CurrentTheme::footer(); ?>
```

CMS самостоятельно выбирает этот файл для главной страницы.

## Шаблон обычной страницы

Создайте файл `template/page/default.php`:

```php
<?php include CurrentTheme::header(); ?>

<main>
    <h1><?php echo PageData::getTitle(); ?></h1>
    <div><?php echo PageData::getContent(); ?></div>
</main>

<?php include CurrentTheme::footer(); ?>
```

После активации темы шаблон `default` появится в списке шаблонов страницы в административной панели.

Имя файла становится именем шаблона:

```text
template/page/default.php -> default
```

## Шаблоны записей

Шаблоны записей хранятся отдельно:

```text
template/post/{template}.php
```

Например:

```text
template/post/news.php
```

Такой файл можно назначить типу записей через административную панель.

Внутри шаблона доступны данные текущей записи:

```php
<?php include CurrentTheme::header(); ?>

<main>
    <article>
        <h1><?php echo PageData::getTitle(); ?></h1>
        <div><?php echo PageData::getContent(); ?></div>
    </article>
</main>

<?php include CurrentTheme::footer(); ?>
```

## Страница ошибки

Для собственной страницы ошибки создайте `template/404.php`:

```php
<?php include CurrentTheme::header(); ?>

<main>
    <h1>404</h1>
    <p>Страница не найдена.</p>
</main>

<?php include CurrentTheme::footer(); ?>
```

Если файл отсутствует, CMS использует системную страницу ошибки.

## Стили

Основные стили разместите в файле:

```text
assets/css/style.css
```

Он автоматически подключается при вызове:

```php
echo CurrentTheme::getCSS();
```

Дополнительно CMS подключает файл, совпадающий с текущим шаблоном:

```text
assets/css/index.css
assets/css/default.css
assets/css/news.css
```

`index.css` используется на главной странице, остальные файлы подключаются для соответствующих шаблонов.

Отсутствующие файлы просто пропускаются.

## JavaScript

Основной JavaScript разместите в файле:

```text
assets/js/script.js
```

Он автоматически подключается при вызове:

```php
echo CurrentTheme::getJS();
```

Для ES-модулей можно использовать:

```text
assets/js/modules/script.js
```

Также поддерживаются файлы текущего шаблона:

```text
assets/js/default.js
assets/js/modules/default.js
```

Файлы из папки `modules` подключаются с атрибутом `type="module"`.

## Изображения и другие ресурсы

Получайте URL ресурсов через `CurrentTheme::assetUrl()`:

```php
$logo = CurrentTheme::assetUrl(
    'images',
    'logo.svg'
);
```

Использование в HTML:

```php
<img src="<?php echo CurrentTheme::assetUrl('images', 'logo.svg'); ?>" alt="Логотип">
```

Метод проверяет существование файла и добавляет версию темы к URL.

Папку `assets` передавать не нужно.

## Локализация

Переводы темы хранятся в папке `lang`:

```text
lang/
  ru/
    common.php
  en/
    common.php
```

Пример `lang/ru/common.php`:

```php
<?php
return [
    'main' => 'Главная',
    'contacts' => 'Контакты',
];
```

Получение перевода:

```php
echo SiteLang::html('main', 'Главная');
```

Для каждого языка используется его URL-префикс.

## Настройки темы

Файл `theme-settings.json` является необязательным.

Если он отсутствует, тема всё равно может быть активирована. Добавляйте его только тогда, когда пользователю действительно нужны изменяемые настройки темы.

Значения настроек доступны через:

```php
$logo = CurrentTheme::data('logo');
```

Настройки темы лучше вынести в отдельное руководство, поскольку их структура зависит от типов полей административной панели.

## Активация темы

После создания файлов:

1. Откройте раздел тем в административной панели.
2. Выберите тему `Example`.
3. Сохраните настройки.
4. Откройте главную страницу сайта.

CMS покажет тему в списке только как непосредственную папку внутри `resource/themes`.

Тема не будет активирована, если:

- отсутствует `theme-info.json`
- manifest содержит некорректный JSON
- отсутствует одно из обязательных полей manifest
- отсутствует или недоступен `index.php`
- папка темы находится вне `resource/themes`

## Проверка результата

После активации проверьте:

- открывается ли главная страница
- выводятся ли header и footer
- подключился ли `assets/css/style.css`
- подключился ли `assets/js/script.js`
- появился ли шаблон `default` при редактировании страницы
- открывается ли страница с назначенным шаблоном
- отображается ли собственная страница `404`
- изменяется ли параметр `?v=` у ресурсов после обновления версии темы

Локализация темы

Путь: development/themes/theme-localization

Версия документа: 0.973

Статус: stable

Открыть документ

---
title: Локализация темы
slug: theme-localization
doc-id: theme-localization
lang: ru
version: v0.95
section: development
type: themes
status: stable
order: 30
description: Организация переводов темы и получение локализованных строк через SiteLang.
---

# Локализация темы

Локализация темы хранится в PHP-файлах внутри папки `lang`. CMS загружает общий файл переводов и дополнительный файл, соответствующий текущему шаблону.

Для получения строк в шаблонах используется статический класс `SiteLang`.

## Структура папок

Языковые файлы хранятся внутри активной темы:

```text
example/
  lang/
    ru/
      common.php
      index.php
      404.php
      page/
        default.php
      post/
        news.php
    en/
      common.php
      index.php
      404.php
      page/
        default.php
      post/
        news.php
```

Название языковой папки должно совпадать с URL-префиксом языка.

Например:

```text
prefix: ru -> lang/ru
prefix: en -> lang/en
```

Код языка и его URL-префикс могут отличаться. Для названия папки используется именно префикс.

Префикс записывается строчными латинскими буквами и может содержать цифры или `-`.

## Общие переводы

Файл `common.php` загружается на всех обычных страницах сайта:

```text
lang/ru/common.php
```

Пример:

```php
<?php
return [
    'main' => 'Главная',
    'contacts' => 'Контакты',
    'copyright' => 'Все права защищены.',
];
```

Получение значения:

```php
$title = SiteLang::get('main');
```

Общие переводы подходят для:

- header и footer
- главного меню
- общих кнопок
- контактных данных
- повторяющихся подписей
- сообщений, используемых в разных шаблонах

## Вложенные ключи

Переводы можно объединять во вложенные массивы:

```php
<?php
return [
    'navigation' => [
        'main' => 'Главная',
        'catalog' => 'Каталог',
        'contacts' => 'Контакты',
    ],
    'buttons' => [
        'send' => 'Отправить',
        'read-more' => 'Читать далее',
    ],
];
```

Путь к значению записывается через точку:

```php
$catalog = SiteLang::get('navigation.catalog');
$send = SiteLang::get('buttons.send');
```

`SiteLang::get()` возвращает только скалярное значение. Если путь указывает на массив, используется резервное значение.

## Контекстные переводы

Кроме `common.php`, CMS загружает файл текущего контекста.

| Контекст | Файл |
|---|---|
| Главная страница | `lang/{prefix}/index.php` |
| Страница ошибки | `lang/{prefix}/404.php` |
| Шаблон страницы | `lang/{prefix}/page/{template}.php` |
| Шаблон записи | `lang/{prefix}/post/{template}.php` |

Контекстный файл дополняет общие переводы и может переопределять их.

## Главная страница

Для главной страницы используется:

```text
lang/ru/index.php
```

Пример:

```php
<?php
return [
    'hero' => [
        'title' => 'Добро пожаловать',
        'description' => 'Описание главной страницы.',
    ],
];
```

Использование в `index.php` темы:

```php
<section class="hero">
    <h1><?php echo SiteLang::html('hero.title'); ?></h1>
    <p><?php echo SiteLang::html('hero.description'); ?></p>
</section>
```

## Шаблон страницы

Для файла шаблона:

```text
template/page/default.php
```

используется файл переводов:

```text
lang/ru/page/default.php
```

Пример:

```php
<?php
return [
    'title' => 'О странице',
    'back' => 'Вернуться назад',
];
```

Использование в шаблоне:

```php
<h1><?php echo SiteLang::html('title'); ?></h1>

<a href="<?php echo CurrentLanguage::url('/'); ?>">
    <?php echo SiteLang::html('back'); ?>
</a>
```

Имя языкового файла определяется именем шаблона, а не URL страницы.

Несколько страниц с шаблоном `default` используют один контекстный файл переводов.

## Шаблон записи

Для файла:

```text
template/post/news.php
```

используется:

```text
lang/ru/post/news.php
```

Пример:

```php
<?php
return [
    'published' => 'Опубликовано',
    'back-to-list' => 'Вернуться к новостям',
];
```

Использование:

```php
<p><?php echo SiteLang::html('published'); ?></p>

<a href="<?php echo CurrentLanguage::url('/news'); ?>">
    <?php echo SiteLang::html('back-to-list'); ?>
</a>
```

## Страница ошибки

Переводы страницы ошибки хранятся в:

```text
lang/ru/404.php
```

Пример:

```php
<?php
return [
    'title' => 'Страница не найдена',
    'description' => 'Проверьте адрес или вернитесь на главную страницу.',
    'home' => 'На главную',
];
```

Использование в `template/404.php`:

```php
<h1><?php echo SiteLang::html('title'); ?></h1>
<p><?php echo SiteLang::html('description'); ?></p>

<a href="<?php echo CurrentLanguage::url('/'); ?>">
    <?php echo SiteLang::html('home'); ?>
</a>
```

## Язык по умолчанию

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

Допустим, русский язык является языком по умолчанию:

```php
<?php
return [
    'main' => 'Главная',
    'contacts' => 'Контакты',
    'catalog' => 'Каталог',
];
```

А английский файл содержит только часть переводов:

```php
<?php
return [
    'main' => 'Home',
    'catalog' => 'Catalog',
];
```

Для английской версии сайта:

- `main` вернёт `Home`
- `catalog` вернёт `Catalog`
- `contacts` вернёт `Контакты`

Отсутствующий перевод сохраняется из языка по умолчанию.

## Порядок объединения

Файлы объединяются в следующем порядке:

1. `common.php` языка по умолчанию.
2. `common.php` текущего языка.
3. Контекстный файл языка по умолчанию.
4. Контекстный файл текущего языка.

Каждый следующий файл дополняет предыдущие данные и переопределяет совпадающие ключи.

Вложенные массивы объединяются рекурсивно.

Это позволяет:

- хранить основной набор переводов в языке по умолчанию
- переводить только необходимые значения
- задавать общие строки в `common.php`
- переопределять строки для конкретного шаблона

## Получение строки

### get(string $path, string $default = ''): string

Возвращает строку без дополнительной обработки:

```php
$title = SiteLang::get('page.title');
```

Резервное значение передаётся вторым аргументом:

```php
$title = SiteLang::get(
    'page.title',
    'Заголовок страницы'
);
```

Резервное значение возвращается, если:

- ключ отсутствует
- путь указывает на массив
- значение не является скалярным

## Вывод текста в HTML

### html(string $path, string $default = ''): string

Экранирует специальные HTML-символы:

```php
<h1><?php echo SiteLang::html('page.title'); ?></h1>
```

Резервное значение также экранируется:

```php
<p>
    <?php echo SiteLang::html('page.description', 'Описание отсутствует'); ?>
</p>
```

Используйте `html()` для обычного текста, который выводится в HTML.

HTML-теги внутри перевода будут показаны как текст:

```php
<?php
return [
    'message' => '<strong>Важное сообщение</strong>',
];
```

При выводе через `html()` браузер не обработает тег `<strong>`.

## Получение исходного значения

`get()` не экранирует результат:

```php
$value = SiteLang::get('message');
```

Используйте его, когда значение:

- не выводится напрямую в HTML
- передаётся в другую функцию
- сравнивается в PHP
- обрабатывается отдельно перед выводом

## Все загруженные переводы

Получить весь объединённый массив можно через:

```php
$translations = SiteLang::all();
```

Массив содержит:

- общие переводы
- переводы текущего контекста
- значения языка по умолчанию
- переопределения текущего языка

## Добавление нового языка

Перед созданием файлов язык должен быть настроен и активирован в CMS.

После этого:

1. Узнайте URL-префикс языка.
2. Создайте папку `lang/{prefix}`.
3. Добавьте `common.php`.
4. Добавьте необходимые контекстные файлы.
5. Откройте страницу с префиксом нового языка.
6. Проверьте переводы и резервные значения.

Например, для префикса `de`:

```text
lang/de/common.php
lang/de/index.php
lang/de/page/default.php
```

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

## Переводы и URL

`SiteLang` отвечает только за текстовые значения.

Для формирования внутренних ссылок текущего языка используйте:

```php
$url = CurrentLanguage::url('/contacts');
```

Пример:

```php
<a href="<?php echo CurrentLanguage::url('/contacts'); ?>">
    <?php echo SiteLang::html('navigation.contacts', 'Контакты'); ?>
</a>
```

## Важные замечания

- каждый языковой файл должен возвращать массив
- папка языка называется по URL-префиксу
- контекстный файл называется по шаблону
- общие строки следует хранить в `common.php`
- контекстные строки следует хранить рядом с соответствующим шаблоном
- язык по умолчанию должен содержать основной набор переводов
- отсутствующий файл текущего языка не мешает использовать переводы языка по умолчанию
- `theme-settings.json` использует собственные названия полей и не загружается через `SiteLang`

Настройки темы

Путь: development/themes/theme-settings

Версия документа: 0.973

Статус: stable

Открыть документ

---
title: Настройки темы
slug: theme-settings
doc-id: theme-settings
lang: ru
version: v0.95
section: development
type: themes
status: stable
order: 20
description: Создание изменяемых настроек темы через theme-settings.json.
---

# Настройки темы

Файл `theme-settings.json` описывает настройки, которые пользователь может изменять через административную панель.

Сохранённые значения доступны в шаблонах через статический класс `CurrentTheme`.

## Расположение файла

Разместите файл в корне темы рядом с `theme-info.json`:

```text
example/
  index.php
  theme-info.json
  theme-settings.json
```

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

## Минимальный пример

```json
{
    "general": {
        "logo": {
            "type": "image",
            "name": {
                "ru": "Логотип"
            },
            "default": ""
        }
    }
}
```

После выбора темы поле `logo` появится в её настройках.

Получение сохранённого значения:

```php
$logo = CurrentTheme::data('logo');
```

## Группы настроек

На верхнем уровне поддерживаются две группы:

| Группа | Назначение |
|---|---|
| `general` | Основные настройки темы. |
| `variables` | Дополнительные переменные шаблона. |

Обе группы необязательны:

```json
{
    "general": {},
    "variables": {}
}
```

Другие группы не поддерживаются и делают файл настроек некорректным.

В PHP значения обеих групп получаются одинаково через `CurrentTheme::data()`.

## Структура поля

Каждое поле записывается под уникальным ключом:

```json
{
    "general": {
        "accent-color": {
            "type": "color",
            "name": {
                "ru": "Основной цвет",
                "en": "Accent color"
            },
            "default": "#000000"
        }
    }
}
```

Поле содержит:

| Параметр | Описание |
|---|---|
| `type` | Тип элемента административной формы. |
| `name` | Локализованные названия поля. |
| `default` | Значение по умолчанию. |

Ключ поля может содержать:

- латинские буквы
- цифры
- `_`
- `-`

Один ключ нельзя повторять в разных группах.

## Названия полей

Параметр `name` должен быть массивом переводов:

```json
{
    "name": {
        "ru": "Логотип",
        "en": "Logo"
    }
}
```

Название на русском языке обязательно.

Код языка записывается строчными латинскими буквами и может содержать цифры или `-`.

Если перевода для текущего языка административной панели нет, используется русское название.

## Поддерживаемые типы

| Тип | Значение |
|---|---|
| `input` | Однострочный текст. |
| `textarea` | Многострочный текст. |
| `editor` | Содержимое визуального редактора. |
| `checkbox` | Логическое значение. |
| `multi` | Массив повторяющихся значений. |
| `table` | Массив табличных данных. |
| `image` | Значение поля выбора изображения. |
| `video` | Значение поля выбора видео. |
| `file-all` | Значение поля выбора файла. |
| `color` | Строковое значение цвета. |

Неизвестный тип делает весь `theme-settings.json` некорректным.

## Значения по умолчанию

Тип значения `default` зависит от типа поля.

### Строковые поля

Для текстовых полей, файлов и цвета используется строка:

```json
{
    "type": "input",
    "name": {
        "ru": "Телефон"
    },
    "default": ""
}
```

Скалярные значения приводятся к строке. Логическое значение для строкового поля превращается в пустую строку.

### Checkbox

Для `checkbox` используется `true` или `false`:

```json
{
    "type": "checkbox",
    "name": {
        "ru": "Показывать контакты"
    },
    "default": true
}
```

Другой тип значения будет заменён на `false`.

### Multi и table

Для `multi` и `table` используется массив:

```json
{
    "type": "multi",
    "name": {
        "ru": "Социальные сети"
    },
    "default": []
}
```

Другой тип значения будет заменён на пустой массив.

## Полный пример

```json
{
    "general": {
        "logo": {
            "type": "image",
            "name": {
                "ru": "Логотип",
                "en": "Logo"
            },
            "default": ""
        },
        "accent-color": {
            "type": "color",
            "name": {
                "ru": "Основной цвет",
                "en": "Accent color"
            },
            "default": "#1b1b1b"
        },
        "show-contacts": {
            "type": "checkbox",
            "name": {
                "ru": "Показывать контакты",
                "en": "Show contacts"
            },
            "default": true
        }
    },
    "variables": {
        "phone": {
            "type": "input",
            "name": {
                "ru": "Телефон",
                "en": "Phone"
            },
            "default": ""
        },
        "legal": {
            "type": "textarea",
            "name": {
                "ru": "Реквизиты",
                "en": "Legal information"
            },
            "default": ""
        }
    }
}
```

## Получение значений

### Строка

```php
$phone = CurrentTheme::data('phone');
```

### Значение по умолчанию

Второй аргумент используется, если ключ отсутствует:

```php
$phone = CurrentTheme::data(
    'phone',
    'Телефон не указан'
);
```

### Checkbox

```php
if (CurrentTheme::data('show-contacts', true)) {
    ?>
    <div class="contacts">
        <?php echo CurrentTheme::data('phone'); ?>
    </div>
    <?php
}
```

### Массив

```php
$items = CurrentTheme::data('social-links', []);

foreach ($items as $item) {
    // Вывод элемента.
}
```

Форма массива зависит от настройки поля в административной панели.

## Использование изображения

```php
$logo = CurrentTheme::data('logo');

if ($logo !== '') {
    ?>
    <img src="<?php echo $logo; ?>" alt="Логотип">
    <?php
}
```

`CurrentTheme::data()` возвращает сохранённое значение. Метод не формирует URL ресурса и не изменяет его.

## Сохранение настроек

После создания или изменения `theme-settings.json`:

1. Откройте раздел тем в административной панели.
2. Выберите нужную тему.
3. Заполните появившиеся поля.
4. Сохраните настройки.
5. Проверьте значения в шаблоне темы.

Значения сохраняются отдельно для каждой папки темы.

Если сохранённого значения нет, используется `default` из `theme-settings.json`.

Если поле удалено из manifest, его сохранённое значение больше не попадает в данные активной темы.

## Проверка файла

Настройки не будут сохранены, если:

- JSON содержит синтаксическую ошибку
- файл находится не в корне темы
- используется неизвестная группа
- ключ поля имеет недопустимый формат
- ключ повторяется в группах
- отсутствует поддерживаемый `type`
- отсутствует русское название поля
- название поля пустое

Некорректный файл не делает `theme-info.json` недействительным, но CMS не загружает значения настроек и не позволяет сохранить настройки выбранной темы.

## Проверка результата

После сохранения проверьте:

- появились ли поля в нужных разделах административной панели
- сохраняются ли строковые значения
- возвращает ли `checkbox` логическое значение
- возвращают ли `multi` и `table` массивы
- применяются ли значения `default`
- возвращает ли второй аргумент `CurrentTheme::data()` резервное значение