Инструкции | Сегодня, 15:04 8
Версия: 1.0.0 (2026-06-17)
Лицензия: MIT
Репозиторий: github.com/tcse/Email2Post
Демо: email2post.tcse-cms.com
Email2Post — это PHP-скрипт, который подключается к вашему почтовому ящику через IMAP и автоматически публикует полученные письма как посты в блоге.
Скрипт не требует базы данных, работает на чистом PHP и хранит все данные в JSON-файлах. Идеальное решение для контент-проектов, новостных рассылок и личных блогов.
php-imap)php -m | grep imap в терминале,
чтобы убедиться, что расширение установлено.
git clone https://github.com/tcse/Email2Post.git
cd Email2Post
Или скачайте ZIP-архив.
Загрузите содержимое папки в любую директорию на вашем сервере, например:
/blog/ — для отдельного блога/plugins/tcse/email2post/ — если используете как подсистемуdata/ должна быть доступна для записи (755 или 775)data/ защищена от прямого доступа из браузера.
В файле .htaccess уже прописаны соответствующие правила.
Файл конфигурации находится по пути: data/config.php
<?php
return [
// Базовый URL сайта
'base_url' => 'https://ваш-сайт.ru/blog/',
// Настройки email (IMAP)
'email' => [
'enabled' => true,
'imap' => [
'host' => '{imap.ваш-провайдер.ru:993/imap/ssl}INBOX',
'username' => 'blog@ваш-сайт.ru',
'password' => 'ВАШ_ПАРОЛЬ',
],
// Пароли для публикации
'publish_passwords' => [
'мойПароль123' => 'Имя Автора',
],
'moderation' => [
'admin_email' => 'admin@ваш-сайт.ru',
],
],
// Настройки сайта
'site' => [
'title' => 'Мой блог из Email',
'description' => 'Публикации из email-писем',
],
];
| Секция | Ключ | Описание |
|---|---|---|
base_url |
— | Базовый URL сайта (обязательно) |
debug |
true/false — вывод ошибок |
|
email |
enabled |
true/false |
imap.host |
Строка подключения IMAP | |
imap.username |
Логин почтового ящика | |
imap.password |
Пароль почтового ящика | |
publish_passwords |
Массив паролей для публикации | |
site |
title |
Название сайта |
description |
Описание сайта | |
blog |
posts_per_page |
Количество постов на странице (по умолч. 12) |
parsers |
default |
auto, markdown, bbcode_dle, plain |
common.hashtags |
true/false — превращать #теги в ссылки |
|
common.autolinks |
true/false — превращать ссылки в кликабельные |
Вы можете управлять публикацией прямо из текста письма, используя следующие шорткоды:
| Шорткод | Действие |
|---|---|
[p:пароль] |
Автоматическая публикация, если пароль совпадает с publish_passwords |
[status:published] |
Опубликовать пост сразу (published) или отправить на модерацию (pending) |
[cut] |
Всё выше — анонс в ленте. Всё ниже — полный текст. |
[end] |
Игнорировать весь текст после этого маркера |
[tags "тег1, тег2"] |
Добавить теги к посту |
[format:markdown] |
Принудительно указать формат (markdown, bbcode, plain) |
[p:мойСекретныйПароль]
[format:markdown]
# Заголовок статьи
Это краткое введение. Оно попадёт в анонс.
[cut]
А это полный текст статьи. Он будет виден только на странице самого поста.
[tags "новости, php, разработка"]
[end]
Подпись и прочая информация игнорируется.
Email2Post автоматически определяет формат текста и применяет соответствующий парсер.
[b], [i], [url], [quote], списки[format:markdown].
# Заголовок H1
## Заголовок H2
**Жирный текст** и *курсив*
- Список пункт 1
- Список пункт 2
[Текст ссылки](https://example.com)
[h1]Заголовок H1[/h1]
[h2]Заголовок H2[/h2]
[b]Жирный текст[/b] и [i]курсив[/i]
[url]https://example.com[/url]
[url=https://example.com]Текст ссылки[/url]
Для автоматической проверки почты и публикации новых писем настройте cron-задачу.
*/10 * * * * php /путь/до/вашей/папки/core/cron/email_parser.php
Эта команда будет проверять почту каждые 10 минут.
/usr/bin/php или /usr/local/bin/php.
Для отладки вы можете запустить парсер вручную через браузер или терминал:
https://ваш-сайт.ru/core/cron/email_parser.phpphp core/cron/email_parser.php
Все шаблоны находятся в папке templates/minimal/.
templates/minimal/
├── base_01_head.php # <head> + стили + мета-теги
├── base_02_header.php # Шапка с переключением темы
├── base_03_main.php # Основное содержимое
├── base_04_footer.php # Подвал
├── base_05_bottom.php # Скрипты (Swiper, TTS)
├── blog_list.php # Лента постов
├── blog_post.php # Страница поста
└── tag.php # Страница тега
$config — массив с настройками из config.php$post — данные текущего поста (на странице поста)$posts — массив постов (на странице списка)$pageType — 'post' или 'list'BASE_URL — базовый URL сайтаtemplates/minimal/.
Все стили находятся в base_01_head.php.
[p:blog2025]
# Новая статья
Это моя первая публикация через Email2Post.
[cut]
Полный текст статьи, который отображается на странице поста.
[p:blog2025]
[status:published]
[tags "новости, обновления"]
## Анонс новой версии
Мы рады сообщить о выходе новой версии...
[cut]
Подробности обновления... [url]https://example.com/changelog[/url]
[p:blog2025]
[format:bbcode]
[h1]Заголовок статьи[/h1]
[b]Важная информация:[/b] текст с выделением.
[ul]
[li]Пункт 1[/li]
[li]Пункт 2[/li]
[/ul]
[p:пароль]publish_passwords в config.phppendingdata/logs/email_parser.logВ config.php измените значение blog.posts_per_page (по умолчанию 12).
Создайте свой парсер в папке core/parsers/ и зарегистрируйте его в blog_post.php.
core/, templates/, index.php, .htaccessdata/ и настройки в config.php сохраняютсяdata/posts_email.json — все опубликованные постыdata/pending_posts.json — посты на модерацииdata/uploads/email_attachments/ — вложения<?= $variable ?>),
так и напрямую в HTML.
$config)$post)$posts)Доступны во всех шаблонах.
| Переменная | Тип | Описание | Пример |
|---|---|---|---|
BASE_URL |
string | Базовый URL сайта | /post/123 |
DEBUG |
bool | Режим отладки (true/false) |
<?php if (DEBUG) { ... } ?> |
SITE_TITLE |
string | Название сайта из конфига | <?= SITE_TITLE ?> |
SITE_DESCRIPTION |
string | Описание сайта из конфига | <?= SITE_DESCRIPTION ?> |
TEMPLATE_PATH |
string | Путь к активному шаблону | <?= TEMPLATE_PATH ?> |
TCSE_ROOT |
string | Корневая папка скрипта | Для внутреннего использования |
$config)
Массив $config содержит все настройки из data/config.php.
Доступен во всех шаблонах.
| Путь | Тип | Описание | Пример вывода |
|---|---|---|---|
$config['base_url'] |
string | Базовый URL сайта | <?= $config['base_url'] ?> |
$config['debug'] |
bool | Режим отладки | <?php if ($config['debug']) { ... } ?> |
$config['site'])| Путь | Тип | Описание | Пример вывода |
|---|---|---|---|
$config['site']['title'] |
string | Название сайта | <?= $config['site']['title'] ?> |
$config['site']['description'] |
string | Описание сайта | <?= $config['site']['description'] ?> |
$config['site']['accent_color'] |
string | Цвет акцента (CSS) | --accent: ; |
$config['channel'])| Путь | Тип | Описание | Пример вывода |
|---|---|---|---|
$config['channel']['blog_title'] |
string | Название блога | <?= $config['channel']['blog_title'] ?> |
$config['channel']['channel_username'] |
string | Имя Telegram-канала (без @) | @ |
$config['email']['moderation'])| Путь | Тип | Описание | Пример вывода |
|---|---|---|---|
$config['email']['moderation']['admin_email'] |
string | Email администратора | <?= $config['email']['moderation']['admin_email'] ?> |
$post)Доступны на странице отдельного поста (blog_post.php).
| Поле | Тип | Описание | Пример вывода |
|---|---|---|---|
$post['id'] |
string | Уникальный ID поста | <?= $post['id'] ?> |
$post['title'] |
string | Заголовок поста | <h1><?= $post['title'] ?></h1> |
$post['text'] |
string | Полный текст поста (с разметкой) | <?= $post['text'] ?> |
$post['caption'] |
string | Краткий текст (анонс) | <?= $post['caption'] ?> |
$post['date'] |
string | Дата публикации (Y-m-d H:i:s) | <?= date('d.m.Y', strtotime($post['date'])) ?> |
$post['author'] |
string | Имя автора | <?= $post['author'] ?> |
$post['author_email'] |
string | Email автора | <?= $post['author_email'] ?> |
$post['source'] |
string | Источник (email или telegram) |
<?= $post['source'] ?> |
$post['tags'] |
array | Массив тегов | <?php foreach ($post['tags'] as $tag) { echo $tag; } ?> |
$post['format'] |
string | Формат контента (markdown, bbcode, plain) |
<?= $post['format'] ?> |
$post['status'] |
string | Статус (published, pending, draft) |
<?= $post['status'] ?> |
$post['images'] |
array | Массив URL изображений | <?= $post['images'][0] ?> |
$post['photo_file_id'] |
string | Главное изображение (путь или ID) | <?= $post['photo_file_id'] ?> |
$post['attachments'] |
array | Массив вложений (файлы из email) | <?php foreach ($post['attachments'] as $file) { echo $file['filename']; } ?> |
$post['audio'] |
array|null | Аудио-вложение | <?= $post['audio']['file_id'] ?? '' ?> |
<?php if (!empty($post['tags']) && is_array($post['tags'])): ?>
<div class="post-tags">
<?php foreach ($post['tags'] as $tag): ?>
<a href="<?= BASE_URL ?>/tag/<?= urlencode($tag) ?>" class="post-tag">
#<?= htmlspecialchars($tag) ?>
</a>
<?php endforeach; ?>
</div>
<?php endif; ?>
$posts)Доступны на странице списка постов (blog_list.php).
| Переменная | Тип | Описание | Пример вывода |
|---|---|---|---|
$posts |
array | Массив постов для текущей страницы | <?php foreach ($posts as $item): ?> ... <?php endforeach; ?> |
$currentPage |
int | Номер текущей страницы | Страница <?= $currentPage ?> |
$totalPages |
int | Общее количество страниц | из <?= $totalPages ?> |
Те же поля, что и у $post, но доступны как $item['поле'] внутри цикла.
| Поле | Тип | Описание | Пример в цикле |
|---|---|---|---|
$item['id'] |
string | ID поста | <?= $item['id'] ?> |
$item['title'] |
string | Заголовок | <?= $item['title'] ?> |
$item['caption'] |
string | Краткий текст (анонс) | <?= $item['caption'] ?> |
$item['date'] |
string | Дата публикации | <?= date('d.m.Y', strtotime($item['date'])) ?> |
$item['author'] |
string | Автор | <?= $item['author'] ?> |
$item['tags'] |
array | Массив тегов | <?php print_r($item['tags']) ?> |
$item['images'] |
array | Массив изображений | <img src="<?= $item['images'][0] ?>"> |
<?php foreach ($posts as $item):
$title = $item['title'] ?? 'Без названия';
$date = date('d.m.Y', strtotime($item['date']));
$url = BASE_URL . '/post/' . $item['id'];
$excerpt = mb_substr(strip_tags($item['caption'] ?? $item['text'] ?? ''), 0, 150);
?>
<article class="post-item">
<h2><a href="<?= $url ?>"><?= htmlspecialchars($title) ?></a></h2>
<time><?= $date ?></time>
<p><?= htmlspecialchars($excerpt) ?>...</p>
<a href="<?= $url ?>">Читать далее →</a>
</article>
<?php endforeach; ?>
Доступны в base_01_head.php и других базовых шаблонах.
| Переменная | Тип | Описание | Пример вывода |
|---|---|---|---|
$pageType |
string | Тип страницы: post или list |
<?= $pageType ?> |
$title |
string | Заголовок страницы (для <title>) | <title><?= $title ?></title> |
$description |
string | Описание страницы (для meta) | <meta name="description" content="<?= $description ?>"> |
Временно добавьте этот код в шаблон, чтобы увидеть все доступные данные:
<?php if (DEBUG): ?>
<pre style="background: #f1f5f9; padding: 1rem; border-radius: 8px; font-size: 0.8rem;">
<?php var_dump($post ?? [], $posts ?? [], $config ?? []) ?>
</pre>
<?php endif; ?>
<?php if (!empty($post['images']) && is_array($post['images'])): ?>
<div class="post-gallery">
<?php foreach ($post['images'] as $img): ?>
<img src="<?= BASE_URL . $img ?>" alt="<?= htmlspecialchars($post['title']) ?>" loading="lazy">
<?php endforeach; ?>
</div>
<?php endif; ?>
<a href="<?= BASE_URL ?>/post/<?= $post['id'] ?>">
<?= htmlspecialchars($post['title']) ?>
</a>
<time datetime="<?= date('c', strtotime($post['date'])) ?>">
<?= date('d.m.Y H:i', strtotime($post['date'])) ?>
</time>
<?php if (!empty($config['channel']['channel_username'])): ?>
<a href="https://t.me/<?= $config['channel']['channel_username'] ?>" target="_blank">
Telegram
</a>
<?php endif; ?>
<?php foreach ($posts as $item):
$hasImage = !empty($item['images']) && is_array($item['images']);
$imageUrl = $hasImage ? BASE_URL . $item['images'][0] : BASE_URL . '/assets/no-image.jpg';
?>
<article class="post-card">
<img src="<?= $imageUrl ?>" alt="<?= htmlspecialchars($item['title'] ?? '') ?>">
<div class="post-card-body">
<h3><a href="<?= BASE_URL ?>/post/<?= $item['id'] ?>">
<?= htmlspecialchars($item['title'] ?? 'Без названия') ?>
</a></h3>
<time><?= date('d.m.Y', strtotime($item['date'])) ?></time>
<p><?= htmlspecialchars(mb_substr(strip_tags($item['caption'] ?? ''), 0, 150)) ?>...</p>
<a href="<?= BASE_URL ?>/post/<?= $item['id'] ?>" class="read-more">Читать далее →</a>
</div>
</article>
<?php endforeach; ?>
| Функция | Описание | Пример |
|---|---|---|
htmlspecialchars() |
Экранирует HTML-символы (безопасность) | <?= htmlspecialchars($post['title']) ?> |
strip_tags() |
Удаляет HTML-теги | <?= strip_tags($post['text']) ?> |
mb_substr() |
Обрезает строку (с учётом UTF-8) | <?= mb_substr($text, 0, 200) ?> |
date() |
Форматирует дату | <?= date('d.m.Y', strtotime($post['date'])) ?> |
urlencode() |
Кодирует URL | <?= urlencode($tag) ?> |
empty() |
Проверяет, пусто ли значение | <?php if (!empty($post['tags'])): ?> |
htmlspecialchars() при выводе пользовательских данных в HTML,
чтобы предотвратить XSS-атаки.
Веб-разработчик с 20-летним стажем, основатель веб-студии TCSE. Специализация: DLE «под ключ», Webasyst, Parts-Soft.ru, технический аудит.
Эта статья подробно описывает создание небольшого скрипта на Node.js для выгрузки векторных иконок из Figma в проект, а...
ПодробнееTailwind CSS — как фреймворк для разработчиков довольно прост в понимании. По сути, он позволяет вам встраивать код CSS...
ПодробнееНаткнулся на вот такой полезный класс, любителям chained вызовов и jquery понравится очень! PHP Simple HTML DOM Parser:...
ПодробнееШаблон таблицы + видео-инструкция по использованию, для эффективного определения приоритетов постраничной...
ПодробнееУстановка обновлений CMS при наличии действующей лицензии на DLE. Интеграция в проект новых функции реализованных в...
ПодробнееОзнакомительное руководство по использованию Bootstrap для создания веб-страниц и тестирования. Здесь вы узнаете, из...
Подробнее
💬 Комментарии
В связи с новыми требованиями законодательства РФ (ФЗ-152, ФЗ «О рекламе») и ужесточением контроля со стороны РКН, мы отключили систему комментариев на сайте.
🔒 Важно Теперь мы не собираем и не храним ваши персональные данные — даже если очень захотим.
💡 Хотите обсудить материал?
Присоединяйтесь к нашему Telegram-каналу:
https://t.me/tcsecmsНажмите кнопку ниже — и вы сразу попадёте в чат с комментариями