mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-06-06 19:44:35 -04:00
CSS inliner plugin
This commit is contained in:
parent
3dafb34d8f
commit
2bc83aff27
41
app/Providers/MailCssInlinerServiceProvider.php
Normal file
41
app/Providers/MailCssInlinerServiceProvider.php
Normal file
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use App\Utils\CssInlinerPlugin;
|
||||
use Illuminate\Mail\MailManager;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
class MailCssInlinerServiceProvider extends ServiceProvider
|
||||
{
|
||||
// Thanks to @fedeisas/laravel-mail-css-inliner
|
||||
|
||||
/**
|
||||
* Bootstrap the application events.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
$this->publishes([
|
||||
__DIR__ . '/../config/css-inliner.php' => base_path('config/css-inliner.php'),
|
||||
], 'config');
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the service provider.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
$this->app->singleton(CssInlinerPlugin::class, function ($app) {
|
||||
return new CssInlinerPlugin([]);
|
||||
});
|
||||
|
||||
$this->app->afterResolving('mail.manager', function (MailManager $mailManager) {
|
||||
$mailManager->getSwiftMailer()->registerPlugin($this->app->make(CssInlinerPlugin::class));
|
||||
return $mailManager;
|
||||
});
|
||||
}
|
||||
}
|
140
app/Utils/CssInlinerPlugin.php
Normal file
140
app/Utils/CssInlinerPlugin.php
Normal file
@ -0,0 +1,140 @@
|
||||
<?php
|
||||
|
||||
namespace App\Utils;
|
||||
|
||||
use TijsVerkoyen\CssToInlineStyles\CssToInlineStyles;
|
||||
|
||||
class CssInlinerPlugin implements \Swift_Events_SendListener
|
||||
{
|
||||
/**
|
||||
* @var CssToInlineStyles
|
||||
*/
|
||||
protected $converter;
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
protected $cssCache;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $options;
|
||||
|
||||
/**
|
||||
* @param array $options options defined in the configuration file.
|
||||
*/
|
||||
public function __construct(array $options)
|
||||
{
|
||||
$this->converter = new CssToInlineStyles();
|
||||
$this->options = $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Swift_Events_SendEvent $evt
|
||||
*/
|
||||
public function beforeSendPerformed(\Swift_Events_SendEvent $evt)
|
||||
{
|
||||
$message = $evt->getMessage();
|
||||
|
||||
if ($message->getContentType() === 'text/html'
|
||||
|| ($message->getContentType() === 'multipart/alternative' && $message->getBody())
|
||||
|| ($message->getContentType() === 'multipart/mixed' && $message->getBody())
|
||||
) {
|
||||
[$body, $cssResources] = $this->messageSieve($message->getBody());
|
||||
$css = $this->concatCss($cssResources);
|
||||
$message->setBody($this->converter->convert($body, $css));
|
||||
}
|
||||
|
||||
foreach ($message->getChildren() as $part) {
|
||||
if (strpos($part->getContentType(), 'text/html') === 0) {
|
||||
[$body, $cssResources] = $this->messageSieve($part->getBody());
|
||||
$css = $this->concatCss($cssResources);
|
||||
$part->setBody($this->converter->convert($body, $css));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Do nothing
|
||||
*
|
||||
* @param \Swift_Events_SendEvent $evt
|
||||
*/
|
||||
public function sendPerformed(\Swift_Events_SendEvent $evt)
|
||||
{
|
||||
// Do Nothing
|
||||
}
|
||||
|
||||
protected function concatCss(array $cssResources): string
|
||||
{
|
||||
$output = '';
|
||||
foreach ($cssResources as $cssResource) {
|
||||
$output.= $this->fetchCss($cssResource);
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
protected function fetchCss(string $filename): string
|
||||
{
|
||||
if (isset($this->cssCache[$filename])) {
|
||||
return $this->cssCache[$filename];
|
||||
}
|
||||
|
||||
$fixedFilename = $filename;
|
||||
// Fix relative protocols on hrefs. Assume https.
|
||||
if (substr($filename, 0, 2) === '//') {
|
||||
$fixedFilename = 'https:' . $filename;
|
||||
}
|
||||
|
||||
$content = file_get_contents($fixedFilename);
|
||||
if (! $content) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$this->cssCache[$filename] = $content;
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
protected function messageSieve(string $message): array
|
||||
{
|
||||
$cssResources = [];
|
||||
|
||||
// Initialize with config defaults, if any
|
||||
if (isset($this->options['css-files'])) {
|
||||
$cssResources = $this->options['css-files'];
|
||||
}
|
||||
|
||||
$dom = new \DOMDocument();
|
||||
// set error level
|
||||
$internalErrors = libxml_use_internal_errors(true);
|
||||
|
||||
$dom->loadHTML($message);
|
||||
|
||||
// Restore error level
|
||||
libxml_use_internal_errors($internalErrors);
|
||||
$link_tags = $dom->getElementsByTagName('link');
|
||||
|
||||
/** @var \DOMElement $link */
|
||||
foreach ($link_tags as $link) {
|
||||
if ($link->getAttribute('rel') === 'stylesheet') {
|
||||
array_push($cssResources, $link->getAttribute('href'));
|
||||
}
|
||||
}
|
||||
|
||||
$link_tags = $dom->getElementsByTagName('link');
|
||||
for ($i = $link_tags->length; --$i >= 0;) {
|
||||
$link = $link_tags->item($i);
|
||||
if ($link->getAttribute('rel') === 'stylesheet') {
|
||||
$link->parentNode->removeChild($link);
|
||||
}
|
||||
}
|
||||
|
||||
if (count($cssResources)) {
|
||||
return [$dom->saveHTML(), $cssResources];
|
||||
}
|
||||
|
||||
return [$message, []];
|
||||
}
|
||||
}
|
@ -181,7 +181,7 @@ return [
|
||||
App\Providers\MultiDBProvider::class,
|
||||
App\Providers\ClientPortalServiceProvider::class,
|
||||
App\Providers\NinjaTranslationServiceProvider::class,
|
||||
|
||||
App\Providers\MailCssInlinerServiceProvider::class,
|
||||
],
|
||||
|
||||
/*
|
||||
|
18
config/css-inliner.php
Normal file
18
config/css-inliner.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Css Files
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Css file of your style for your emails
|
||||
| The content of these files will be added directly into the inliner
|
||||
| Use absolute paths, ie. public_path('css/main.css')
|
||||
|
|
||||
*/
|
||||
|
||||
'css-files' => [],
|
||||
|
||||
];
|
Loading…
x
Reference in New Issue
Block a user