Skip to content

Commit eeb4c45

Browse files
committed
Add version 2.0.1 updates: Introduce filter to hide Test Jobs page, add filters documentation
- Added filter `redis_queue_show_test_jobs_page` to allow hiding the "Test Jobs" submenu/page in production environments. - Created `docs/filters.md` to document all public plugin filters with usage examples. - Implemented defensive checks to prevent access to the Test Jobs page when hidden by the filter. - Updated version numbers in `readme.txt` and `redis-queue.php` to reflect the new release.
1 parent f4fa5da commit eeb4c45

File tree

6 files changed

+240
-7
lines changed

6 files changed

+240
-7
lines changed

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,19 @@ All notable changes to this project will be documented in this file.
44

55
The format is based on Keep a Changelog and adheres to Semantic Versioning.
66

7+
## [2.0.1] - 2025-10-14
8+
### Added
9+
- Filter `redis_queue_show_test_jobs_page` lets administrators (or site integrators) hide the "Test Jobs" admin submenu/page in production or restricted environments.
10+
- Documentation: New `docs/filters.md` reference listing all public plugin filters with signatures & examples.
11+
12+
### Changed
13+
- Minor defensive check added to prevent direct access to the Test Jobs page when disabled by filter.
14+
15+
### Notes
16+
- This is a non-breaking incremental release over 2.0.0 focused on admin customization.
17+
- Added dedicated Filters Reference doc for easier extensibility.
18+
19+
720

821
## [2.0.0] - 2025-10-13
922
### Breaking Changes

docs/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ Potential future doc: `security-hardening.md` (advanced token rotation, audit ag
128128
| Overview & Root README | Feature overview, installation basics | [Root README](../README.md) |
129129
| Usage & Operations | Admin UI, job lifecycle, troubleshooting | [Usage](usage.md) |
130130
| Extending Jobs | Build custom job classes & best practices | [Extending Jobs](extending-jobs.md) |
131+
| Filters Reference | All available plugin filters (hooks) | [Filters](filters.md) |
131132
| REST API Reference | Endpoints, auth (scopes, rate limit), logging | [REST API](worker-rest-api.md) |
132133
| Scaling Strategies | Horizontal + segmentation + backpressure | [Scaling](scaling.md) |
133134
| Maintenance & Operations | Purging, stuck jobs, logs, DR | [Maintenance](maintenance.md) |

docs/filters.md

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
# Filters Reference
2+
3+
This document lists all public WordPress filters exposed by the Redis Queue plugin (excluding third‑party library filters). Use these hooks to customize behaviour without modifying core plugin code.
4+
5+
> Prefix: All filters start with `redis_queue_`.
6+
7+
## Table of Contents
8+
- [UI / Admin](#ui--admin)
9+
- [Job Creation & Mapping](#job-creation--mapping)
10+
- [Job Retry & Backoff](#job-retry--backoff)
11+
- [Job Payload Validation](#job-payload-validation)
12+
- [REST API Token Scopes](#rest-api-token-scopes)
13+
14+
---
15+
## UI / Admin
16+
### `redis_queue_show_test_jobs_page`
17+
Controls whether the "Test Jobs" submenu appears in the admin and whether the page can be accessed directly.
18+
19+
| Type | Default | Since |
20+
|------|---------|-------|
21+
| bool | `true` | 2.0.1 |
22+
23+
Return `false` to hide the menu and block direct access (useful for production hardening).
24+
25+
```php
26+
// In mu-plugin or theme functions.php
27+
add_filter( 'redis_queue_show_test_jobs_page', '__return_false' );
28+
```
29+
30+
---
31+
## Job Creation & Mapping
32+
### `redis_queue_create_job`
33+
Allows creation of custom job instances for dynamic job types that are not part of the built‑in set (`email`, `image_processing`, `api_sync`).
34+
35+
Signature:
36+
```php
37+
apply_filters( 'redis_queue_create_job', $job_or_null, string $job_type, array $payload );
38+
```
39+
- `$job_or_null` (mixed): Always `null` on input; return a `Queue_Job` instance to handle the type.
40+
- `$job_type` (string): Requested job type.
41+
- `$payload` (array): Raw payload passed by caller.
42+
43+
Return: `\Soderlind\RedisQueue\Contracts\Queue_Job|null`.
44+
45+
Example:
46+
```php
47+
add_filter( 'redis_queue_create_job', function( $job, $type, $payload ) {
48+
if ( 'report_generation' !== $type ) {
49+
return $job; // leave untouched
50+
}
51+
return new \MyPlugin\Jobs\Report_Generation_Job( $payload );
52+
}, 10, 3 );
53+
```
54+
55+
### `redis_queue_job_classes`
56+
Extends or overrides the canonical mapping from simple job type identifiers (lowercase) to fully qualified job class names used during deserialization / processing by the `Job_Processor`.
57+
58+
Signature:
59+
```php
60+
apply_filters( 'redis_queue_job_classes', array $map );
61+
```
62+
- `$map` (array): Base mapping like `['email' => Email_Job::class, ...]`.
63+
64+
Return: Modified associative array.
65+
66+
Example (add `report_generation`):
67+
```php
68+
add_filter( 'redis_queue_job_classes', function( $map ) {
69+
$map['report_generation'] = \MyPlugin\Jobs\Report_Generation_Job::class;
70+
return $map;
71+
});
72+
```
73+
74+
---
75+
## Job Retry & Backoff
76+
### `redis_queue_should_retry_job`
77+
Determines if a failed job should be retried (only consulted if current attempts < max_attempts).
78+
79+
Signature:
80+
```php
81+
apply_filters( 'redis_queue_should_retry_job', bool $should_retry, Queue_Job $job, ?\Exception $exception, int $attempt );
82+
```
83+
- `$should_retry` (bool): Default `true` after basic guards.
84+
- `$job` (Queue_Job): Job instance.
85+
- `$exception` (?Exception): The exception that caused failure or `null` when failure was non-exception based.
86+
- `$attempt` (int): Current attempt number (1-based) that just failed.
87+
88+
Return: `bool` (retry or not).
89+
90+
Example (disable retries on specific exception):
91+
```php
92+
add_filter( 'redis_queue_should_retry_job', function( $retry, $job, $exception, $attempt ) {
93+
if ( $exception instanceof \MyPlugin\FatalRemoteAPIException ) {
94+
return false; // don't bother retrying
95+
}
96+
return $retry;
97+
}, 10, 4 );
98+
```
99+
100+
### `redis_queue_job_retry_delay`
101+
Adjusts delay (seconds) before a retry attempt is re-enqueued.
102+
103+
Signature:
104+
```php
105+
apply_filters( 'redis_queue_job_retry_delay', int $delay, Queue_Job $job, int $attempt );
106+
```
107+
- `$delay` (int): Computed delay (configured backoff item or exponential fallback capped at 3600s).
108+
- `$job` (Queue_Job): Job instance.
109+
- `$attempt` (int): Attempt number that just failed.
110+
111+
Return: New delay (int).
112+
113+
Example (progressive jitter):
114+
```php
115+
add_filter( 'redis_queue_job_retry_delay', function( $delay, $job, $attempt ) {
116+
return (int) ( $delay * ( 0.9 + mt_rand() / mt_getrandmax() * 0.2 ) );
117+
}, 10, 3 );
118+
```
119+
120+
---
121+
## Job Payload Validation
122+
### `redis_queue_validate_job_payload`
123+
Override or augment validation of a job payload.
124+
125+
Signature:
126+
```php
127+
apply_filters( 'redis_queue_validate_job_payload', bool $is_valid, array $payload, Queue_Job $job );
128+
```
129+
- `$is_valid` (bool): Default `true`.
130+
- `$payload` (array): Job payload.
131+
- `$job` (Queue_Job): Job instance.
132+
133+
Return: `bool` (allow enqueue / continue processing).
134+
135+
Example (require key):
136+
```php
137+
add_filter( 'redis_queue_validate_job_payload', function( $valid, $payload, $job ) {
138+
if ( 'report_generation' === $job->get_job_type() && empty( $payload['report_id'] ) ) {
139+
return false;
140+
}
141+
return $valid;
142+
}, 10, 3 );
143+
```
144+
145+
---
146+
## REST API Token Scopes
147+
These filters control what endpoints a token with a restricted scope may access.
148+
149+
### `redis_queue_token_allowed_routes`
150+
Defines the list of allowable REST routes for a non-`full` scope token before per-request evaluation.
151+
152+
Signature:
153+
```php
154+
apply_filters( 'redis_queue_token_allowed_routes', array $routes, string $scope );
155+
```
156+
- `$routes` (array): Default `[ '/redis-queue/v1/workers/trigger' ]` when scope != `full`.
157+
- `$scope` (string): Token scope, e.g. `worker` or `full`.
158+
159+
Return: Adjusted list of route paths.
160+
161+
Example (allow stats endpoint):
162+
```php
163+
add_filter( 'redis_queue_token_allowed_routes', function( $routes, $scope ) {
164+
if ( 'worker' === $scope ) {
165+
$routes[] = '/redis-queue/v1/stats';
166+
}
167+
return $routes;
168+
}, 10, 2 );
169+
```
170+
171+
### `redis_queue_token_scope_allow`
172+
Final gate to allow/deny a request for a token (after URL match). Use to implement dynamic rules (time windows, IP allowlists, etc.).
173+
174+
Signature:
175+
```php
176+
apply_filters( 'redis_queue_token_scope_allow', bool $allowed, string $scope, WP_REST_Request $request );
177+
```
178+
- `$allowed` (bool): Result after earlier checks.
179+
- `$scope` (string): Token scope.
180+
- `$request` (WP_REST_Request): Full request object.
181+
182+
Return: `bool` (permit request?).
183+
184+
Example (block trigger outside office hours):
185+
```php
186+
add_filter( 'redis_queue_token_scope_allow', function( $allowed, $scope, $request ) {
187+
if ( 'worker' === $scope ) {
188+
$hour = (int) gmdate('G');
189+
if ( $hour < 6 || $hour > 22 ) {
190+
return false; // Outside allowed window
191+
}
192+
}
193+
return $allowed;
194+
}, 10, 3 );
195+
```
196+
197+
---
198+
## Notes
199+
- All filters follow standard WordPress priority & argument count conventions.
200+
- Always return the original value when not modifying behaviour to maintain chain integrity.
201+
- Avoid expensive operations inside hot-path filters (`job_retry_delay`, `should_retry_job`).
202+
203+
Happy extending! 🚀

readme.txt

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ Tags: redis, queue, background, jobs, performance
55
Requires at least: 6.7
66
Tested up to: 6.8
77
Requires PHP: 8.3
8-
Stable tag: 2.0.0
8+
Stable tag: 2.0.1
99
License: GPL v2 or later
1010
License URI: https://www.gnu.org/licenses/gpl-2.0.html
1111

@@ -102,6 +102,12 @@ The plugin includes fallback mechanisms and graceful error handling. Jobs will f
102102

103103
== Changelog ==
104104

105+
= 2.0.1 =
106+
* Added: Filter `redis_queue_show_test_jobs_page` to optionally hide the Test Jobs admin page (useful on production).
107+
* Added: New documentation file `docs/filters.md` enumerating all available filters.
108+
* Changed: Defensive access guard when page is disabled.
109+
* Note: Non-breaking refinement over 2.0.0.
110+
105111
= 2.0.0 =
106112
* **BREAKING CHANGES - Major version update**
107113
* Plugin renamed from "Redis Queue Demo" to "Redis Queue"
@@ -158,6 +164,9 @@ The plugin includes fallback mechanisms and graceful error handling. Jobs will f
158164

159165
== Upgrade Notice ==
160166

167+
= 2.0.1 =
168+
Adds filter to disable Test Jobs page in production and introduces a filters reference doc (`docs/filters.md`).
169+
161170
= 2.0.0 =
162171
MAJOR BREAKING CHANGES: Plugin renamed to "Redis Queue". Namespace, function names, and hooks all changed. Review migration guide in CHANGELOG.md before upgrading. No automatic backward compatibility.
163172

redis-queue.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* Plugin Name: Redis Queue
44
* Plugin URI: https://github.com/soderlind/redis-queue
55
* Description: Redis-backed prioritized, delayed & retryable background jobs for WordPress (workers, REST API, admin UI).
6-
* Version: 2.0.0
6+
* Version: 2.0.1
77
* Requires at least: 6.7
88
* Requires PHP: 8.3
99
* Author: Per Soderlind
@@ -20,7 +20,7 @@
2020
}
2121

2222
// Plugin version and requirements.
23-
define( 'REDIS_QUEUE_VERSION', '2.0.0' );
23+
define( 'REDIS_QUEUE_VERSION', '2.0.1' );
2424
define( 'REDIS_QUEUE_MIN_PHP', '8.3' );
2525

2626
// Plugin file paths and URLs.
@@ -59,7 +59,7 @@ function redis_queue_migrate_options_v2() {
5959
$old_key = 'redis_queue_demo_' . $suffix;
6060
$new_key = 'redis_queue_' . $suffix;
6161
$old_val = get_option( $old_key, null );
62-
62+
6363
// Only migrate if old value exists and new value doesn't exist.
6464
if ( $old_val !== null && get_option( $new_key, null ) === null ) {
6565
update_option( $new_key, $old_val );
@@ -83,7 +83,7 @@ function redis_queue_migrate_options_v2() {
8383
Soderlind\RedisQueue\Core\Redis_Queue::get_instance();
8484
} else {
8585
// Show error notice if dependencies are missing.
86-
add_action( 'admin_notices', function() {
86+
add_action( 'admin_notices', function () {
8787
if ( current_user_can( 'activate_plugins' ) ) {
8888
echo '<div class="notice notice-error"><p>';
8989
echo '<strong>Redis Queue:</strong> ';
@@ -142,7 +142,7 @@ function redis_queue_process_jobs( $queue = 'default', $max_jobs = null ) {
142142
if ( ! $instance || ! $instance->get_queue_manager() || ! $instance->get_job_processor() ) {
143143
return false;
144144
}
145-
145+
146146
// Create a synchronous worker and process jobs.
147147
$worker = new \Soderlind\RedisQueue\Workers\Sync_Worker( $instance->get_queue_manager(), $instance->get_job_processor() );
148148
return $worker->process_jobs( (array) $queue, $max_jobs );

src/Admin/Admin_Interface.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,10 @@ public function add_admin_menu() {
3939
\add_menu_page( __( 'Redis Queue', 'redis-queue' ), __( 'Redis Queue', 'redis-queue' ), 'manage_options', 'redis-queue', [ $this, 'render_dashboard_page' ], 'dashicons-database-view', 30 );
4040
\add_submenu_page( 'redis-queue', __( 'Dashboard', 'redis-queue' ), __( 'Dashboard', 'redis-queue' ), 'manage_options', 'redis-queue', [ $this, 'render_dashboard_page' ] );
4141
\add_submenu_page( 'redis-queue', __( 'Jobs', 'redis-queue' ), __( 'Jobs', 'redis-queue' ), 'manage_options', 'redis-queue-jobs', [ $this, 'render_jobs_page' ] );
42-
\add_submenu_page( 'redis-queue', __( 'Test Jobs', 'redis-queue' ), __( 'Test Jobs', 'redis-queue' ), 'manage_options', 'redis-queue-test', [ $this, 'render_test_page' ] );
42+
// Allow integrators to hide the Test Jobs page (e.g., production hardening) via filter.
43+
if ( \apply_filters( 'redis_queue_show_test_jobs_page', true ) ) {
44+
\add_submenu_page( 'redis-queue', __( 'Test Jobs', 'redis-queue' ), __( 'Test Jobs', 'redis-queue' ), 'manage_options', 'redis-queue-test', [ $this, 'render_test_page' ] );
45+
}
4346
\add_submenu_page( 'redis-queue', __( 'Settings', 'redis-queue' ), __( 'Settings', 'redis-queue' ), 'manage_options', 'redis-queue-settings', [ $this, 'render_settings_page' ] );
4447
}
4548

@@ -104,6 +107,10 @@ public function render_jobs_page() {
104107
include __DIR__ . '/partials/jobs-inline.php';
105108
}
106109
public function render_test_page() {
110+
// Defensive: if hidden after initial menu build, block access.
111+
if ( ! \apply_filters( 'redis_queue_show_test_jobs_page', true ) ) {
112+
\wp_die( esc_html__( 'The Test Jobs page has been disabled by configuration.', 'redis-queue' ) );
113+
}
107114
include __DIR__ . '/partials/test-inline.php';
108115
}
109116
public function render_settings_page() {

0 commit comments

Comments
 (0)