From a4672a1fed2825665f3c9b7845c96d67308b0c4c Mon Sep 17 00:00:00 2001 From: Maurice Renck Date: Fri, 13 Jan 2023 17:34:02 +0100 Subject: [PATCH] feat: controll webmention sending on page --- blueprints/fields/block-webmentions.yml | 11 ++ composer.json | 5 +- composer.lock | 128 +++++++++++++++++++++++- index.php | 7 +- site/blueprints/pages/phpunit.yml | 4 +- tests/utils/senderTest.php | 92 +++++++++++------ utils/Sender.php | 38 +++++-- utils/WebmentionSender.php | 5 +- vendor/composer/ClassLoader.php | 37 ++++--- vendor/composer/autoload_classmap.php | 1 + vendor/composer/autoload_real.php | 27 ++--- vendor/composer/autoload_static.php | 1 + vendor/composer/installed.php | 8 +- 13 files changed, 276 insertions(+), 88 deletions(-) create mode 100644 blueprints/fields/block-webmentions.yml diff --git a/blueprints/fields/block-webmentions.yml b/blueprints/fields/block-webmentions.yml new file mode 100644 index 0000000..10bbf9e --- /dev/null +++ b/blueprints/fields/block-webmentions.yml @@ -0,0 +1,11 @@ +type: group +fields: + webmentionsStatus: + label: Webmentions + type: toggle + default: true + text: + - en: Webmentions deactivated + de: Webmentions deaktiviert + - en: Webmentions active + de: Webmentions aktiviert diff --git a/composer.json b/composer.json index c4a5a9d..12f7989 100644 --- a/composer.json +++ b/composer.json @@ -27,7 +27,8 @@ }, "require-dev": { "getkirby/cms": "^3.8", - "phpunit/phpunit": "^9.5" + "phpunit/phpunit": "^9.5", + "mockery/mockery": "^1.5" }, "replace": { "mauricerenck/tratschtante": "*" @@ -51,4 +52,4 @@ "npm run build" ] } -} \ No newline at end of file +} diff --git a/composer.lock b/composer.lock index 76f8b9e..451a5e7 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "f70b13a1d5041929f62a7fd10d45762c", + "content-hash": "9a714bb8ac466e5e6e029d91e801cbdb", "packages": [ { "name": "getkirby/composer-installer", @@ -534,6 +534,57 @@ ], "time": "2022-11-15T12:18:18+00:00" }, + { + "name": "hamcrest/hamcrest-php", + "version": "v2.0.1", + "source": { + "type": "git", + "url": "https://github.com/hamcrest/hamcrest-php.git", + "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/8c3d0a3f6af734494ad8f6fbbee0ba92422859f3", + "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3", + "shasum": "" + }, + "require": { + "php": "^5.3|^7.0|^8.0" + }, + "replace": { + "cordoval/hamcrest-php": "*", + "davedevelopment/hamcrest-php": "*", + "kodova/hamcrest-php": "*" + }, + "require-dev": { + "phpunit/php-file-iterator": "^1.4 || ^2.0", + "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.1-dev" + } + }, + "autoload": { + "classmap": [ + "hamcrest" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "This is the PHP port of Hamcrest Matchers", + "keywords": [ + "test" + ], + "support": { + "issues": "https://github.com/hamcrest/hamcrest-php/issues", + "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.0.1" + }, + "time": "2020-07-09T08:09:16+00:00" + }, { "name": "laminas/laminas-escaper", "version": "2.12.0", @@ -708,6 +759,78 @@ }, "time": "2016-12-13T01:01:17+00:00" }, + { + "name": "mockery/mockery", + "version": "1.5.1", + "source": { + "type": "git", + "url": "https://github.com/mockery/mockery.git", + "reference": "e92dcc83d5a51851baf5f5591d32cb2b16e3684e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mockery/mockery/zipball/e92dcc83d5a51851baf5f5591d32cb2b16e3684e", + "reference": "e92dcc83d5a51851baf5f5591d32cb2b16e3684e", + "shasum": "" + }, + "require": { + "hamcrest/hamcrest-php": "^2.0.1", + "lib-pcre": ">=7.0", + "php": "^7.3 || ^8.0" + }, + "conflict": { + "phpunit/phpunit": "<8.0" + }, + "require-dev": { + "phpunit/phpunit": "^8.5 || ^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4.x-dev" + } + }, + "autoload": { + "psr-0": { + "Mockery": "library/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Pádraic Brady", + "email": "padraic.brady@gmail.com", + "homepage": "http://blog.astrumfutura.com" + }, + { + "name": "Dave Marshall", + "email": "dave.marshall@atstsolutions.co.uk", + "homepage": "http://davedevelopment.co.uk" + } + ], + "description": "Mockery is a simple yet flexible PHP mock object framework", + "homepage": "https://github.com/mockery/mockery", + "keywords": [ + "BDD", + "TDD", + "library", + "mock", + "mock objects", + "mockery", + "stub", + "test", + "test double", + "testing" + ], + "support": { + "issues": "https://github.com/mockery/mockery/issues", + "source": "https://github.com/mockery/mockery/tree/1.5.1" + }, + "time": "2022-09-07T15:32:08+00:00" + }, { "name": "myclabs/deep-copy", "version": "1.11.0", @@ -2757,7 +2880,8 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": ">=8.0.0" + "php": ">=8.0.0", + "ext-curl": "*" }, "platform-dev": [], "plugin-api-version": "2.3.0" diff --git a/index.php b/index.php index 7089084..1dd4ba3 100644 --- a/index.php +++ b/index.php @@ -16,6 +16,9 @@ 'snippets' => [ 'activitypub-wm' => __DIR__ . '/snippets/activitypub-webmention.php', ], + 'blueprints' => [ + 'indieconnector/fields/webmentions' => __DIR__ . '/blueprints/fields/block-webmentions.yml', + ], 'routes' => [ [ 'pattern' => '(indieConnector|indieconnector)/webhook/webmentionio', @@ -87,8 +90,8 @@ [ 'pattern' => '^.well-known/((host-meta|webfinger).(:any)|(host-meta|webfinger))', 'method' => 'OPTIONS|GET|POST|PUT', - 'action' => function($file) { - if(!option('mauricerenck.indieConnector.activityPubBridge', false)) { + 'action' => function ($file) { + if (!option('mauricerenck.indieConnector.activityPubBridge', false)) { return false; } diff --git a/site/blueprints/pages/phpunit.yml b/site/blueprints/pages/phpunit.yml index 4cd9d9b..7ad184c 100644 --- a/site/blueprints/pages/phpunit.yml +++ b/site/blueprints/pages/phpunit.yml @@ -1,11 +1,13 @@ options: changeSlug: false - update: false + update: true sections: phpunit: type: fields fields: + indieConnetor: + extends: indieconnector/fields/webmentions textfield: type: textarea label: Text diff --git a/tests/utils/senderTest.php b/tests/utils/senderTest.php index 5c73ebb..7042892 100644 --- a/tests/utils/senderTest.php +++ b/tests/utils/senderTest.php @@ -1,15 +1,20 @@ getPageMock(); $senderUtils = new Sender(); $result = $senderUtils->pageHasNeededStatus($page); @@ -18,24 +23,16 @@ public function testPageHasNeededStatus() public function testStopsOnWrongNeededStatus() { - $page = page('phpunit'); - kirby()->impersonate('kirby'); - $unpublishedPage = $page->duplicate('phpunit-unpublished'); - $unpublishedPage->unpublish(); + $page = $this->getPageMock(true); $senderUtils = new Sender(); - $result = $senderUtils->pageHasNeededStatus($unpublishedPage); + $result = $senderUtils->pageHasNeededStatus($page); $this->assertFalse($result); - - kirby()->impersonate('kirby'); - $unpublishedPage->delete(); } public function testPageHasAllowedTemplate() { - $page = page('phpunit'); - $senderUtils = new Sender(); $result = $senderUtils->templateIsAllowed('phpunit'); @@ -44,8 +41,6 @@ public function testPageHasAllowedTemplate() public function testPageHasNotAllowedTemplate() { - $page = page('phpunit'); - $senderUtils = new Sender(); $result = $senderUtils->templateIsAllowed('nope'); @@ -54,8 +49,6 @@ public function testPageHasNotAllowedTemplate() public function testPageHasBlockedTemplate() { - $page = page('phpunit'); - $senderUtils = new Sender(); $result = $senderUtils->templateIsBlocked('blocked-template'); @@ -64,17 +57,45 @@ public function testPageHasBlockedTemplate() public function testPageHasNotBlockedTemplate() { - $page = page('phpunit'); - $senderUtils = new Sender(); $result = $senderUtils->templateIsBlocked('phpunit'); $this->assertFalse($result); } + public function testPageDisabledWebmentions() + { + $page = $this->getPageMock(false, ['webmentionsstatus' => false]); + + $senderUtils = new Sender(); + $result = $senderUtils->pageFullfillsCriteria($page); + + $this->assertFalse($result); + } + + public function testPageEnabledWebmentions() + { + $page = $this->getPageMock(false, ['webmentionsstatus' => true]); + + $senderUtils = new Sender(); + $result = $senderUtils->pageFullfillsCriteria($page); + + $this->assertTrue($result); + } + + public function testPageNotSetWebmentions() + { + $page = $this->getPageMock(); + + $senderUtils = new Sender(); + $result = $senderUtils->pageFullfillsCriteria($page); + + $this->assertFalse($result); + } + public function testShouldFindUrls() { - $page = page('phpunit'); + $page = $this->getPageMock(); $expectedUrls = [ 'https://text-field-url.tld', @@ -97,10 +118,18 @@ public function testShouldFindUrls() public function testShouldReturnProcessedUrls() { - $page = page('phpunit'); + $page = $this->getPageMock(); - $senderUtils = new Sender(); - $urls = $senderUtils->getProcessedUrls($page); + $fileMock = File::factory([ + 'parent' => $page, + 'filename' => 'indieConnector.json', + 'content' => ['["https://processed-url.tld"]'] + ]); + + $senderUtilsMock = Mockery::mock('mauricerenck\IndieConnector\Sender')->makePartial(); + $senderUtilsMock->shouldReceive('readOutbox')->andReturn($fileMock); + + $urls = $senderUtilsMock->getProcessedUrls($page); $this->assertCount(1, $urls); $this->assertEquals(['https://processed-url.tld'], $urls); @@ -108,15 +137,14 @@ public function testShouldReturnProcessedUrls() public function testShouldCleanupUrls() { - $page = page('phpunit'); - $sampleUrls = [ 'https://text-field-url.tld', 'https://processed-url.tld' ]; $senderUtils = new Sender(); - $urls = $senderUtils->cleanupUrls($sampleUrls, $page); + $processedUrls = ["https://processed-url.tld"]; + $urls = $senderUtils->cleanupUrls($sampleUrls, $processedUrls); $this->assertCount(1, $urls); $this->assertContains($sampleUrls[0], $urls); @@ -125,19 +153,17 @@ public function testShouldCleanupUrls() public function testShouldStoreProcessedUrls() { - $page = page('phpunit'); + $page = $this->getPageMock(); $sampleUrls = [ 'https://text-field-url.tld', 'https://processed-url.tld' ]; - $senderUtils = new Sender(); - $result = $senderUtils->storeProcessedUrls($sampleUrls, $page); - $this->assertTrue($result); + $senderUtilsMock = Mockery::mock('mauricerenck\IndieConnector\Sender')->makePartial(); + $senderUtilsMock->shouldReceive('writeOutbox')->andReturn(true); - // restore original state of file - $outboxFilePath = $page->root() . '/' . option('mauricerenck.indieConnector.outboxFilename', 'indieConnector.json'); - Data::write($outboxFilePath, ['https://processed-url.tld']); + $result = $senderUtilsMock->storeProcessedUrls($sampleUrls, $sampleUrls, $page); + $this->assertTrue($result); } } diff --git a/utils/Sender.php b/utils/Sender.php index 0ec6d81..b700a8a 100644 --- a/utils/Sender.php +++ b/utils/Sender.php @@ -2,6 +2,7 @@ namespace mauricerenck\IndieConnector; +use Kirby\Cms\File; use Kirby\Data\Data; use \IndieWeb\MentionClient; use file_exists; @@ -12,7 +13,6 @@ class Sender { - private $processed; private $fieldsToParseUrls; public function __construct() @@ -35,7 +35,9 @@ public function pageFullfillsCriteria($page) return false; } - // TODO make turning on/off sendmention from the panel page possible + if ($page->webmentionsStatus()->isFalse()) { + return false; + } return true; } @@ -62,16 +64,14 @@ public function templateIsBlocked($template) return (in_array($template, $blockList)); } - public function cleanupUrls($urls, $page) + public function cleanupUrls($urls, $processedUrls) { if (count($urls) === 0) { return []; } - $processedUrls = $this->getProcessedUrls($page); $cleanedUrls = []; - foreach ($urls as $url) { if (!in_array($url, $processedUrls)) { $cleanedUrls[] = $url; @@ -83,22 +83,22 @@ public function cleanupUrls($urls, $page) public function getProcessedUrls($page) { - $outboxFilePath = $page->root() . '/' . option('mauricerenck.indieConnector.outboxFilename', 'indieConnector.json'); - if (!file_exists($outboxFilePath)) { + $outboxFile = $this->readOutbox($page); + + if (is_null($outboxFile)) { return []; } try { - return Data::read($outboxFilePath); + return json_decode($outboxFile->content()->toArray()[0]); } catch (Exception $e) { return []; } } - public function storeProcessedUrls($urls, $page) + public function storeProcessedUrls($urls, $processedUrls, $page) { try { - $processedUrls = $this->getProcessedUrls($page); $combinedUrls = array_merge($processedUrls, $urls); $this->writeOutbox($combinedUrls, $page); @@ -185,9 +185,25 @@ private function parseLayoutFields($content) return join('', $htmlParts); } - private function writeOutbox($urls, $page) + public function readOutbox($page): File|null + { + $outboxFile = $page->file(option('mauricerenck.indieConnector.outboxFilename', 'indieConnector.json')); + + if (is_null($outboxFile)) { + return null; + } + + if (!$outboxFile->exists()) { + return null; + } + + return $outboxFile; + } + + public function writeOutbox($urls, $page) { $outboxFilePath = $page->root() . '/' . option('mauricerenck.indieConnector.outboxFilename', 'indieConnector.json'); Data::write($outboxFilePath, $urls); } + } diff --git a/utils/WebmentionSender.php b/utils/WebmentionSender.php index 3ccfc14..048e700 100644 --- a/utils/WebmentionSender.php +++ b/utils/WebmentionSender.php @@ -25,7 +25,8 @@ public function sendWebmentions($updatedPage) } $urls = $this->findUrls($updatedPage); - $cleanedUrls = $this->cleanupUrls($urls, $updatedPage); + $processedUrls = $this->getProcessedUrls($updatedPage); + $cleanedUrls = $this->cleanupUrls($urls, $processedUrls); if (count($cleanedUrls) === 0) { return; @@ -43,7 +44,7 @@ public function sendWebmentions($updatedPage) } } - $this->storeProcessedUrls($urls, $updatedPage); + $this->storeProcessedUrls($urls, $processedUrls, $updatedPage); } public function send(string $targetUrl, string $sourceUrl) diff --git a/vendor/composer/ClassLoader.php b/vendor/composer/ClassLoader.php index afef3fa..fd56bd7 100644 --- a/vendor/composer/ClassLoader.php +++ b/vendor/composer/ClassLoader.php @@ -42,6 +42,9 @@ */ class ClassLoader { + /** @var \Closure(string):void */ + private static $includeFile; + /** @var ?string */ private $vendorDir; @@ -106,6 +109,7 @@ class ClassLoader public function __construct($vendorDir = null) { $this->vendorDir = $vendorDir; + self::initializeIncludeClosure(); } /** @@ -425,7 +429,7 @@ public function unregister() public function loadClass($class) { if ($file = $this->findFile($class)) { - includeFile($file); + (self::$includeFile)($file); return true; } @@ -555,18 +559,23 @@ private function findFileWithExtension($class, $ext) return false; } -} -/** - * Scope isolated include. - * - * Prevents access to $this/self from included files. - * - * @param string $file - * @return void - * @private - */ -function includeFile($file) -{ - include $file; + private static function initializeIncludeClosure(): void + { + if (self::$includeFile !== null) { + return; + } + + /** + * Scope isolated include. + * + * Prevents access to $this/self from included files. + * + * @param string $file + * @return void + */ + self::$includeFile = static function($file) { + include $file; + }; + } } diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php index ef3f453..e72d9e0 100644 --- a/vendor/composer/autoload_classmap.php +++ b/vendor/composer/autoload_classmap.php @@ -16,6 +16,7 @@ 'mauricerenck\\IndieConnector\\HookHelper' => $baseDir . '/utils/hookHelper.php', 'mauricerenck\\IndieConnector\\MastodonSender' => $baseDir . '/utils/sendMastodon.php', 'mauricerenck\\IndieConnector\\Sender' => $baseDir . '/utils/Sender.php', + 'mauricerenck\\IndieConnector\\TestCaseMocked' => $baseDir . '/utils/TestCaseMocked.php', 'mauricerenck\\IndieConnector\\WebmentionReceiver' => $baseDir . '/utils/receiver.php', 'mauricerenck\\IndieConnector\\WebmentionSender' => $baseDir . '/utils/WebmentionSender.php', 'mauricerenck\\IndieConnector\\WebmentionStats' => $baseDir . '/utils/stats.php', diff --git a/vendor/composer/autoload_real.php b/vendor/composer/autoload_real.php index 81e0e3b..193ca06 100644 --- a/vendor/composer/autoload_real.php +++ b/vendor/composer/autoload_real.php @@ -33,25 +33,18 @@ public static function getLoader() $loader->register(true); - $includeFiles = \Composer\Autoload\ComposerStaticInitd2f181dd341adfd58b96ccab3bed9890::$files; - foreach ($includeFiles as $fileIdentifier => $file) { - composerRequired2f181dd341adfd58b96ccab3bed9890($fileIdentifier, $file); + $filesToLoad = \Composer\Autoload\ComposerStaticInitd2f181dd341adfd58b96ccab3bed9890::$files; + $requireFile = static function ($fileIdentifier, $file) { + if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { + $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; + + require $file; + } + }; + foreach ($filesToLoad as $fileIdentifier => $file) { + ($requireFile)($fileIdentifier, $file); } return $loader; } } - -/** - * @param string $fileIdentifier - * @param string $file - * @return void - */ -function composerRequired2f181dd341adfd58b96ccab3bed9890($fileIdentifier, $file) -{ - if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { - $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; - - require $file; - } -} diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php index caa7b60..7abef54 100644 --- a/vendor/composer/autoload_static.php +++ b/vendor/composer/autoload_static.php @@ -53,6 +53,7 @@ class ComposerStaticInitd2f181dd341adfd58b96ccab3bed9890 'mauricerenck\\IndieConnector\\HookHelper' => __DIR__ . '/../..' . '/utils/hookHelper.php', 'mauricerenck\\IndieConnector\\MastodonSender' => __DIR__ . '/../..' . '/utils/sendMastodon.php', 'mauricerenck\\IndieConnector\\Sender' => __DIR__ . '/../..' . '/utils/Sender.php', + 'mauricerenck\\IndieConnector\\TestCaseMocked' => __DIR__ . '/../..' . '/utils/TestCaseMocked.php', 'mauricerenck\\IndieConnector\\WebmentionReceiver' => __DIR__ . '/../..' . '/utils/receiver.php', 'mauricerenck\\IndieConnector\\WebmentionSender' => __DIR__ . '/../..' . '/utils/WebmentionSender.php', 'mauricerenck\\IndieConnector\\WebmentionStats' => __DIR__ . '/../..' . '/utils/stats.php', diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php index 0b7582d..739daed 100644 --- a/vendor/composer/installed.php +++ b/vendor/composer/installed.php @@ -1,8 +1,8 @@ array( 'name' => 'mauricerenck/indieconnector', - 'pretty_version' => '1.6.0', - 'version' => '1.6.0.0', + 'pretty_version' => '1.6.3', + 'version' => '1.6.3.0', 'reference' => NULL, 'type' => 'kirby-plugin', 'install_path' => __DIR__ . '/../../', @@ -29,8 +29,8 @@ 'dev_requirement' => false, ), 'mauricerenck/indieconnector' => array( - 'pretty_version' => '1.6.0', - 'version' => '1.6.0.0', + 'pretty_version' => '1.6.3', + 'version' => '1.6.3.0', 'reference' => NULL, 'type' => 'kirby-plugin', 'install_path' => __DIR__ . '/../../',