Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
22 changes: 5 additions & 17 deletions docs/audit-log.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,20 +49,8 @@ Once registered, your custom Stream Connector's records will be part of the Audi

## Action Scheduler

The Audit Log plugin (Stream) bundles [Action Scheduler](https://actionscheduler.org/) as a dependency. By default, Altis disables the Action Scheduler queue runner cron event (`action_scheduler_run_queue`) since it is not needed for core audit logging functionality.

If another plugin in your project requires Action Scheduler's queue processing, you can re-enable it in your Altis configuration:

```json
{
"extra": {
"altis": {
"modules": {
"security": {
"disable-action-scheduler-cron": false
}
}
}
}
}
```
The Audit Log plugin (Stream) can use [Action Scheduler](https://actionscheduler.org/) for deferred work, but Altis does not load it. Audit Log records are kept indefinitely, so Altis turns off Stream's automatic purge, and Stream uses WP-Cron for any deferred work instead.

Other plugins can continue to use Action Scheduler if they need it, such as WooCommerce, which loads its own copy. Altis does not interfere with them.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: this changes behaviour for sites running WooCommerce or another plugin that bundles Action Scheduler. Their queue runner and the Tools → Scheduled Actions menu now work, where we used to suppress them. That’s the right outcome, it would be good if we could add it to the release notes or changelog, so it doesn’t surprise anyone?

@mikelittle mikelittle Oct 9, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's what the disable-action-scheduler-cron: false was for. If our previous default behaviour broke things for another site's plugins, they will have needed to set that setting to stop us breaking things. Now we don't turn it off. So they no longer need the setting.


Earlier versions allowed this behaviour to be changed with the `extra.altis.modules.security.disable-action-scheduler-cron` setting. That setting no longer has any effect, and you can safely remove it from your `composer.json`.
44 changes: 5 additions & 39 deletions inc/stream/namespace.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,41 +39,14 @@ function bootstrap() {
add_filter( 'site_option_wp_stream_network', __NAMESPACE__ . '\\default_stream_network_options' );
add_filter( 'default_site_option_wp_stream_network', __NAMESPACE__ . '\\default_stream_network_options' );
add_action( 'network_admin_menu', __NAMESPACE__ . '\\remove_stream_admin_pages', 11 );
add_action( 'admin_menu', __NAMESPACE__ . '\\remove_action_scheduler_menu', 11 );
add_action( 'admin_bar_menu', __NAMESPACE__ . '\\override_network_admin_bar_menu', 100 );
add_filter( 'wp_stream_record_array', __NAMESPACE__ . '\\filter_wp_stream_record_array', 10, 1 );

// Initialise Action Scheduler so Stream can use its API functions (e.g. as_enqueue_async_action).
// Requiring action-scheduler.php adds hooks on plugins_loaded at priority 0,
// but we're already past that, so we must manually trigger registration and initialization.
// The register function name includes the version number (e.g. action_scheduler_register_3_dot_9_dot_3)
// and changes with each release, so we find it dynamically to avoid breakage on updates.
require_once Altis\ROOT_DIR . '/vendor/xwp/stream/vendor/woocommerce/action-scheduler/action-scheduler.php';
$as_versions = \ActionScheduler_Versions::instance();
if ( ! $as_versions->latest_version() ) {
foreach ( get_defined_functions()['user'] as $func ) {
if ( strpos( $func, 'action_scheduler_register_' ) === 0 ) {
call_user_func( $func );
break;
}
}
}
\ActionScheduler_Versions::initialize_latest_version();

// Disable the Action Scheduler queue runner cron event unless another plugin needs it.
// AS is only needed here for Stream's API functions, not for processing queued actions.
$config = Altis\get_config()['modules']['security'];
if ( $config['disable-action-scheduler-cron'] ?? true ) {
add_action( 'init', function () {
if ( class_exists( 'ActionScheduler', false ) ) {
remove_action( 'init', [ \ActionScheduler::runner(), 'init' ], 1 );
$timestamp = wp_next_scheduled( 'action_scheduler_run_queue', [ 'WP Cron' ] );
if ( $timestamp ) {
wp_unschedule_event( $timestamp, 'action_scheduler_run_queue', [ 'WP Cron' ] );
}
}
}, 0 );
}
// Records are kept indefinitely in a write-only log, so Stream's purge job has nothing to
// delete. Stop it being scheduled and use WP-Cron (Cavalcade) rather than Action Scheduler.
// Stream reads these filters when its plugin file loads, so they must be added first.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: this is only true of wp_stream_use_action_scheduler. wp_stream_enable_auto_purge is read later, on wp_loaded (class-admin.php#L273) and when the purge runs. Maybe:

	// `wp_stream_use_action_scheduler` is read when Stream's plugin file loads, so these must be added before the require.

add_filter( 'wp_stream_enable_auto_purge', '__return_false' );
add_filter( 'wp_stream_use_action_scheduler', '__return_false' );

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sites that set disable-action-scheduler-cron: false will have Action Scheduler’s action_scheduler_run_queue event scheduled every minute. Action Scheduler is no longer loaded, so nothing will ever remove that event, and Cavalcade will keep running a job with no handler every minute.

Could we clear it once, only when no other plugin provides Action Scheduler? The class_exists check means a site running WooCommerce keeps its own queue runner.

add_action( 'init', function () {
	if ( class_exists( 'ActionScheduler', false ) || get_option( 'altis_stream_as_cleanup' ) ) {
		return;
	}
	wp_unschedule_hook( 'action_scheduler_run_queue' );
	update_option( 'altis_stream_as_cleanup', 1 );
} );

If we’d rather not ship cleanup code, an upgrade note with wp cron event delete action_scheduler_run_queue would also work.

Related, and lower impact: the description says Stream clears purge jobs that earlier versions scheduled. That’s true for WP-Cron. For jobs stored in Action Scheduler’s tables, Stream only clears them when Action Scheduler’s functions are loaded (class-admin.php#L1104). Since we no longer load it, those rows stay in the actionscheduler_* tables. That’s harmless, because nothing runs them and the purge is blocked when it runs anyway. It might just be worth softening that line in the description.

@mikelittle mikelittle Oct 9, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason someone might set disable-action-scheduler-cron: false is that they do want Actions Scheduler to run for another plugin. For example, WooCommerce. We only care to turn it off for Stream. If another plugin needs it, it will install its own copy and use that. That setting was to stop Altis from breaking that other plugin. Now we no longer interfere with other plugins' use of AS.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed the comment above to make it clearer we are disabling the loading of Action scheduler only. The fact we disable auto purge means WP Cron won't get used either, but we don't need to say that.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair point. Any site that set that flag has its own Action Scheduler handling the event, so it won’t be orphaned. Happy to drop this.


require_once Altis\ROOT_DIR . '/vendor/xwp/stream/stream.php';
}
Expand Down Expand Up @@ -117,13 +90,6 @@ function remove_stream_admin_pages() {
remove_submenu_page( $wp_stream->admin->records_page_slug, $wp_stream->admin->network->network_settings_page_slug );
}

/**
* Remove the Action Scheduler admin page.
*/
function remove_action_scheduler_menu() {
remove_submenu_page( 'tools.php', 'action-scheduler' );
}

/**
* Override the Stream admin bar menu.
*
Expand Down
1 change: 0 additions & 1 deletion load.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
'require-login' => ! in_array( Altis\get_environment_type(), [ 'production', 'local' ], true ),
'php-basic-auth' => false,
'audit-log' => true,
'disable-action-scheduler-cron' => true,
'disable-accounts' => true,
'2-factor-authentication' => Altis\get_environment_type() === 'local' ? true : [
'required' => [
Expand Down
Loading