From 37ed0436dd3b83a266722ae8606d2bebee5be72b Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sun, 11 Aug 2019 11:30:22 +0200 Subject: [PATCH] added the cssinliner and inky extensions --- .travis.yml | 4 + doc/filters/index.rst | 2 + doc/filters/inky.rst | 40 +++++++++ doc/filters/inline_css.rst | 64 ++++++++++++++ extra/cssinliner-extra/.gitignore | 4 + extra/cssinliner-extra/LICENSE | 19 +++++ extra/cssinliner-extra/README.md | 8 ++ extra/cssinliner-extra/composer.json | 39 +++++++++ extra/cssinliner-extra/phpunit.xml.dist | 30 +++++++ .../src/CssInlinerExtension.php | 36 ++++++++ .../tests/Fixtures/inline_css.test | 53 ++++++++++++ .../tests/IntegrationTest.php | 30 +++++++ extra/inky-extra/.gitignore | 4 + extra/inky-extra/LICENSE | 19 +++++ extra/inky-extra/README.md | 8 ++ extra/inky-extra/composer.json | 39 +++++++++ extra/inky-extra/phpunit.xml.dist | 30 +++++++ .../DependencyInjection/TwigInkyExtension.php | 29 +++++++ .../src/Bundle/Resources/config/inky.xml | 14 +++ .../inky-extra/src/Bundle/TwigInkyBundle.php | 18 ++++ extra/inky-extra/src/InkyExtension.php | 31 +++++++ extra/inky-extra/tests/Fixtures/inky.test | 16 ++++ extra/inky-extra/tests/IntegrationTest.php | 30 +++++++ .../src/DependencyInjection/Configuration.php | 34 ++------ .../TwigExtraExtension.php | 15 ++-- extra/twig-extra-bundle/src/Extensions.php | 85 +++++++++++++++++++ .../src/MissingExtensionSuggestor.php | 40 +-------- .../src/Resources/config/cssinliner.xml | 14 +++ .../src/Resources/config/inky.xml | 14 +++ 29 files changed, 698 insertions(+), 71 deletions(-) create mode 100644 doc/filters/inky.rst create mode 100644 doc/filters/inline_css.rst create mode 100644 extra/cssinliner-extra/.gitignore create mode 100644 extra/cssinliner-extra/LICENSE create mode 100644 extra/cssinliner-extra/README.md create mode 100644 extra/cssinliner-extra/composer.json create mode 100644 extra/cssinliner-extra/phpunit.xml.dist create mode 100644 extra/cssinliner-extra/src/CssInlinerExtension.php create mode 100644 extra/cssinliner-extra/tests/Fixtures/inline_css.test create mode 100644 extra/cssinliner-extra/tests/IntegrationTest.php create mode 100644 extra/inky-extra/.gitignore create mode 100644 extra/inky-extra/LICENSE create mode 100644 extra/inky-extra/README.md create mode 100644 extra/inky-extra/composer.json create mode 100644 extra/inky-extra/phpunit.xml.dist create mode 100644 extra/inky-extra/src/Bundle/DependencyInjection/TwigInkyExtension.php create mode 100644 extra/inky-extra/src/Bundle/Resources/config/inky.xml create mode 100644 extra/inky-extra/src/Bundle/TwigInkyBundle.php create mode 100644 extra/inky-extra/src/InkyExtension.php create mode 100644 extra/inky-extra/tests/Fixtures/inky.test create mode 100644 extra/inky-extra/tests/IntegrationTest.php create mode 100644 extra/twig-extra-bundle/src/Extensions.php create mode 100644 extra/twig-extra-bundle/src/Resources/config/cssinliner.xml create mode 100644 extra/twig-extra-bundle/src/Resources/config/inky.xml diff --git a/.travis.yml b/.travis.yml index d36161487..df08eb390 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,13 +11,17 @@ before_install: install: - travis_retry composer install + - (cd extra/cssinliner-extra && travis_retry composer install) - (cd extra/html-extra && travis_retry composer install) + - (cd extra/inky-extra && travis_retry composer install) - (cd extra/intl-extra && travis_retry composer install) - (cd extra/markdown-extra && travis_retry composer install) script: - ./vendor/bin/simple-phpunit + - (cd extra/cssinliner-extra && ./vendor/bin/simple-phpunit) - (cd extra/html-extra && ./vendor/bin/simple-phpunit) + - (cd extra/inky-extra && ./vendor/bin/simple-phpunit) - (cd extra/intl-extra && ./vendor/bin/simple-phpunit) - (cd extra/markdown-extra && ./vendor/bin/simple-phpunit) diff --git a/doc/filters/index.rst b/doc/filters/index.rst index 08a4250c0..e64dc7c96 100644 --- a/doc/filters/index.rst +++ b/doc/filters/index.rst @@ -27,6 +27,8 @@ Filters format_number format_time html_to_markdown + inline_css + inky join json_encode keys diff --git a/doc/filters/inky.rst b/doc/filters/inky.rst new file mode 100644 index 000000000..b5635a28c --- /dev/null +++ b/doc/filters/inky.rst @@ -0,0 +1,40 @@ +``inky`` +======== + +.. versionadded:: 2.12 + The ``inky`` filter was added in Twig 2.12. + +The ``inky`` filter processes an `inky email template +`_: + +.. code-block:: twig + + {% apply inky %} + + + + + {% endapply %} + +You can also use the filter on an included file: + +.. code-block:: twig + + {{ include('some_template.inky.twig')|inky }} + +.. note:: + + The ``inky`` filter is part of the ``CssInlinerExtension`` which is not + installed by default. Install it first: + + .. code-block:: bash + + $ composer req twig/cssinliner-extra + + Then, use the ``twig/extra-bundle`` on Symfony projects or add the extension + explictly on the Twig environment:: + + use Twig\Extra\CssInliner\CssInlinerExtension; + + $twig = new \Twig\Environment(...); + $twig->addExtension(new CssInlinerExtension()); diff --git a/doc/filters/inline_css.rst b/doc/filters/inline_css.rst new file mode 100644 index 000000000..d0f75c966 --- /dev/null +++ b/doc/filters/inline_css.rst @@ -0,0 +1,64 @@ +``inline_css`` +============== + +.. versionadded:: 2.12 + The ``inline_css`` filter was added in Twig 2.12. + +The ``inline_css`` filter inline CSS styles in HTML documents: + +.. code-block:: twig + + {% apply inline_css %} + + + + + +

