From 9b710aa921e9432ade54a12f5ad5f73c468ea9d1 Mon Sep 17 00:00:00 2001 From: Daniel Huf Date: Thu, 14 Dec 2017 19:08:22 +0100 Subject: [PATCH 1/8] [TASK] Escape and clean code --- Classes/View/MjmlBasedView.php | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/Classes/View/MjmlBasedView.php b/Classes/View/MjmlBasedView.php index bfbd673..53fcce3 100644 --- a/Classes/View/MjmlBasedView.php +++ b/Classes/View/MjmlBasedView.php @@ -3,12 +3,13 @@ namespace Saccas\Mjml\View; use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\CMS\Extbase\Mvc\Cli\Command; use TYPO3\CMS\Fluid\View\StandaloneView; use TYPO3\CMS\Core\Utility\CommandUtility; class MjmlBasedView extends StandaloneView { - function render() + function render($actionName = null) { return $this->getHtmlFromMjml(parent::render()); } @@ -18,17 +19,34 @@ class MjmlBasedView extends StandaloneView $configuration = unserialize($GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf']['mjml']); $temporaryMjmlFileWithPath = GeneralUtility::tempnam('mjml_', '.mjml'); - $mjmlFile = fopen($temporaryMjmlFileWithPath, 'w'); - fwrite($mjmlFile, $mjml); - fclose($mjmlFile); + + GeneralUtility::writeFileToTypo3tempDir($temporaryMjmlFileWithPath, $mjml); // see https://mjml.io/download and https://www.npmjs.com/package/mjml-cli - $cmd = $configuration['nodeBinaryPath'] . ' ' . $configuration['mjmlBinaryPath'] . $configuration['mjmlBinary'] .' ' . $configuration['mjmlParams'] . ' ' . $temporaryMjmlFileWithPath; + $cmd = $configuration['nodeBinaryPath'] . ' ' . $configuration['mjmlBinaryPath'] . $configuration['mjmlBinary']; + $args = $configuration['mjmlParams'] . ' ' . $temporaryMjmlFileWithPath; $result = []; $returnValue = ''; - CommandUtility::exec($cmd, $result, $returnValue); + CommandUtility::exec($this->getEscapedCommand($cmd, $args), $result, $returnValue); + + GeneralUtility::unlink_tempfile($temporaryMjmlFileWithPath); return implode('',$result); } + + /** + * @param string $cmd + * @param string $args + * @return string + */ + private function getEscapedCommand(string $cmd, string $args) { + $escapedCmd = escapeshellcmd($cmd); + + $argsArray = explode(' ', $args); + $escapedArgsArray = CommandUtility::escapeShellArguments($argsArray); + $escapedArgs = implode(' ', $escapedArgsArray); + + return $escapedCmd . ' ' . $escapedArgs; + } } From d1bc4870f7d89293dfeb6bef02236238cb17540e Mon Sep 17 00:00:00 2001 From: Daniel Siepmann Date: Thu, 14 Dec 2017 22:30:49 +0100 Subject: [PATCH 2/8] TASK: Provide phpcs integration for CGL Use common accepted PSR2 for CGL checks. --- composer.json | 9 ++++++++- phpcs.xml.dist | 17 +++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 phpcs.xml.dist diff --git a/composer.json b/composer.json index ddbff51..02de3f6 100644 --- a/composer.json +++ b/composer.json @@ -12,9 +12,16 @@ }, "require": { "php": ">=7.0.0 <=7.2.99", - "typo3/cms-core": ">=8.7.0,<8.9.99" + "typo3/cms-core": "^8.7.0", + "typo3/cms-form": "^8.7.0" + }, + "require-dev": { + "squizlabs/php_codesniffer": "^3.2.0" }, "scripts": { + "lint": [ + "./vendor/bin/phpcs" + ], "post-install-cmd": [ "npm install" ] diff --git a/phpcs.xml.dist b/phpcs.xml.dist new file mode 100644 index 0000000..4bf161b --- /dev/null +++ b/phpcs.xml.dist @@ -0,0 +1,17 @@ + + + The coding standard for this extension. + + ./Classes/ + + + + + + + + + + + + From c11283119bde426298b0797c03ab1c725263f5a8 Mon Sep 17 00:00:00 2001 From: Daniel Siepmann Date: Thu, 14 Dec 2017 22:32:08 +0100 Subject: [PATCH 3/8] TASK: Fix CGL Issues Fix issues in view class. Do not print progress while linting. --- Classes/View/MjmlBasedView.php | 7 ++++--- phpcs.xml.dist | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Classes/View/MjmlBasedView.php b/Classes/View/MjmlBasedView.php index 53fcce3..a18f515 100644 --- a/Classes/View/MjmlBasedView.php +++ b/Classes/View/MjmlBasedView.php @@ -9,7 +9,7 @@ use TYPO3\CMS\Core\Utility\CommandUtility; class MjmlBasedView extends StandaloneView { - function render($actionName = null) + public function render($actionName = null) { return $this->getHtmlFromMjml(parent::render()); } @@ -32,7 +32,7 @@ class MjmlBasedView extends StandaloneView GeneralUtility::unlink_tempfile($temporaryMjmlFileWithPath); - return implode('',$result); + return implode('', $result); } /** @@ -40,7 +40,8 @@ class MjmlBasedView extends StandaloneView * @param string $args * @return string */ - private function getEscapedCommand(string $cmd, string $args) { + private function getEscapedCommand(string $cmd, string $args) + { $escapedCmd = escapeshellcmd($cmd); $argsArray = explode(' ', $args); diff --git a/phpcs.xml.dist b/phpcs.xml.dist index 4bf161b..61e813a 100644 --- a/phpcs.xml.dist +++ b/phpcs.xml.dist @@ -5,7 +5,7 @@ ./Classes/ - + From 227ca8a0352a398ff7f5c1abd9e18cccd6997358 Mon Sep 17 00:00:00 2001 From: Daniel Siepmann Date: Thu, 14 Dec 2017 22:42:05 +0100 Subject: [PATCH 4/8] TASK: Add php linting To make sure there are no syntax errors in files. --- .travis.yml | 14 ++++++++++++++ composer.json | 4 ++++ 2 files changed, 18 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..4b49b98 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,14 @@ +language: php + +php: + - 5.6 + - 7.0 + - 7.1 + - 7.2 + +install: + - composer install + +script: + - composer lint + - composer cgl diff --git a/composer.json b/composer.json index 02de3f6..725fa2b 100644 --- a/composer.json +++ b/composer.json @@ -20,6 +20,10 @@ }, "scripts": { "lint": [ + "! find Classes -type f -name \"*.php\" -exec php -d error_reporting=32767 -l {} \\; 2>&1 >&- | grep \"^\"", + "! find Tests -type f -name \"*.php\" -exec php -d error_reporting=32767 -l {} \\; 2>&1 >&- | grep \"^\"" + ], + "cgl": [ "./vendor/bin/phpcs" ], "post-install-cmd": [ From eba401aa9fc419958f5af665f95a40b30bb28347 Mon Sep 17 00:00:00 2001 From: Daniel Siepmann Date: Thu, 14 Dec 2017 23:06:08 +0100 Subject: [PATCH 5/8] TASK: Add first test Add all dependencies to run tests. Configure tests as composer task. Add tests to travis. Adjust code to make him exchangeable and testable. --- .gitignore | 4 ++ .travis.yml | 1 + .../Domain/Finishers/MjmlEmailFinisher.php | 14 ----- Classes/Domain/Renderer/Command.php | 45 ++++++++++++++ Classes/Domain/Renderer/RendererInterface.php | 16 +++++ Classes/View/MjmlBasedView.php | 60 ++++++------------- Tests/Unit/AbstractUnitTestCase.php | 22 +++++++ Tests/Unit/View/MjmlBasedViewTest.php | 55 +++++++++++++++++ composer.json | 18 +++++- phpunit.xml.dist | 28 +++++++++ 10 files changed, 206 insertions(+), 57 deletions(-) create mode 100644 Classes/Domain/Renderer/Command.php create mode 100644 Classes/Domain/Renderer/RendererInterface.php create mode 100644 Tests/Unit/AbstractUnitTestCase.php create mode 100644 Tests/Unit/View/MjmlBasedViewTest.php create mode 100644 phpunit.xml.dist diff --git a/.gitignore b/.gitignore index a11232f..cb300fb 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,7 @@ .sass-cache node_modules bower_components +composer.lock +package-lock.json +typo3 +vendor diff --git a/.travis.yml b/.travis.yml index 4b49b98..932656a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,3 +12,4 @@ install: script: - composer lint - composer cgl + - composer test diff --git a/Classes/Domain/Finishers/MjmlEmailFinisher.php b/Classes/Domain/Finishers/MjmlEmailFinisher.php index 6fd9876..a2321f9 100644 --- a/Classes/Domain/Finishers/MjmlEmailFinisher.php +++ b/Classes/Domain/Finishers/MjmlEmailFinisher.php @@ -1,20 +1,6 @@ getEscapedCommand($cmd, $args), $result, $returnValue); + + GeneralUtility::unlink_tempfile($temporaryMjmlFileWithPath); + + return implode('', $result); + } + + /** + * @param string $cmd + * @param string $args + * @return string + */ + protected function getEscapedCommand(string $cmd, string $args) + { + $escapedCmd = escapeshellcmd($cmd); + + $argsArray = explode(' ', $args); + $escapedArgsArray = CommandUtility::escapeShellArguments($argsArray); + $escapedArgs = implode(' ', $escapedArgsArray); + + return $escapedCmd . ' ' . $escapedArgs; + } +} diff --git a/Classes/Domain/Renderer/RendererInterface.php b/Classes/Domain/Renderer/RendererInterface.php new file mode 100644 index 0000000..5cef1b2 --- /dev/null +++ b/Classes/Domain/Renderer/RendererInterface.php @@ -0,0 +1,16 @@ +renderer = $renderer; + if ($this->renderer === null) { + $this->renderer = $this->objectManager->get(RendererInterface::class); + } + } + public function render($actionName = null) { - return $this->getHtmlFromMjml(parent::render()); - } - - protected function getHtmlFromMjml($mjml) - { - $configuration = unserialize($GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf']['mjml']); - - $temporaryMjmlFileWithPath = GeneralUtility::tempnam('mjml_', '.mjml'); - - GeneralUtility::writeFileToTypo3tempDir($temporaryMjmlFileWithPath, $mjml); - - // see https://mjml.io/download and https://www.npmjs.com/package/mjml-cli - $cmd = $configuration['nodeBinaryPath'] . ' ' . $configuration['mjmlBinaryPath'] . $configuration['mjmlBinary']; - $args = $configuration['mjmlParams'] . ' ' . $temporaryMjmlFileWithPath; - - $result = []; - $returnValue = ''; - CommandUtility::exec($this->getEscapedCommand($cmd, $args), $result, $returnValue); - - GeneralUtility::unlink_tempfile($temporaryMjmlFileWithPath); - - return implode('', $result); - } - - /** - * @param string $cmd - * @param string $args - * @return string - */ - private function getEscapedCommand(string $cmd, string $args) - { - $escapedCmd = escapeshellcmd($cmd); - - $argsArray = explode(' ', $args); - $escapedArgsArray = CommandUtility::escapeShellArguments($argsArray); - $escapedArgs = implode(' ', $escapedArgsArray); - - return $escapedCmd . ' ' . $escapedArgs; + return $this->renderer->getHtmlFromMjml(parent::render($actionName)); } } diff --git a/Tests/Unit/AbstractUnitTestCase.php b/Tests/Unit/AbstractUnitTestCase.php new file mode 100644 index 0000000..814e944 --- /dev/null +++ b/Tests/Unit/AbstractUnitTestCase.php @@ -0,0 +1,22 @@ +setCacheConfigurations([ + 'extbase_object' => [ + 'backend' => NullBackend::class, + ], + ]); + } +} diff --git a/Tests/Unit/View/MjmlBasedViewTest.php b/Tests/Unit/View/MjmlBasedViewTest.php new file mode 100644 index 0000000..5648444 --- /dev/null +++ b/Tests/Unit/View/MjmlBasedViewTest.php @@ -0,0 +1,55 @@ + + + + + + + Easy and Quick + + + + Responsive + + + + + Discover + + + + + +'; + + /** + * @test + */ + public function viewCallsRendererAndReturnsRenderedHtml() + { + $expectedHtml = '

