PluginProbe
Stream – Activity Log & Audit Trail / 4.0.2
Stream – Activity Log & Audit Trail v4.0.2
4.4.0 4.3.0 4.2.2 4.2.1 trunk 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 3.0.0 3.0.1 3.0.2 3.0.3 3.0.4 3.0.5 3.0.6 3.0.7 3.1 3.1.1 3.10.0 3.2.0 3.2.1 3.2.2 3.2.3 All 50 releases
← All changes | classes/class-settings.php +18 -265 trunk4.0.2 View file →
@@ -111,9 +111,9 @@
111 111
112 112 $search = '';
113 113 $input = wp_stream_filter_input( INPUT_POST, 'find' );
114 114
115 - if ( isset( $input['term'] ) ) {
115 + if ( ! isset( $input['term'] ) ) {
116 116 $search = wp_unslash( trim( $input['term'] ) );
117 117 }
118 118
119 119 $request = (object) array(
@@ -354,10 +354,24 @@
354 354 'desc' => esc_html__( 'WordPress will automatically prevent duplicate comments from flooding the database. By default, Stream does not track these attempts unless you opt-in here. Enabling this is not necessary or recommended for most sites.', 'stream' ),
355 355 'after_field' => esc_html__( 'Enabled', 'stream' ),
356 356 'default' => 0,
357 357 ),
358 - $this->build_delete_all_records_field(),
359 - $this->build_clean_orphan_meta_field(),
358 + array(
359 + 'name' => 'delete_all_records',
360 + 'title' => esc_html__( 'Reset Stream Database', 'stream' ),
361 + 'type' => 'link',
362 + 'href' => add_query_arg(
363 + array(
364 + 'action' => 'wp_stream_reset',
365 + 'wp_stream_nonce_reset' => wp_create_nonce( 'stream_nonce_reset' ),
366 + ),
367 + admin_url( 'admin-ajax.php' )
368 + ),
369 + 'class' => 'warning',
370 + 'desc' => esc_html__( 'Warning: This will delete all activity records from the database.', 'stream' ),
371 + 'default' => 0,
372 + 'sticky' => 'bottom',
373 + ),
360 374 ),
361 375 ),
362 376 );
363 377
@@ -385,33 +399,8 @@
385 399 );
386 400
387 401 array_push( $fields['advanced']['fields'], $wp_cron_tracking );
388 402
389 - // Abilities API toggle is only meaningful on WordPress 6.9+. On
390 - // network-activated multisite, Abilities::is_enabled() reads the
391 - // network option (wp_stream_network), so a per-site checkbox on the
392 - // site's own settings screen would be a no-op and misleading. Hide
393 - // the field from per-site settings pages, but keep it available in
394 - // network admin and in REST/CLI contexts where update_all_setting_values()
395 - // routes writes to the network option correctly.
396 - $hide_per_site = $this->plugin->is_network_activated() && is_admin() && ! is_network_admin();
397 -
398 - if (
399 - class_exists( '\WP_Ability' )
400 - && ! $hide_per_site
401 - ) {
402 - $enable_abilities_api = array(
403 - 'name' => 'enable_abilities_api',
404 - 'title' => esc_html__( 'Enable Abilities API and MCP', 'stream' ),
405 - 'type' => 'checkbox',
406 - 'desc' => esc_html__( 'Expose Stream operations to AI agents via the WordPress Abilities API (and MCP when the MCP Adapter plugin is installed). Requires WordPress 6.9.', 'stream' ),
407 - 'after_field' => esc_html__( 'Enabled', 'stream' ),
408 - 'default' => 0,
409 - );
410 -
411 - array_push( $fields['advanced']['fields'], $enable_abilities_api );
412 - }
413 -
414 403 /**
415 404 * Filter allows for modification of options fields
416 405 *
417 406 * @return array Array of option fields
@@ -438,170 +427,8 @@
438 427 return $this->fields;
439 428 }
440 429
441 430 /**
442 - * Build the "Reset Stream Database" settings field definition.
443 - *
444 - * Extracted so the async-deletion running-state check
445 - * ({@see Admin::is_running_async_deletion()}) is evaluated once per render
446 - * instead of once per field property, and only in admin context.
447 - *
448 - * `Settings::__construct` populates `$this->options = $this->get_options()`
449 - * on the `init` hook for every pageload, which walks `get_fields()`. The
450 - * field is only ever rendered in admin, so outside admin the dynamic state
451 - * is irrelevant and the Action Scheduler query is skipped entirely.
452 - *
453 - * @return array
454 - */
455 - private function build_delete_all_records_field() {
456 - $is_running_deletion = is_admin() ? Admin::is_running_async_deletion() : false;
457 -
458 - return array(
459 - 'name' => 'delete_all_records',
460 - 'title' => esc_html__( 'Reset Stream Database', 'stream' ),
461 - 'type' => $is_running_deletion ? 'none' : 'link',
462 - 'href' => add_query_arg(
463 - array(
464 - 'action' => 'wp_stream_reset',
465 - 'wp_stream_nonce_reset' => wp_create_nonce( 'stream_nonce_reset' ),
466 - ),
467 - admin_url( 'admin-ajax.php' )
468 - ),
469 - 'class' => 'warning',
470 - 'desc' => esc_html( $this->get_deletion_warning( $is_running_deletion ) ),
471 - 'default' => 0,
472 - 'sticky' => 'bottom',
473 - );
474 - }
475 -
476 - /**
477 - * Build the "Clean Orphaned Meta" settings field definition.
478 - *
479 - * Extracted so the auto-purge running-state check
480 - * ({@see Admin::is_running_auto_purge()}) is evaluated once per render
481 - * instead of once per field property, and only in admin context — the
482 - * field is never rendered outside admin, so the Action Scheduler query
483 - * is skipped on front-end pageloads.
484 - *
485 - * @return array
486 - */
487 - private function build_clean_orphan_meta_field() {
488 - $is_running = is_admin() ? Admin::is_running_auto_purge() : false;
489 -
490 - return array(
491 - 'name' => 'clean_orphan_meta',
492 - 'title' => esc_html__( 'Clean Orphaned Meta', 'stream' ),
493 - 'type' => $is_running ? 'none' : 'link',
494 - 'href' => add_query_arg(
495 - array(
496 - 'action' => 'wp_stream_clean_orphan_meta',
497 - 'wp_stream_nonce_clean_orphan_meta' => wp_create_nonce( 'stream_nonce_clean_orphan_meta' ),
498 - ),
499 - admin_url( 'admin-ajax.php' )
500 - ),
501 - 'desc' => $is_running
502 - ? esc_html__( 'Auto-purge is currently running. The orphan reaper will execute as part of that cycle; the manual cleanup link is hidden to avoid duplicating the work.', 'stream' )
503 - : esc_html__( 'Schedules an immediate background cleanup of stream_meta rows whose parent record is missing. Safe to run while Stream is in use; runs once via Action Scheduler.', 'stream' ),
504 - 'default' => 0,
505 - 'sticky' => 'bottom',
506 - );
507 - }
508 -
509 - /**
510 - * Returns a single setting value, reading the network-level option when
511 - * Stream is network-activated on multisite.
512 - *
513 - * Settings::get_options() only loads from get_site_option() inside
514 - * is_network_admin() screens. In REST and frontend contexts on a
515 - * network-activated install, $this->options reflects the (typically empty)
516 - * per-site option, which would silently mask a network-admin-controlled
517 - * setting. This accessor handles that case so callers don't have to
518 - * duplicate the multisite branching.
519 - *
520 - * @param string $key Fully-qualified setting key (e.g. "advanced_enable_abilities_api").
521 - * @param mixed $default_value Value returned when the setting is not present.
522 - *
523 - * @return mixed
524 - */
525 - public function get_setting_value( $key, $default_value = null ) {
526 - if (
527 - is_multisite()
528 - && isset( $this->plugin )
529 - && $this->plugin->is_network_activated()
530 - ) {
531 - $options = (array) get_site_option( $this->network_options_key, array() );
532 - } else {
533 - $options = (array) $this->options;
534 - }
535 -
536 - return isset( $options[ $key ] ) ? $options[ $key ] : $default_value;
537 - }
538 -
539 - /**
540 - * Returns the full options array, reading the network-level option when
541 - * Stream is network-activated on multisite. Mirrors get_setting_value()
542 - * but returns the entire array.
543 - *
544 - * @return array
545 - */
546 - public function get_all_setting_values() {
547 - if (
548 - is_multisite()
549 - && isset( $this->plugin )
550 - && $this->plugin->is_network_activated()
551 - ) {
552 - return (array) get_site_option( $this->network_options_key, array() );
553 - }
554 -
555 - return (array) $this->options;
556 - }
557 -
558 - /**
559 - * Persists the options array, writing to the network-level option when
560 - * Stream is network-activated on multisite. Used by REST/ability writers
561 - * which run outside is_network_admin() but must respect the authoritative
562 - * store. Refreshes $this->options afterwards so in-request reads see the
563 - * new values.
564 - *
565 - * @param array $options Full options array to persist (caller is responsible
566 - * for merging over existing values when desired).
567 - *
568 - * @return bool True on a successful write, false on no-op or failure.
569 - */
570 - public function update_all_setting_values( array $options ) {
571 - $is_network = (
572 - is_multisite()
573 - && isset( $this->plugin )
574 - && $this->plugin->is_network_activated()
575 - );
576 -
577 - if ( $is_network ) {
578 - $result = update_site_option( $this->network_options_key, $options );
579 - } else {
580 - $result = update_option( $this->option_key, $options );
581 - }
582 -
583 - // Refresh the in-memory copy so subsequent reads in the same request
584 - // see the updated values. On network-activated installs we re-read
585 - // from the network option directly because Settings::get_options()
586 - // gates on is_network_admin() and would return the (now-stale)
587 - // per-site option in REST contexts. Merge defaults on top so callers
588 - // reading $plugin->settings->options keep seeing a fully-populated
589 - // array (matches get_options()'s historical contract).
590 - if ( $is_network ) {
591 - $defaults = $this->get_defaults( $this->option_key );
592 - $this->options = wp_parse_args(
593 - (array) get_site_option( $this->network_options_key, array() ),
594 - $defaults
595 - );
596 - } else {
597 - $this->options = $this->get_options();
598 - }
599 -
600 - return (bool) $result;
601 - }
602 -
603 - /**
604 431 * Returns a list of options based on the current screen.
605 432 *
606 433 * @return array
607 434 */
@@ -644,47 +471,8 @@
644 471 return (array) $defaults;
645 472 }
646 473
647 474 /**
648 - * Retrieves the deletion warning message based on the site type
649 - * and whether or not there is currently a process running to delete the tables.
650 - *
651 - * @param bool|null $is_running_deletion Optional pre-computed deletion state.
652 - * Pass to avoid a duplicate Action Scheduler
653 - * query when the caller has already checked.
654 - * Defaults to checking only in admin context.
655 - * Untyped parameter to remain compatible with
656 - * phpcs.xml.dist testVersion=7.0- (nullable
657 - * type declarations require PHP 7.1+).
658 - * @return string The deletion warning message.
659 - */
660 - public function get_deletion_warning( $is_running_deletion = null ): string {
661 -
662 - if ( null === $is_running_deletion ) {
663 - $is_running_deletion = is_admin() ? Admin::is_running_async_deletion() : false;
664 - }
665 -
666 - if ( $is_running_deletion ) {
667 -
668 - $warning = __( 'Currently deleting records. Please be patient, this can take a while.', 'stream' );
669 -
670 - } elseif ( $this->plugin->is_multisite_network_activated() ) {
671 -
672 - $warning = __( 'Warning: This will delete all activity records from the database for all sites.', 'stream' );
673 -
674 - } elseif ( $this->plugin->is_multisite_not_network_activated() ) {
675 -
676 - $warning = __( 'Warning: This will delete all activity records from the database for this site.', 'stream' );
677 -
678 - } else {
679 -
680 - $warning = __( 'Warning: This will delete all activity records from the database.', 'stream' );
681 - }
682 -
683 - return $warning;
684 - }
685 -
686 - /**
687 475 * Registers settings fields and sections
688 476 *
689 477 * @return void
690 478 */
@@ -979,16 +767,8 @@
979 767 esc_attr( $href ),
980 768 esc_attr( $title )
981 769 );
982 770 break;
983 - case 'none':
984 - // Intentional no-op: callers set 'none' to hide a control's value
985 - // column while still letting the row label + description render
986 - // (e.g. Reset Stream Database while a deletion is running, or
987 - // Clean Orphaned Meta while the auto-purge chain is active).
988 - // The description string carries the running-state message.
989 - $output = '';
990 - break;
991 771 case 'select2':
992 772 if ( ! isset( $current_value ) ) {
993 773 $current_value = '';
994 774 }
@@ -1393,38 +1173,11 @@
1393 1173 $ttl_after = isset( $new_value['general_records_ttl'] ) ? (int) $new_value['general_records_ttl'] : - 1;
1394 1174
1395 1175 if ( $ttl_after < $ttl_before ) {
1396 1176 /**
1397 - * Fires when the records TTL is shortened.
1398 - *
1399 - * Preserved for backward compatibility with third-party code that
1400 - * hooked this action in Stream <= 4.1.x. The auto-purge itself
1401 - * no longer listens to this hook (it was migrated to Action
1402 - * Scheduler), so trigger the purge directly below.
1177 + * Action assists in purging when TTL is shortened
1403 1178 */
1404 1179 do_action( 'wp_stream_auto_purge' );
1405 -
1406 - // Trigger an immediate auto-purge cycle so the shortened TTL
1407 - // takes effect now instead of at the next 12h recurring tick.
1408 - //
1409 - // Enqueue the recurring action as a one-shot async action so the
1410 - // work serializes through the scheduler. Calling
1411 - // purge_scheduled_action() inline here would bypass the overlap
1412 - // guard's view of "in-flight" work (the current request is not a
1413 - // scheduled action) and could stack a parallel chain when a
1414 - // real chain is already running. Falls back to inline if no
1415 - // scheduler is available (defensive — Plugin::__construct() sets it).
1416 - if ( ! empty( $this->plugin->scheduler ) ) {
1417 - if ( ! \WP_Stream\Admin::is_running_auto_purge() ) {
1418 - $this->plugin->scheduler->enqueue_async(
1419 - \WP_Stream\Admin::AUTO_PURGE_ACTION,
1420 - array(),
1421 - \WP_Stream\Admin::AUTO_PURGE_GROUP
1422 - );
1423 - }
1424 - } elseif ( isset( $this->plugin->admin ) ) {
1425 - $this->plugin->admin->purge_scheduled_action();
1426 - }
1427 1180 }
1428 1181 }
1429 1182
1430 1183 /**