diff --git a/source/php/Config/Feature.php b/source/php/Config/Feature.php index ab649e0..aba58c1 100644 --- a/source/php/Config/Feature.php +++ b/source/php/Config/Feature.php @@ -18,7 +18,7 @@ class Feature implements FeatureInterface { 'admin_highlight_links' => 1, //On save autofixer - 'fix_internal_links' => false, + 'fix_internal_links' => 1, 'maintain_link_registry' => 1, //CLI Features diff --git a/source/php/LinkUpdater/LinkUpdater.php b/source/php/LinkUpdater/LinkUpdater.php index 9ffe10f..e5e402b 100644 --- a/source/php/LinkUpdater/LinkUpdater.php +++ b/source/php/LinkUpdater/LinkUpdater.php @@ -1,5 +1,4 @@ -<?php - +<?php namespace BrokenLinkDetector\LinkUpdater; @@ -7,115 +6,150 @@ use BrokenLinkDetector\Config\Config; use BrokenLinkDetector\Database\Database; use BrokenLinkDetector\HooksRegistrar\Hookable; +use WP_Post; class LinkUpdater implements LinkUpdaterInterface, Hookable { - public function __construct(private WpService $wpService, private Config $config, private Database $database) - { + private ?string $storedPermalink = null; + + public function __construct(private WpService $wpService, private Config $config, private Database $database) + { + } + + /** + * Add hooks for the link updater + * @return void + */ + public function addHooks(): void + { + //Fetches the previous permalink before the post is updated + $this->wpService->addAction('pre_post_update', [$this, 'beforeUpdateLinks'], 10, 2); + + //Updates the links in the post content if the post name has changed + $this->wpService->addAction('post_updated', [$this, 'updateLinks'], 10, 3); + } + + /** + * Before the post is updated, store the post data + * @param int $postId + * @param array $post + * @return void + */ + public function beforeUpdateLinks(int $postId, array $post): void + { + $this->storedPermalink = $this->wpService->getPermalink($postId); + } + + /** + * Update the links in the post content if the post name has changed + * @param int $id + * @param WP_Post $postBefore + * @param WP_Post $postAfter + * @return void + */ + public function updateLinks(int $postId, WP_Post $postBefore, WP_Post $postAfter): void + { + $previousPermalink = $this->storedPermalink; + $currentPermalink = $this->wpService->getPermalink($postId); + + if (!$this->isValidReplaceRequest($previousPermalink, $currentPermalink)) { + return; } - /** - * Add hooks for the link updater - * @return void - */ - public function addHooks(): void - { - $this->wpService->addFilter('wp_insert_post_data', array($this, 'updateLinks'), 10, 2); + if ($this->shouldReplaceForPosttype($postAfter->post_type)) { + $this->replaceLinks($previousPermalink, $currentPermalink); + } + } + + /** + * Validate the replace request + * @param string $oldLink + * @param string $newLink + * @return bool + */ + private function isValidReplaceRequest(string $oldLink, string $newLink): bool + { + //Ensure that the old link is not the same as the new link + if ($oldLink === $newLink) { + return false; } - /** - * Update the links in the post content if the post name has changed - * @param array $data - * @param array $post - * @return bool - */ - public function updateLinks(array $data, array $post): array - { - if(is_null($post)) { - return $data; - } - - foreach(['post_type', 'post_name'] as $keys) { - if(!isset($data[$keys]) || !isset($post[$keys])) { - return $data; - } - } - - if($this->linkHasChanged($data, $post) && $this->shouldReplaceForPosttype($data['post_type'])) { - - $postId = $post['ID'] ?? null; - - if(is_numeric($postId)) { - $this->replaceLinks( - $this->createPermalink($postId, $data['post_name']), - $this->createPermalink($postId, $post['post_name']) - ); - } - } - return $data; + //Ensure that the old link is not empty + if (empty($oldLink)) { + return false; } - /** - * Replace the old link with the new link in posts that contains the link - * @param string $newLink - * @param string $oldLink - * @return int - */ - private function replaceLinks(string $newLink, string $oldLink): int - { + //Ensure that the new link is not empty + if (empty($newLink)) { + return false; + } + //Ensure that the old link is not a home url + if ($this->wpService->homeUrl() === $oldLink) { + return false; + } + + return true; + } + + /** + * Replace the old link with the new link in the post content + * @param string $oldLink + * @param string $newLink + * @return int + */ + private function replaceLinks(string $oldLink, string $newLink): int + { $db = $this->database->getInstance(); - $db->query( - $db->prepare( - "UPDATE $db->posts - SET post_content = REPLACE(post_content, %s, %s) - WHERE post_content LIKE %s", - $oldLink, - $newLink, - '%' . $db->esc_like($oldLink) . '%' - ) + //Get the post ids that contain the old link + $postIds = $db->get_col( + $db->prepare( + "SELECT ID + FROM $db->posts + WHERE post_content LIKE %s", + '%' . $db->esc_like($oldLink) . '%' + ) ); - return $db->rows_affected; - } - - /** - * Create a permalink from the post id and post name - * @param int $postId - * @param string $postName - * @return string - */ - public function createPermalink(int $postId, string $postName): string - { - $permalink = preg_replace('/[^\/]+\/?$/', - $postName, - $this->wpService->getPermalink($postId) - ); - - $permalink = rtrim($permalink, '/'); - - return $permalink; - } + //Update the post content + $db->query( + $db->prepare( + "UPDATE $db->posts + SET post_content = REPLACE(post_content, %s, %s) + WHERE post_content LIKE %s", + $oldLink, + $newLink, + '%' . $db->esc_like($oldLink) . '%' + ) + ); - /** - * Check if the link has changed - * @param array $data The newly submitted data - * @param array $post The stored post data - * @return bool - */ - public function linkHasChanged(array $data, array $post): bool - { - return $data['post_name'] !== $post['post_name']; - } + //Clear the object cache for the post ids + $this->clearObjectCache($postIds); - /** - * Check if the link should be replaced for the post type - * @param string $postType - * @return bool - */ - private function shouldReplaceForPosttype(string $postType): bool - { - return !in_array($postType, $this->config->linkUpdaterBannedPostTypes()); + //Return the number of rows affected + return $db->rows_affected; + } + + /** + * Clear the object cache for the post ids + * + * @param array $postIds + */ + private function clearObjectCache(array $postIds): void + { + foreach ($postIds as $postId) { + $this->wpService->cleanPostCache($postId); } + } + + /** + * Check if the post type should be replaced + * @param string $postType + * @return bool + */ + private function shouldReplaceForPosttype(string $postType): bool + { + return !in_array($postType, $this->config->linkUpdaterBannedPostTypes()); + } } \ No newline at end of file diff --git a/source/php/LinkUpdater/LinkUpdater.test.php b/source/php/LinkUpdater/LinkUpdater.test.php deleted file mode 100644 index 13ba14c..0000000 --- a/source/php/LinkUpdater/LinkUpdater.test.php +++ /dev/null @@ -1,114 +0,0 @@ -<?php - -namespace BrokenLinkDetector\LinkUpdater; - -use PHPUnit\Framework\TestCase; -use WpService\Implementations\FakeWpService; -use BrokenLinkDetector\Config\Config; -use BrokenLinkDetector\Database\Database; -use AcfService\Implementations\FakeAcfService; - -class LinkUpdaterTest extends TestCase -{ - /** - * @testdox Test that createPermalink generates a correct permalink based on postName - * - * @dataProvider uriProvider - */ - public function testCreatePermalink($input, $expected, $postName): void - { - // Arrange - $wpService = new FakeWpService([ - 'getPermalink' => $input, - 'getOption' => 'option' - ]); - $acfService = new FakeAcfService([]); - - $config = new Config( - $wpService, - $acfService, - 'filter-prefix', - 'plugin-path', - 'plugin-url' - ); - - $postId = 123; - - $linkUpdater = new LinkUpdater( - $wpService, - $config, - new Database( - $config, - $wpService - ) - ); - - // Act - $result = $linkUpdater->createPermalink($postId, $postName); - - // Assert - $this->assertEquals($expected, $result); - } - - /** - * Test urls. - */ - public function testThatLinkHasChangedDetectsLinksThatChanges() - { - // Arrange - $wpService = new FakeWpService([ - 'getPermalink' => 'https://example.com/old-permalink/', - 'getOption' => 'option', - 'applyFilters' => function($filter, $value) { - return $value; - } - ]); - - $acfService = new FakeAcfService([]); - - $config = new Config( - $wpService, - $acfService, - 'filter-prefix', - 'plugin-path', - 'plugin-url' - ); - - $linkUpdater = new LinkUpdater( - $wpService, - $config, - new Database( - $config, - $wpService - ) - ); - - $data = [ - 'post_name' => 'sample-post', - 'post_type' => 'post' - ]; - - $post = [ - 'ID' => 123, - 'post_name' => 'old-post', - 'post_type' => 'post' - ]; - - $result = $linkUpdater->linkHasChanged($data, $post); - - // Assert - $this->assertTrue($result); - } - - /** - * Test urls. - */ - private function uriProvider() - { - return [ - ['https://example.com/old-permalink/', 'https://example.com/sample-post', 'sample-post'], - ['https://example.com/old-permalink', 'https://example.com/sample-post', 'sample-post'], - ['https://example.com/old-permalink/with/multiple/subs', 'https://example.com/old-permalink/with/multiple/sample-post', 'sample-post'] - ]; - } -} \ No newline at end of file diff --git a/source/php/LinkUpdater/LinkUpdaterInterface.php b/source/php/LinkUpdater/LinkUpdaterInterface.php index 62b8739..f632e16 100644 --- a/source/php/LinkUpdater/LinkUpdaterInterface.php +++ b/source/php/LinkUpdater/LinkUpdaterInterface.php @@ -1,8 +1,8 @@ <?php namespace BrokenLinkDetector\LinkUpdater; - +use WP_Post; interface LinkUpdaterInterface { - public function updateLinks(array $data, array $post): array; + public function updateLinks(int $postId, WP_Post $postBefore, WP_Post $postAfter): void; } \ No newline at end of file