Hello CSS!

+ + + {% endapply %} + +You can also add some stylesheets by passing them as arguments to the filter: + +.. code-block:: twig + + {% apply inline_css(source("some_styles.css"), source("another.css")) %} + + +

Hello CSS!

+ + + {% endapply %} + +Styles loaded via the filter override the styles defined in the `` +

Great!

+ +{% endapply %} + + +{% apply inline_css(source('css'))|spaceless %} + +

Great!

+ +{% endapply %} + + +{% apply inline_css(source('css'), source('more_css'))|spaceless %} + +

Great!

+ +{% endapply %} + + +{% apply inline_css(source('css') ~ source('more_css'))|spaceless %} + +

Great!

+ +{% endapply %} + + +{{ include('html')|inline_css(source('css') ~ source('more_css'))|spaceless }} +--TEMPLATE(html)-- + +

Great!

+ +--TEMPLATE(css)-- +p { color: red } +--TEMPLATE(more_css)-- +p { color: blue } +--DATA-- +return [] +--EXPECT-- +

Great!

+ +

Great!

+ +

Great!

+ +

Great!

+ +

Great!

diff --git a/extra/cssinliner-extra/tests/IntegrationTest.php b/extra/cssinliner-extra/tests/IntegrationTest.php new file mode 100644 index 000000000..5ab6ec9b4 --- /dev/null +++ b/extra/cssinliner-extra/tests/IntegrationTest.php @@ -0,0 +1,30 @@ + + + + + + + + + + ./tests/ + + + + + + ./ + + ./tests + ./vendor + + + + diff --git a/extra/inky-extra/src/Bundle/DependencyInjection/TwigInkyExtension.php b/extra/inky-extra/src/Bundle/DependencyInjection/TwigInkyExtension.php new file mode 100644 index 000000000..c1d763572 --- /dev/null +++ b/extra/inky-extra/src/Bundle/DependencyInjection/TwigInkyExtension.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Twig\Extra\Inky\Bundle\DependencyInjection; + +use Symfony\Component\Config\FileLocator; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; +use Symfony\Component\HttpKernel\DependencyInjection\Extension; + +/** + * @author Fabien Potencier + */ +class TwigInkyExtension extends Extension +{ + public function load(array $configs, ContainerBuilder $container) + { + $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); + $loader->load('inky.xml'); + } +} diff --git a/extra/inky-extra/src/Bundle/Resources/config/inky.xml b/extra/inky-extra/src/Bundle/Resources/config/inky.xml new file mode 100644 index 000000000..e4a6e73c8 --- /dev/null +++ b/extra/inky-extra/src/Bundle/Resources/config/inky.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + diff --git a/extra/inky-extra/src/Bundle/TwigInkyBundle.php b/extra/inky-extra/src/Bundle/TwigInkyBundle.php new file mode 100644 index 000000000..9bb43fbfa --- /dev/null +++ b/extra/inky-extra/src/Bundle/TwigInkyBundle.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Twig\Extra\Inky\Bundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +class TwigInkyBundle extends Bundle +{ +} diff --git a/extra/inky-extra/src/InkyExtension.php b/extra/inky-extra/src/InkyExtension.php new file mode 100644 index 000000000..271f1c2a4 --- /dev/null +++ b/extra/inky-extra/src/InkyExtension.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Twig\Extra\Inky; + +use Pinky; +use Twig\Extension\AbstractExtension; +use Twig\TwigFilter; + +class InkyExtension extends AbstractExtension +{ + public function getFilters() + { + return [ + new TwigFilter('inky', 'Twig\\Extra\\Inky\\twig_inky', ['is_safe' => ['html']]), + ]; + } +} + +function twig_inky(string $body): string +{ + return Pinky\transformString($body)->saveHTML(); +} diff --git a/extra/inky-extra/tests/Fixtures/inky.test b/extra/inky-extra/tests/Fixtures/inky.test new file mode 100644 index 000000000..76b603239 --- /dev/null +++ b/extra/inky-extra/tests/Fixtures/inky.test @@ -0,0 +1,16 @@ +--TEST-- +"inky" filter +--TEMPLATE-- +{% apply inky %} + {{ var }} +{%- endapply %} + +{{ include("inky")|inky }} +--TEMPLATE(inky)-- +{{ var }} +--DATA-- +return ['var' => 'value
'] +--EXPECT-- +
value<br />
+ +
value<br />
diff --git a/extra/inky-extra/tests/IntegrationTest.php b/extra/inky-extra/tests/IntegrationTest.php new file mode 100644 index 000000000..317d36476 --- /dev/null +++ b/extra/inky-extra/tests/IntegrationTest.php @@ -0,0 +1,30 @@ +getRootNode(); - $rootNode - ->children() - ->arrayNode('html') - ->{class_exists(HtmlExtension::class) ? 'canBeDisabled' : 'canBeEnabled'}() + foreach (Extensions::getClasses() as $extension => $class) { + $rootNode + ->children() + ->arrayNode($extension) + ->{class_exists($class) ? 'canBeDisabled' : 'canBeEnabled'}() + ->end() ->end() - ->end() - ; - - $rootNode - ->children() - ->arrayNode('markdown') - ->{class_exists(MarkdownExtension::class) ? 'canBeDisabled' : 'canBeEnabled'}() - ->end() - ->end() - ; - - $rootNode - ->children() - ->arrayNode('intl') - ->{class_exists(IntlExtension::class) ? 'canBeDisabled' : 'canBeEnabled'}() - ->end() - ->end() - ; + ; + } return $treeBuilder; } diff --git a/extra/twig-extra-bundle/src/DependencyInjection/TwigExtraExtension.php b/extra/twig-extra-bundle/src/DependencyInjection/TwigExtraExtension.php index 7a05f8710..da5aadf4e 100644 --- a/extra/twig-extra-bundle/src/DependencyInjection/TwigExtraExtension.php +++ b/extra/twig-extra-bundle/src/DependencyInjection/TwigExtraExtension.php @@ -15,6 +15,7 @@ use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; use Symfony\Component\HttpKernel\DependencyInjection\Extension; +use Twig\Extra\TwigExtraBundle\Extensions; /** * @author Fabien Potencier @@ -31,16 +32,10 @@ class TwigExtraExtension extends Extension $loader->load('suggestor.xml'); } - if ($this->isConfigEnabled($container, $config['html'])) { - $loader->load('html.xml'); - } - - if ($this->isConfigEnabled($container, $config['markdown'])) { - $loader->load('markdown.xml'); - } - - if ($this->isConfigEnabled($container, $config['intl'])) { - $loader->load('intl.xml'); + foreach (array_keys(Extensions::getClasses()) as $extension) { + if ($this->isConfigEnabled($container, $config[$extension])) { + $loader->load($extension.'.xml'); + } } } } diff --git a/extra/twig-extra-bundle/src/Extensions.php b/extra/twig-extra-bundle/src/Extensions.php new file mode 100644 index 000000000..e57a012a4 --- /dev/null +++ b/extra/twig-extra-bundle/src/Extensions.php @@ -0,0 +1,85 @@ + [ + 'name' => 'html', + 'class' => HtmlExtension::class, + 'class_name' => 'HtmlExtension', + 'package' => 'twig/html-extra', + 'filters' => ['data_uri'], + 'functions' => ['html_classes'], + ], + 'markdown' => [ + 'name' => 'markdown', + 'class' => MarkdownExtension::class, + 'class_name' => 'MarkdownExtension', + 'package' => 'twig/markdown-extra', + 'filters' => ['html_to_markdown', 'markdown_to_html'], + ], + 'intl' => [ + 'name' => 'intl', + 'class' => IntlExtension::class, + 'class_name' => 'IntlExtension', + 'package' => 'twig/intl-extra', + 'filters' => ['country_name', 'currency_name', 'currency_symbol', 'language_name', 'country_timezones', + 'format_currency', 'format_number', 'format_decimal_number', 'format_currency_number', + 'format_percent_number', 'format_scientific_number', 'format_spellout_number', 'format_ordinal_number', + 'format_duration_number', 'format_date', 'format_datetime', 'format_time' + ], + ], + 'cssinliner' => [ + 'name' => 'cssinliner', + 'class' => CssInlinerExtension::class, + 'class_name' => 'CssInlinerExtension', + 'package' => 'twig/cssinliner-extra', + 'filters' => ['inline_css'], + ], + 'inky' => [ + 'name' => 'inky', + 'class' => InkyExtension::class, + 'class_name' => 'InkyExtension', + 'package' => 'twig/inky-extra', + 'filters' => ['inky'], + ], + ]; + + public static function getClasses(): array + { + return array_column(self::EXTENSIONS, 'class', 'name'); + } + + public static function getFilters(string $name): array + { + foreach (self::EXTENSIONS as $extension) { + if (in_array($name, $extension['filters'])) { + return [$extension['class_name'], $extension['package']]; + } + } + + return []; + } + + public static function getFunctions(string $name): array + { + foreach (self::EXTENSIONS as $extension) { + if (in_array($name, $extension['functions'])) { + return [$extension['class_name'], $extension['package']]; + } + } + + return []; + } +} diff --git a/extra/twig-extra-bundle/src/MissingExtensionSuggestor.php b/extra/twig-extra-bundle/src/MissingExtensionSuggestor.php index fbc12df62..68160782b 100644 --- a/extra/twig-extra-bundle/src/MissingExtensionSuggestor.php +++ b/extra/twig-extra-bundle/src/MissingExtensionSuggestor.php @@ -15,42 +15,10 @@ use Twig\Error\SyntaxError; final class MissingExtensionSuggestor { - private const FILTERS = [ - // HTML - 'data_uri' => ['HtmlExtension', 'twig/html-extra'], - - // Markdown - 'html_to_markdown' => ['MarkdownExtension', 'twig/markdown-extra'], - 'markdown_to_html' => ['MarkdownExtension', 'twig/markdown-extra'], - - // Intl - 'country_name' => ['IntlExtension', 'twig/intl-extra'], - 'currency_name' => ['IntlExtension', 'twig/intl-extra'], - 'currency_symbol' => ['IntlExtension', 'twig/intl-extra'], - 'language_name' => ['IntlExtension', 'twig/intl-extra'], - 'country_timezones' => ['IntlExtension', 'twig/intl-extra'], - 'format_currency' => ['IntlExtension', 'twig/intl-extra'], - 'format_number' => ['IntlExtension', 'twig/intl-extra'], - 'format_decimal_number' => ['IntlExtension', 'twig/intl-extra'], - 'format_currency_number' => ['IntlExtension', 'twig/intl-extra'], - 'format_percent_number' => ['IntlExtension', 'twig/intl-extra'], - 'format_scientific_number' => ['IntlExtension', 'twig/intl-extra'], - 'format_spellout_number' => ['IntlExtension', 'twig/intl-extra'], - 'format_ordinal_number' => ['IntlExtension', 'twig/intl-extra'], - 'format_duration_number' => ['IntlExtension', 'twig/intl-extra'], - 'format_date' => ['IntlExtension', 'twig/intl-extra'], - 'format_datetime' => ['IntlExtension', 'twig/intl-extra'], - 'format_time' => ['IntlExtension', 'twig/intl-extra'], - ]; - - private const FUNCTIONS = [ - 'html_classes' => ['HtmlExtension', 'twig/html-extra'], - ]; - public function suggestFilter(string $name): bool { - if (isset(self::FILTERS[$name])) { - throw new SyntaxError(sprintf('The "%s" filter is part of the %s, which is not installed/enabled; try running "composer require %s twig/extra-bundle".', $name, self::FILTERS[$name][0], self::FILTERS[$name][1])); + if ($filter = Extensions::getFilter($name)) { + throw new SyntaxError(sprintf('The "%s" filter is part of the %s, which is not installed/enabled; try running "composer require %s twig/extra-bundle".', $name, $filter[0], $filter[1])); } return false; @@ -58,8 +26,8 @@ final class MissingExtensionSuggestor public function suggestFunction(string $name): bool { - if (isset(self::FUNCTIONS[$name])) { - throw new SyntaxError(sprintf('The "%s" function is part of the %s, which is not installed/enabled; try running "composer require %s twig/extra-bundle".', $name, self::FILTERS[$name][0], self::FILTERS[$name][1])); + if ($function = Extensions::getFunction($name)) { + throw new SyntaxError(sprintf('The "%s" function is part of the %s, which is not installed/enabled; try running "composer require %s twig/extra-bundle".', $name, $function[0], $function[1])); } return false; diff --git a/extra/twig-extra-bundle/src/Resources/config/cssinliner.xml b/extra/twig-extra-bundle/src/Resources/config/cssinliner.xml new file mode 100644 index 000000000..ca6d9a930 --- /dev/null +++ b/extra/twig-extra-bundle/src/Resources/config/cssinliner.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + diff --git a/extra/twig-extra-bundle/src/Resources/config/inky.xml b/extra/twig-extra-bundle/src/Resources/config/inky.xml new file mode 100644 index 000000000..a0bca2c55 --- /dev/null +++ b/extra/twig-extra-bundle/src/Resources/config/inky.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + +