Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion src/Base/RestAPI/ItemFields/FeaturedImageField.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@

class FeaturedImageField extends CreatesFields
{
private const CACHE_KEY = 'openpub_featured_image_%d';

/**
* Attachment meta that ends up in the cached field.
*/
private const CACHED_META_KEYS = ['_wp_attachment_metadata', '_wp_attached_file', '_wp_attachment_image_alt'];

/**
* Gets the featured image of a post.
*
Expand All @@ -21,7 +28,7 @@ public function create(WP_Post $post): array
}

$id = \get_post_thumbnail_id($post->ID);
$cacheKey = sprintf('openpub_featured_image_%d', $id);
$cacheKey = sprintf(self::CACHE_KEY, $id);
$cached = get_transient($cacheKey);

if (is_array($cached)) {
Expand Down Expand Up @@ -50,6 +57,21 @@ public function create(WP_Post $post): array
return $result;
}

public function clearCache(int $attachmentId): void
{
delete_transient(sprintf(self::CACHE_KEY, $attachmentId));
}

/**
* Image editor saves (crop, rotate, scale, restore) only touch attachment meta.
*/
public function clearCacheOnMetaChange(int $metaId, int $objectId, string $metaKey): void
{
if (in_array($metaKey, self::CACHED_META_KEYS, true)) {
$this->clearCache($objectId);
}
}

/**
* Get meta data of an attachment.
*/
Expand Down
8 changes: 8 additions & 0 deletions src/Base/RestAPI/RestAPIServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use OWC\OpenPub\Base\RestAPI\Controllers\SearchController;
use OWC\OpenPub\Base\RestAPI\Controllers\SettingsController;
use OWC\OpenPub\Base\RestAPI\Controllers\ThemeController;
use OWC\OpenPub\Base\RestAPI\ItemFields\FeaturedImageField;
use WP_REST_Server;

class RestAPIServiceProvider extends ServiceProvider
Expand All @@ -22,6 +23,13 @@ public function register(): void
$this->plugin->loader->addFilter('rest_api_init', $this, 'registerRoutes');
$this->plugin->loader->addFilter('owc/config-expander/rest-api/whitelist', $this, 'whitelist', 10, 1);

// Before cache purges on the default priority, so a portal refetch never rebuilds from the stale image.
$featuredImageField = new FeaturedImageField($this->plugin);
$this->plugin->loader->addAction('updated_post_meta', $featuredImageField, 'clearCacheOnMetaChange', 5, 3);
$this->plugin->loader->addAction('added_post_meta', $featuredImageField, 'clearCacheOnMetaChange', 5, 3);
$this->plugin->loader->addAction('edit_attachment', $featuredImageField, 'clearCache', 5, 1);
$this->plugin->loader->addAction('delete_attachment', $featuredImageField, 'clearCache', 5, 1);

$this->registerModelFields();
}

Expand Down
41 changes: 41 additions & 0 deletions tests/Unit/Base/RestAPI/ItemFields/FeaturedImageFieldTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -224,4 +224,45 @@ public function it_returns_a_correct_featured_image_response_with_multiple_size(
];
$this->assertEquals($expected, $actual);
}

/** @test */
public function it_clears_the_cached_image_of_an_attachment()
{
WP_Mock::userFunction('delete_transient', [
'args' => ['openpub_featured_image_2'],
'times' => 1,
]);

(new FeaturedImageField($this->plugin))->clearCache(2);

$this->assertTrue(true);
}

/** @test */
public function it_clears_the_cached_image_when_the_attachment_is_edited()
{
WP_Mock::userFunction('delete_transient', [
'args' => ['openpub_featured_image_2'],
'times' => 3,
]);

$featuredImageField = new FeaturedImageField($this->plugin);
$featuredImageField->clearCacheOnMetaChange(10, 2, '_wp_attachment_metadata');
$featuredImageField->clearCacheOnMetaChange(10, 2, '_wp_attached_file');
$featuredImageField->clearCacheOnMetaChange(10, 2, '_wp_attachment_image_alt');

$this->assertTrue(true);
}

/** @test */
public function it_keeps_the_cached_image_on_unrelated_meta_changes()
{
WP_Mock::userFunction('delete_transient', [
'times' => 0,
]);

(new FeaturedImageField($this->plugin))->clearCacheOnMetaChange(10, 2, '_edit_lock');

$this->assertTrue(true);
}
}
21 changes: 21 additions & 0 deletions tests/Unit/Base/RestAPI/RestAPIServiceProviderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use OWC\OpenPub\Base\Foundation\Config;
use OWC\OpenPub\Base\Foundation\Loader;
use OWC\OpenPub\Base\Foundation\Plugin;
use OWC\OpenPub\Base\RestAPI\ItemFields\FeaturedImageField;
use OWC\OpenPub\Base\RestAPI\RestAPIServiceProvider;
use OWC\OpenPub\Tests\TestCase;
use WP_Mock;
Expand Down Expand Up @@ -69,6 +70,26 @@ public function check_registration_of_rest_endpoints()
'registerRoutes'
])->once();

foreach (['updated_post_meta', 'added_post_meta'] as $hook) {
$plugin->loader->shouldReceive('addAction')->withArgs([
$hook,
m::type(FeaturedImageField::class),
'clearCacheOnMetaChange',
5,
3
])->once();
}

foreach (['edit_attachment', 'delete_attachment'] as $hook) {
$plugin->loader->shouldReceive('addAction')->withArgs([
$hook,
m::type(FeaturedImageField::class),
'clearCache',
5,
1
])->once();
}

$configRestAPIFields = [
'posttype1' => [
'endpoint_field1' =>
Expand Down
Loading