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
6 changes: 3 additions & 3 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ jobs:

strategy:
matrix:
php: [8.3, 8.2]
laravel: [11.*]
statamic: [^5.0]
php: [8.4, 8.3]
laravel: [12.*]
statamic: [^6.0]
os: [ubuntu-latest]

name: ${{ matrix.php }} - ${{ matrix.statamic }} - ${{ matrix.laravel }}
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,4 @@ To invalidate all pages containing your tracked data call:
Tracker::flush();
```

Alternatively you can use the Cache Tracker Utility in the CP to enter a list of URLs you want to clear.
13 changes: 7 additions & 6 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,17 @@
}
},
"require": {
"php": "^8.1",
"php": "^8.2",
"pixelfear/composer-dist-plugin": "^0.1.5",
"statamic/cms": "^4.55 || ^5.0"
"statamic/cms": "^6.0"
},
"require-dev": {
"laravel/pint": "^1.13",
"mockery/mockery": "^1.3.1",
"nunomaduro/collision": "^8.0",
"orchestra/testbench": "^9.0",
"pestphp/pest": "^2.24",
"phpunit/phpunit": "^10.0"
"orchestra/testbench": "^10.0",
"pestphp/pest": "^4.0",
"phpunit/phpunit": "^12.0"
},
"extra": {
"statamic": {
Expand All @@ -39,5 +39,6 @@
"pestphp/pest-plugin": true,
"pixelfear/composer-dist-plugin": true
}
}
},
"minimum-stability": "alpha"
}
15 changes: 15 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"dependencies": {
"@statamic/cms": "file:./vendor/statamic/cms/resources/dist-package",
"axios": "^1.12.2"
},
"scripts": {
"dev": "vite",
"build": "vite build",
"production": "vite build"
},
"devDependencies": {
"laravel-vite-plugin": "^1.2.0",
"vite": "^6.3.4"
}
}
92 changes: 92 additions & 0 deletions resources/js/components/CacheTrackerModal.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
<script setup>
import axios from 'axios';
import { ref } from 'vue';
const props = defineProps({
action: { type: Object, required: true },
});
const getColor = (tag) => {
if (tag.indexOf('collection:') === 0) {
return 'yellow';
}
if (tag.indexOf('term:') === 0) {
return 'rose';
}
if (tag.indexOf('global:') === 0) {
return 'violet';
}
if (tag.indexOf('partial:') === 0) {
return 'blue';
}
return 'green';
}
const setShowWhat = (what) => {
show.value = what;
}
const item = props.action?.item_title ?? '';
const url = props.action?.item_url ?? '';
const show = ref('tags');
const tags = ref([]);
const urls = ref([]);
axios
.post(cp_url(`/cache-tracker/tags`), { url: url })
.then(response => {
tags.value = response.data;
})
.catch((e) => { });
axios
.post(cp_url(`/cache-tracker/urls`), { url: url })
.then(response => {
urls.value = response.data;
})
.catch((e) => { });
</script>
<template>
<div>
<ui-button-group class="mb-4">
<ui-button :variant="show == 'tags' ? 'primary' : 'default'" size="sm" @click="setShowWhat('tags')">Tags on this URL</ui-button>
<ui-button :variant="show == 'urls' ? 'primary' : 'default'" size="sm" @click="setShowWhat('urls')">URLs with this item</ui-button>
</ui-button-group>
<div v-if="show == 'tags'">
<ui-description v-text="__('There are no tags tracked on :url.', { url: url })" v-if="tags.length < 1"></ui-description>
<ui-description v-text="__('The following tags are being tracked on :url:', { url: url })" v-if="tags.length"></ui-description>
<div class="flex gap-2 mt-4" v-if="tags.length">
<template v-for="tag in tags">
<ui-badge pill :color="getColor(tag)" v-text="tag"></ui-badge>
</template>
</div>
</div>
<div v-if="show == 'urls'">
<ui-description v-text="__('There are no urls tracked containing :item.', { item: item })" v-if="urls.length < 1"></ui-description>
<ui-description v-text="__('The following URLs contain :item:', { item: 'ryan' })" v-if="urls.length"></ui-description>
<div class="flex gap-2 mt-4" v-if="urls.length">
<template v-for="url in urls">
<a :href="url" v-text="url"></a><br />
</template>
</div>
</div>
</div>
</template>
8 changes: 8 additions & 0 deletions resources/js/cp.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import CacheTrackerModal from './components/CacheTrackerModal.vue';
import ClearUtility from "./pages/ClearUtility.vue";

Statamic.booting(() => {
Statamic.$components.register('cache-tracker-modal', CacheTrackerModal);

Statamic.$components.register('Pages/CacheTracker/ClearUtility', ClearUtility);
});
38 changes: 38 additions & 0 deletions resources/js/pages/ClearUtility.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<script setup>
import axios from 'axios';
import { ref } from 'vue';

const urls = ref('');

const clearCache = async () => {
let response = await axios.post(cp_url('utilities/static-cache-tracker/clear'), { urls: urls.value });

Statamic.$toast.success(response.data.message);
}

</script>

<template>
<ui-header :title="__('Cache Tracker')" icon="taxonomies">
<ui-button class="btn-primary" v-text="__('Clear everything')" @click="urls = '*'; clearCache();" />
</ui-header>

<ui-panel class="h-full flex flex-col">
<ui-panel-header class="flex items-center justify-between min-h-10">
<ui-heading>{{ __('Enter URLs to clear, with each on a new line. You can use * as a wildcard at the end of your URL.') }}</ui-heading>
</ui-panel-header>

<ui-card class="flex-1">

<ui-field>
<ui-textarea label="Enter URLs" v-model="urls" />
</ui-field>

<ui-field class="mt-4 flex justify-end">
<ui-button variant="primary" v-text="__('Clear')" @click="clearCache" />
</ui-field>

</ui-card>
</ui-panel>

</template>
9 changes: 9 additions & 0 deletions routes/cp.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

use Illuminate\Support\Facades\Route;
use Thoughtco\StatamicCacheTracker\Http\Controllers;

Route::name('cache-tracker.')->prefix('cache-tracker')->group(function () {
Route::post('tags', [Controllers\GetTagsController::class, '__invoke'])->name('tags');
Route::post('urls', [Controllers\GetUrlsController::class, '__invoke'])->name('url');
});
51 changes: 51 additions & 0 deletions src/Actions/ClearCache.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php

namespace Thoughtco\StatamicCacheTracker\Actions;

use Statamic\Actions\Action;
use Statamic\Contracts\Entries\Entry;
use Statamic\Contracts\Taxonomies\Term;
use Statamic\Facades\Blink;
use Thoughtco\StatamicCacheTracker\Facades\Tracker;

class ClearCache extends Action
{
public function run($items, $values)
{
$items->filter(fn ($item) => $item->absoluteUrl())
->each(fn ($item) => Tracker::remove($item->absoluteUrl()));

return __('Cache cleared');
}

public function icon(): string
{
return 'rewind';
}

public static function title()
{
return __('Clear cache');
}

public function confirmationText()
{
return __('Are you sure you want to clear the static cache for the url: :url ?', ['url' => $this->items->first()->absoluteUrl()]);
}

public function visibleTo($item)
{
if (! auth()->user()->can('clear cache tracker tags')) {
return false;
}

if (! ($item instanceof Entry || $item instanceof Term)) {
return false;
}

return ! Blink::once(
'cache-action::'.$item->collectionHandle.'::'.$item->locale(),
fn () => is_null($item->collection()->route($item->locale()))
);
}
}
65 changes: 65 additions & 0 deletions src/Actions/ViewCacheTags.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php

namespace Thoughtco\StatamicCacheTracker\Actions;

use Statamic\Actions\Action;
use Statamic\Contracts\Entries\Entry;
use Statamic\Facades\Blink;

class ViewCacheTags extends Action
{
protected $component = 'cache-tracker-modal';

protected $runnable = false;

public function run($items, $values)
{
// no running in the corridor
}

public function icon(): string
{
return 'taxonomies';
}

public static function title()
{
return __('View cache tags');
}

public function visibleTo($item)
{
if (! auth()->user()->can('view cache tracker tags')) {
return false;
}

if (! $item instanceof Entry) {
return false;
}

return ! Blink::once(
'cache-action::'.$item->collectionHandle.'::'.$item->locale(),
fn () => is_null($item->collection()->route($item->locale()))
);
}

public function visibleToBulk($items)
{
return false;
}

public function buttonText()
{
/** @translation */
return __('Clear cache');
}

public function toArray()
{
return [
...parent::toArray(),
'item_title' => $this->items->first()?->title,
'item_url' => $this->items->first()?->absoluteUrl(),
];
}
}
27 changes: 27 additions & 0 deletions src/Http/Controllers/GetTagsController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

namespace Thoughtco\StatamicCacheTracker\Http\Controllers;

use Statamic\Http\Controllers\Controller;
use Statamic\Support\Str;
use Thoughtco\StatamicCacheTracker\Facades\Tracker;

class GetTagsController extends Controller
{
public function __invoke(): array
{
if (! $url = request()->input('url')) {
return [];
}

if (Str::endsWith($url, '/')) {
$url = Str::beforeLast($url, '/');
}

if ($data = Tracker::get($url)) {
return $data['tags'];
}

return [];
}
}
35 changes: 35 additions & 0 deletions src/Http/Controllers/GetUrlsController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

namespace Thoughtco\StatamicCacheTracker\Http\Controllers;

use Statamic\Contracts\Entries\Entry;
use Statamic\Contracts\Taxonomies\Term;
use Statamic\Facades\Data;
use Statamic\Http\Controllers\Controller;
use Thoughtco\StatamicCacheTracker\Facades\Tracker;

class GetUrlsController extends Controller
{
public function __invoke(): array
{
if (! $url = request()->input('url')) {
return [];
}

if (! $item = Data::find($url)) {
return [];
}

if ($item instanceof Entry) {
$item = $item->collectionHandle().':'.$item->id();
}

if ($item instanceof Term) {
$item = 'term:'.$item->id();
}

return collect(Tracker::all())
->filter(fn ($tracked) => in_array($item, $tracked['tags']))
->all();
}
}
Loading