Simple HTML

'; + $rendererMock = $this->getMockBuilder(RendererInterface::class)->getMock(); + $rendererMock->expects($this->once()) + ->method('getHtmlFromMjml') + ->with(static::EXAMPLE_MJML_TEMPLATE) + ->willReturn($expectedHtml); + + $subject = new MjmlBasedView(null, $rendererMock); + $subject->setTemplateSource(static::EXAMPLE_MJML_TEMPLATE); + $result = $subject->render(); + + $this->assertSame( + $expectedHtml, + $result, + 'Rendering of view did not return expected HTML.' + ); + } +} diff --git a/composer.json b/composer.json index 725fa2b..1300dd2 100644 --- a/composer.json +++ b/composer.json @@ -10,13 +10,26 @@ "Saccas\\Mjml\\": "Classes/" } }, + "autoload-dev": { + "psr-4": { + "Saccas\\Mjml\\Tests\\": "Tests/" + } + }, "require": { "php": ">=7.0.0 <=7.2.99", "typo3/cms-core": "^8.7.0", "typo3/cms-form": "^8.7.0" }, "require-dev": { - "squizlabs/php_codesniffer": "^3.2.0" + "squizlabs/php_codesniffer": "^3.2.0", + "typo3/cms": "^8.7.0", + "typo3/testing-framework": "^1.2.2" + }, + "extra": { + "typo3/cms": { + "cms-package-dir": "{$vendor-dir}/typo3/cms", + "web-dir": "typo3" + } }, "scripts": { "lint": [ @@ -26,6 +39,9 @@ "cgl": [ "./vendor/bin/phpcs" ], + "test": [ + "TYPO3_PATH_ROOT=typo3 ./vendor/bin/phpunit" + ], "post-install-cmd": [ "npm install" ] diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 0000000..0455ed1 --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,28 @@ + + + + + ./Tests/Unit/ + + + + + + Classes + + + From b6caff30dbbd70d0440168eafb0bd7082220171a Mon Sep 17 00:00:00 2001 From: Daniel Huf Date: Thu, 14 Dec 2017 19:08:22 +0100 Subject: [PATCH 6/8] [TASK] Escape and clean code --- Classes/View/MjmlBasedView.php | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/Classes/View/MjmlBasedView.php b/Classes/View/MjmlBasedView.php index bfbd673..baad7e1 100644 --- a/Classes/View/MjmlBasedView.php +++ b/Classes/View/MjmlBasedView.php @@ -3,14 +3,15 @@ namespace Saccas\Mjml\View; use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\CMS\Extbase\Mvc\Cli\Command; use TYPO3\CMS\Fluid\View\StandaloneView; use TYPO3\CMS\Core\Utility\CommandUtility; class MjmlBasedView extends StandaloneView { - function render() + public function render($actionName = null) { - return $this->getHtmlFromMjml(parent::render()); + return $this->getHtmlFromMjml(parent::render($actionName)); } protected function getHtmlFromMjml($mjml) @@ -18,17 +19,34 @@ class MjmlBasedView extends StandaloneView $configuration = unserialize($GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf']['mjml']); $temporaryMjmlFileWithPath = GeneralUtility::tempnam('mjml_', '.mjml'); - $mjmlFile = fopen($temporaryMjmlFileWithPath, 'w'); - fwrite($mjmlFile, $mjml); - fclose($mjmlFile); + + GeneralUtility::writeFileToTypo3tempDir($temporaryMjmlFileWithPath, $mjml); // see https://mjml.io/download and https://www.npmjs.com/package/mjml-cli - $cmd = $configuration['nodeBinaryPath'] . ' ' . $configuration['mjmlBinaryPath'] . $configuration['mjmlBinary'] .' ' . $configuration['mjmlParams'] . ' ' . $temporaryMjmlFileWithPath; + $cmd = $configuration['nodeBinaryPath'] . ' ' . $configuration['mjmlBinaryPath'] . $configuration['mjmlBinary']; + $args = $configuration['mjmlParams'] . ' ' . $temporaryMjmlFileWithPath; $result = []; $returnValue = ''; - CommandUtility::exec($cmd, $result, $returnValue); + CommandUtility::exec($this->getEscapedCommand($cmd, $args), $result, $returnValue); + + GeneralUtility::unlink_tempfile($temporaryMjmlFileWithPath); return implode('',$result); } + + /** + * @param string $cmd + * @param string $args + * @return string + */ + private function getEscapedCommand(string $cmd, string $args) { + $escapedCmd = escapeshellcmd($cmd); + + $argsArray = explode(' ', $args); + $escapedArgsArray = CommandUtility::escapeShellArguments($argsArray); + $escapedArgs = implode(' ', $escapedArgsArray); + + return $escapedCmd . ' ' . $escapedArgs; + } } From b1a385fc6dcf39b6b8c8a6d67e96fca2b8d75d7d Mon Sep 17 00:00:00 2001 From: Daniel Huf Date: Fri, 15 Dec 2017 21:46:24 +0100 Subject: [PATCH 7/8] [TASK] Add travis.yml --- .travis.yml | 38 ++++++++++++++++++++++++++++++++++++++ composer.json | 11 +++++++++++ 2 files changed, 49 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..274b6b3 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,38 @@ +dist: trusty +sudo: false +language: php + +cache: + directories: + - $HOME/.composer/cache + +install: + - composer require typo3/cms="$TYPO3_VERSION" + +jobs: + include: + - php: 7.1 + env: TYPO3_VERSION=^8.7 + - php: 7.2 + env: TYPO3_VERSION=^8.7 + + - stage: deploy + if: tag IS present + php: 7.1 + before_install: skip + install: skip + before_script: skip + script: | + echo -e "Preparing upload of release ${TRAVIS_TAG} to TER\n" + + TAG_ANNOTATION="$(git tag -n -l $TRAVIS_TAG)" + TAG_MESSAGE="${TAG_ANNOTATION#* }" + + git reset --hard + git clean -xfd + + export PATH=$PATH:$(composer global config bin-dir --absolute 2>/dev/null) + composer global require helhum/ter-client dev-master + + echo "Uploading release ${TRAVIS_TAG} to TER" + ter-client upload $(composer config extra.typo3/cms.extension-key) . -u "$TYPO3_ORG_USERNAME" -p "$TYPO3_ORG_PASSWORD" -m "$TAG_MESSAGE" diff --git a/composer.json b/composer.json index ddbff51..6f4372d 100644 --- a/composer.json +++ b/composer.json @@ -18,5 +18,16 @@ "post-install-cmd": [ "npm install" ] + }, + "replace": { + "mjml": "self.version", + "typo3-ter/mjml": "self.version" + }, + "extra": { + "typo3/cms": { + "extension-key": "mjml", + "cms-package-dir": "{$vendor-dir}/typo3/cms", + "web-dir": "web" + } } } From 4794287b2d7aa716251dd9d8ec2973ec75103169 Mon Sep 17 00:00:00 2001 From: Daniel Siepmann Date: Thu, 14 Dec 2017 23:26:39 +0100 Subject: [PATCH 8/8] TASK: Add test for command renderer --- Classes/Domain/Renderer/Command.php | 2 +- Tests/Unit/Domain/Renderer/CommandTest.php | 66 ++++++++++++++++++++++ phpunit.xml.dist | 4 +- 3 files changed, 69 insertions(+), 3 deletions(-) create mode 100644 Tests/Unit/Domain/Renderer/CommandTest.php diff --git a/Classes/Domain/Renderer/Command.php b/Classes/Domain/Renderer/Command.php index 0dab6bd..b6ec2f9 100644 --- a/Classes/Domain/Renderer/Command.php +++ b/Classes/Domain/Renderer/Command.php @@ -6,7 +6,7 @@ use TYPO3\CMS\Core\Utility\GeneralUtility; class Command implements RendererInterface { - protected function getHtmlFromMjml($mjml) + public function getHtmlFromMjml($mjml) { $configuration = unserialize($GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf']['mjml']); diff --git a/Tests/Unit/Domain/Renderer/CommandTest.php b/Tests/Unit/Domain/Renderer/CommandTest.php new file mode 100644 index 0000000..aa7a7fb --- /dev/null +++ b/Tests/Unit/Domain/Renderer/CommandTest.php @@ -0,0 +1,66 @@ + + + + + + + Easy and Quick + + + + Responsive + + + + + Discover + + + + + +'; + + public function setUp() + { + parent::setUp(); + $this->objectManager = GeneralUtility::makeInstance(ObjectManager::class); + } + + /** + * @test + */ + public function htmlIsReturnedForMjml() + { + $expectedHtml = '
Easy and Quick
Responsive

Discover

'; + $GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf']['mjml'] = serialize([ + 'nodeBinaryPath' => 'node', + 'mjmlBinaryPath' => 'node_modules/.bin/', + 'mjmlBinary' => 'mjml', + 'mjmlParams' => '-s -m', + ]); + + $subject = $this->objectManager->get(Command::class); + $html = $subject->getHtmlFromMjml(static::EXAMPLE_MJML_TEMPLATE); + $this->assertSame( + $expectedHtml, + $html, + 'Command renderer did not return expected HTML.' + ); + } +} diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 0455ed1..f8dad0b 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -16,13 +16,13 @@ - ./Tests/Unit/ + ./Tests/Unit - Classes + ./Classes