Skip to content
Snippets Groups Projects
Commit 8273c5e5 authored by Pavel Piligrimov's avatar Pavel Piligrimov
Browse files

init

parents
No related branches found
No related tags found
No related merge requests found
Showing
with 598 additions and 0 deletions
.idea
/node_modules/
/vendor/
*.lock
*-lock.json
!package-lock.json
\ No newline at end of file
{
"name": "iqdev/mail.mjml",
"description": "Module for generating emails from components",
"minimum-stability": "stable",
"license": "proprietary",
"authors": [
{
"name": "Pavel Piligrimov",
"email": "p.piligrimov@iqdev.digital"
}
],
"version": "0.0.1",
"type": "bitrix-module",
"keywords": [
"bitrix",
"twig",
"mjml",
"edit",
"component"
],
"require": {
"php": ">=7.4",
"eloquent/composer-npm-bridge": "^5.0.1",
"twig/twig": "^1.0"
},
"require-dev": {
"roave/security-advisories": "dev-latest"
},
"config": {
"allow-plugins": {
"eloquent/composer-npm-bridge": true
}
}
}
<?php
use Mail\Mjml\Events;
if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED !== true) {
die();
}
include_once 'lib/mjml/RendererTemplate/componentrenderer.php';
Events::bindEvents();
\ No newline at end of file
<?php
class MailTestComponent extends CBitrixComponent
{
public function executeComponent()
{
$this->arResult['TEMPLATE_DATA'] = $this->arParams;
$this->includeComponentTemplate();
}
}
\ No newline at end of file
{% apply mjml_to_html %}
<mjml>
<mj-body>
<mj-section>
<mj-column>
{% for user in username %}
<mj-text>Hello {{ user }}</mj-text>
{% endfor %}
</mj-column>
</mj-section>
</mj-body>
</mjml>
{% endapply %}
\ No newline at end of file
<?php
use Bitrix\Main\Application;
use Bitrix\Main\IO\Directory;
use Bitrix\Main\Localization\Loc;
if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED !== true) {
die();
}
class mail_mjml extends CModule
{
public $MODULE_ID = 'mail.mjml';
public $MODULE_NAME;
public $MODULE_VERSION;
public $MODULE_VERSION_DATE;
public $MODULE_DESCRIPTION;
public $PARTNER_NAME;
public $PARTNER_URI;
/**
* Конструктор
*
* @return void
*/
public function __construct()
{
$arModuleVersion = [];
include(__DIR__ . '/version.php');
$this->MODULE_VERSION = $arModuleVersion['VERSION'];
$this->MODULE_VERSION_DATE = $arModuleVersion['VERSION_DATE'];
Loc::loadMessages(__FILE__);
$this->MODULE_NAME = Loc::getMessage('MODULE_NAME');
$this->MODULE_DESCRIPTION = Loc::getMessage('MODULE_DESCRIPTION');
$this->PARTNER_NAME = Loc::getMessage('PARTNER_NAME');
$this->PARTNER_URI = Loc::getMessage('PARTNER_URI');
}
/**
* Установка модуля
*
* @return void
*/
function DoInstall()
{
RegisterModule($this->MODULE_ID);
$this->installFiles();
}
/**
* Копирование файлов
*
* @return void
*/
function InstallFiles()
{
CopyDirFiles(
__DIR__ . '/components',
Application::getDocumentRoot() . '/bitrix/components/',
true,
true
);
CopyDirFiles(__DIR__ . '/admin', Application::getDocumentRoot() . '/bitrix/admin');
}
/**
* Удаление файлов
*
* @return void
*/
function UnInstallFiles()
{
$aDir = new DirectoryIterator(__DIR__ . '/components/iqdev/');
foreach ($aDir as $oFileInfo) {
if ($oFileInfo->isDir() && !$oFileInfo->isDot()) {
Directory::deleteDirectory(
Application::getDocumentRoot() . '/bitrix/components/iqdev/' . $oFileInfo->getFilename() . '/'
);
}
}
DeleteDirFiles(__DIR__ . '/admin', Application::getDocumentRoot() . '/bitrix/admin');
}
/**
* Удаление модуля
*
* @return void
* @throws \Bitrix\Main\ArgumentNullException
*/
function DoUninstall()
{
UnRegisterModule($this->MODULE_ID);
$this->UnInstallFiles();
}
}
\ No newline at end of file
<?php
$arModuleVersion = [
'VERSION' => '0.0.1',
'VERSION_DATE' => '2022-07-03 13:00:00',
];
\ No newline at end of file
<?php
$MESS['MODULE_NAME'] = 'Компоненты для почтовых шаблонов';
$MESS['MODULE_DESCRIPTION'] = 'Модуль для генерации электронных писем из компонентов';
$MESS['PARTNER_NAME'] = 'IQ DEV';
$MESS['PARTNER_URI'] = 'https://iqdev.digital/';
\ No newline at end of file
<?php
namespace Mail\Mjml;
/**
*
*/
class Events
{
public static function bindEvents()
{
static::addCustomTemplateEngine();
}
public static function addCustomTemplateEngine()
{
global $arCustomTemplateEngines;
$arCustomTemplateEngines['mjml.twig'] = [
'templateExt' => ['mjml.twig', 'twig'],
'function' => '\Mail\Mjml\Mjml\RendererTemplate\renderTemplateFile',
];
}
}
\ No newline at end of file
<?php
namespace Mail\Mjml\Exception;
/**
* Исключение при рендере шаблона
*/
class ProcessException extends \Exception
{
}
\ No newline at end of file
<?php
namespace Mail\Mjml\Mjml\Process;
use Mail\Mjml\Exception\ProcessException;
/**
* Подготовка и запуск рендера шаблона
*/
class Process
{
private $sCommand;
private $sInput;
private $sOutput;
private $sErrorMessage = '';
public const STDIN = 0;
public const STDOUT = 1;
public const STDERR = 2;
private static $aDescriptors = [
self::STDIN => ['pipe', 'r'],
self::STDOUT => ['pipe', 'w'],
self::STDERR => ['pipe', 'w']
];
public function __construct(string $command, string $input)
{
$this->sCommand = $command;
$this->sInput = $input;
}
/**
* @throws ProcessException
*/
public function run(): void
{
$this->initialize();
$pipes = [];
$process = $this->createProcess($pipes);
$this->setInput($pipes);
$this->setOutput($pipes);
$this->setErrorMessage($pipes);
if (0 !== proc_close($process)) {
throw new ProcessException($this->sErrorMessage);
}
}
public function getOutput()
{
return $this->sOutput;
}
private function initialize(): void
{
$this->sOutput = null;
$this->sErrorMessage = '';
}
/**
* @return resource
*
* @throws ProcessException
*/
private function createProcess(array &$pipes)
{
$process = proc_open($this->sCommand, self::$aDescriptors, $pipes);
if (!is_resource($process)) {
throw new ProcessException('Unable to create a process.');
}
return $process;
}
private function setInput(array $pipes): void
{
$stdin = $pipes[self::STDIN];
fwrite($stdin, $this->sInput);
fclose($stdin);
}
private function setOutput(array $pipes): void
{
$stdout = $pipes[self::STDOUT];
$this->sOutput = stream_get_contents($stdout);
fclose($stdout);
}
private function setErrorMessage(array $pipes): void
{
$stderr = $pipes[self::STDERR];
$this->sErrorMessage = stream_get_contents($stderr);
fclose($stderr);
}
}
\ No newline at end of file
<?php
namespace Mail\Mjml\Mjml\Renderer;
use Mail\Mjml\Mjml\Process\Process;
/**
*
*/
class BinaryRenderer implements RendererInterface
{
/**
* The MJML CLI path.
*
* @var string
*/
private $bin;
/**
* @var string
*/
private $command;
/**
* BinaryRenderer constructor.
*
* @param string $bin
*/
public function __construct(string $bin)
{
$this->bin = $bin;
$this->command = "{$this->bin} -s -i --config.minify";
}
/**
* @inheritDoc
*/
public function render(string $content): string
{
$process = new Process($this->command, $content);
$process->run();
return (string)$process->getOutput();
}
}
\ No newline at end of file
<?php
namespace Mail\Mjml\Mjml\Renderer;
interface RendererInterface
{
/**
* Renders MJML to HTML content.
*
* @param string $content The MJML content
*
* @return string The generated HTML
*/
public function render(string $content): string;
}
\ No newline at end of file
<?php
namespace Mail\Mjml\Mjml\RendererTemplate;
use Bitrix\Main\Application;
use CBitrixComponent;
use CBitrixComponentTemplate;
use Mail\Mjml\Mjml\Renderer\BinaryRenderer;
use Twig\Environment;
use Twig\Loader\FilesystemLoader;
use \Mail\Mjml\Mjml\TwigExtension\MjmlExtension;
/**
* Подключение кастомного типа шаблона
*
* @param string $templateFile
* @param array $arResult
* @param array $arParams
* @param array $arLangMessages
* @param string $templateFolder
* @param string $parentTemplateFolder
* @param CBitrixComponentTemplate $template
*
* @throws \Twig\Error\LoaderError
* @throws \Twig\Error\RuntimeError
* @throws \Twig\Error\SyntaxError
*/
function renderTemplateFile(
$templateFile,
$arResult,
$arParams,
$arLangMessages,
$templateFolder,
$parentTemplateFolder,
$template
) {
global $APPLICATION;
if (($f = Application::getDocumentRoot() . $templateFile) && file_exists($f)) {
if ($_REQUEST['clear_cache'] === 'Y') {
touch($f);
}
}
$loader = new FilesystemLoader(Application::getDocumentRoot());
$twig = new Environment($loader);
$sPathToMjml = $_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/mail.mjml/node_modules/.bin/mjml';
if (is_file($_SERVER['DOCUMENT_ROOT'] . '/local/modules/mail.mjml/install/index.php')) {
$sPathToMjml = $_SERVER['DOCUMENT_ROOT'] . '/local/modules/mail.mjml/node_modules/.bin/mjml';
}
$renderer = new BinaryRenderer($sPathToMjml);
$twig->addExtension(new MjmlExtension($renderer));
echo $twig->render(
$templateFile,
$arResult['TEMPLATE_DATA']
);
$component_epilog = $templateFolder . '/component_epilog.php';
if (file_exists($_SERVER['DOCUMENT_ROOT'] . $component_epilog)) {
/** @var CBitrixComponent $component */
$component = $template->getComponent();
$component->SetTemplateEpilog(
[
'epilogFile' => $component_epilog,
'templateName' => $template->__name,
'templateFile' => $template->__file,
'templateFolder' => $template->__folder,
'templateData' => false,
]
);
}
}
\ No newline at end of file
<?php
namespace Mail\Mjml\Mjml\TwigExtension;
use Twig\TwigFilter;
use Mail\Mjml\Mjml\Renderer\RendererInterface;
use Twig\Extension\AbstractExtension;
/**
* Расширение для twig
*/
class MjmlExtension extends AbstractExtension
{
/**
* @var RendererInterface
*/
protected $oRenderer;
/**
* MjmlExtension constructor.
*
* @param RendererInterface $renderer
*/
public function __construct(RendererInterface $renderer)
{
$this->oRenderer = $renderer;
}
/**
* @inheritDoc
*/
public function getFilters(): array
{
return [
new TwigFilter('mjml_to_html', [$this, 'render'], ['is_safe' => ['all']])
];
}
/**
* Render MJML to HTML
*
* @param string $content
*
* @return string The generated HTML
*/
public function render(string $content): string
{
return $this->oRenderer->render($content);
}
}
\ No newline at end of file
<?php
use Bitrix\Main\Loader;
if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED !== true) {
die();
}
$module_id = 'mail.mjml';
Loader::includeModule($module_id);
\ No newline at end of file
This diff is collapsed.
{
"name": "mail.mjml",
"version": "1.0.0",
"private": true,
"dependencies": {
"mjml": "^4.12.0"
}
}
# Модуль для генерации почтовых шаблонов из компонента
Mjml шаблонизатор https://documentation.mjml.io
## Использование
В шаблоне письма выбрать режим "Визуальный редактор" и подключить компонент.
Например:
```php
$APPLICATION->IncludeComponent(
'iqdev:mail.test',
'',
[
'username' => ['Иван', 'Петр']
]
);
```
### При использовании компонентов в шаблоне использовать файл template с расширением mjml.twig
Файл должен начинаться с тега ```{% apply mjml_to_html %} ```
и заканчиваться ```{% endapply %}```
Например
```twig
{% apply mjml_to_html %}
<mjml>
<mj-body>
<mj-section>
<mj-column>
<mj-text>Hello {{ username }}</mj-text>
</mj-column>
</mj-section>
</mj-body>
</mjml>
{% endapply %}
```
## Twig в шаблоне
В шаблоне помимо синтаксиса mjml можно использовать конструкции twig
Например, цикл:
```twig
<mj-column>
{% for user in username %}
<mj-text>Hello {{ user }}</mj-text>
{% endfor %}
</mj-column>
```
## Параметры компонента для передачи в шаблон
В шаблон template.mjml.twig будут переданы данные из ```arResult['TEMPLATE_DATA']```
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment