Current File : /home/d/i/g/digitaw/www/wp-content/plugins/simple-history/loggers/class-simple-history-logger.php
<?php

namespace Simple_History\Loggers;

use Simple_History\Event_Details\Event_Details_Group;
use Simple_History\Event_Details\Event_Details_Item;
use Simple_History\Helpers;
use Simple_History\Services\Channels_Settings_Page;

/**
 * Logs changes made on the Simple History settings page.
 */
class Simple_History_Logger extends Logger {
	/** @var string Logger slug */
	protected $slug = 'SimpleHistoryLogger';

	/** @var array<int,array<string,string>> Found changes */
	private $arr_found_changes = [];

	/**
	 * Get info about this logger.
	 *
	 * @return array
	 */
	public function get_info() {
		return [
			'name'        => _x( 'Simple History Logger', 'Logger: SimpleHistoryLogger', 'simple-history' ),
			'name_via'    => _x( 'Using plugin Simple History', 'Logger: SimpleHistoryLogger', 'simple-history' ),
			'description' => __( 'Logs changes made on the Simple History settings page.', 'simple-history' ),
			'capability'  => 'manage_options',
			'messages'    => array(
				'modified_settings'               => _x( 'Modified settings', 'Logger: SimpleHistoryLogger', 'simple-history' ),
				'regenerated_rss_feed_secret'     => _x( 'Regenerated RSS feed secret', 'Logger: SimpleHistoryLogger', 'simple-history' ),
				'cleared_log'                     => _x( 'Cleared the log for Simple History ({num_rows_deleted} rows were removed)', 'Logger: SimpleHistoryLogger', 'simple-history' ),
				'purged_events'                   => _x( 'Removed {num_rows} events that were older than {days} days', 'Logger: SimpleHistoryLogger', 'simple-history' ),
				'auto_backfill_completed'         => _x( 'Populated (backfilled) your history with {posts_imported} posts and {users_imported} users from the last {days_back} days', 'Logger: SimpleHistoryLogger', 'simple-history' ),
				'manual_backfill_completed'       => _x( 'Manual backfill created {post_events} post events and {user_events} user events', 'Logger: SimpleHistoryLogger', 'simple-history' ),
				'channel_auto_disabled'           => _x( 'Log forwarding channel "{channel_name}" was auto-disabled after {failure_count} consecutive failures', 'Logger: SimpleHistoryLogger', 'simple-history' ),
				'log_forwarding_settings_updated' => _x( 'Updated Log Forwarding settings', 'Logger: SimpleHistoryLogger', 'simple-history' ),
			),
		];
	}

	/**
	 * Called when service is loaded.
	 *
	 * @return void
	 */
	public function loaded() {
		add_action( 'load-options.php', [ $this, 'on_load_options_page' ] );
		add_action( 'simple_history/rss_feed/secret_updated', [ $this, 'on_rss_feed_secret_updated' ] );
		add_action( 'simple_history/settings/log_cleared', [ $this, 'on_log_cleared' ] );
		add_action( 'simple_history/db/purge_done', [ $this, 'on_purge_done' ], 10, 2 );
		add_action( 'simple_history/backfill/completed', [ $this, 'on_backfill_completed' ] );
		add_action( 'simple_history/channel/auto_disabled', [ $this, 'on_channel_auto_disabled' ], 10, 2 );
	}

	/**
	 * Log when the purge is done.
	 *
	 * @param int $days Number of days to keep.
	 * @param int $total_rows Total number of rows deleted across all batches.
	 * @return void
	 */
	public function on_purge_done( $days, $total_rows ) {
		// Don't log if no events were purged.
		if ( $total_rows === 0 ) {
			return;
		}

		$this->info_message(
			'purged_events',
			[
				'days'     => $days,
				'num_rows' => $total_rows,
			]
		);
	}

	/**
	 * Log when backfill is completed.
	 *
	 * @param array $status Backfill status containing type, post_events_created, user_events_created, etc.
	 * @return void
	 */
	public function on_backfill_completed( $status ) {
		// Bail if no type set.
		if ( empty( $status['type'] ) ) {
			return;
		}

		$post_events  = $status['post_events_created'] ?? 0;
		$user_events  = $status['user_events_created'] ?? 0;
		$total_events = $post_events + $user_events;

		// Don't log if no events were created.
		if ( $total_events === 0 ) {
			return;
		}

		// Determine message key based on type.
		$message_key = $status['type'] === 'auto'
			? 'auto_backfill_completed'
			: 'manual_backfill_completed';

		$this->info_message(
			$message_key,
			[
				'post_events'    => $post_events,
				'user_events'    => $user_events,
				'posts_imported' => $status['posts_imported'] ?? 0,
				'users_imported' => $status['users_imported'] ?? 0,
				'days_back'      => $status['days_back'] ?? 0,
			]
		);
	}

	/**
	 * Log when the log is cleared.
	 *
	 * @param int $num_rows_deleted Number of rows deleted.
	 * @return void
	 */
	public function on_log_cleared( $num_rows_deleted ) {
		$this->info_message(
			'cleared_log',
			[
				'num_rows_deleted' => $num_rows_deleted,
			]
		);
	}

	/**
	 * Log when a channel is auto-disabled due to repeated failures.
	 *
	 * @param object $channel       The channel instance that was auto-disabled.
	 * @param int    $failure_count The number of consecutive failures.
	 * @return void
	 */
	public function on_channel_auto_disabled( $channel, $failure_count ) {
		$context = [
			'channel_name'  => $channel->get_name(),
			'channel_slug'  => $channel->get_slug(),
			'failure_count' => $failure_count,
		];

		// Get the last error message if available.
		if ( method_exists( $channel, 'get_setting' ) ) {
			$last_error = $channel->get_setting( 'last_error', [] );
			if ( ! empty( $last_error['message'] ) ) {
				$context['error_message'] = $last_error['message'];
			}
		}

		$this->warning_message( 'channel_auto_disabled', $context );
	}

	/**
	 * When Simple History settings is saved a POST request is made to
	 * options.php. We hook into that request and log the changes.
	 *
	 * @return void
	 */
	public function on_load_options_page() {
		// Bail if option_page does not exist in $_POST variable.
		// This happens when visiting /wp-admin/options.php directly.
		// phpcs:ignore WordPress.Security.NonceVerification.Missing
		if ( ! isset( $_POST['option_page'] ) ) {
			return;
		}

		// phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotValidated
		$option_page = sanitize_text_field( wp_unslash( $_POST['option_page'] ) );

		// Log changes to general settings.
		if ( $option_page === $this->simple_history::SETTINGS_GENERAL_OPTION_GROUP ) {
			// Save all changes.
			add_action( 'updated_option', array( $this, 'on_updated_option' ), 10, 3 );

			// Finally, before redirecting back to Simple History options page, log the changes.
			add_filter( 'wp_redirect', [ $this, 'commit_log_on_wp_redirect' ], 10, 2 );
		} elseif ( $option_page === Channels_Settings_Page::SETTINGS_OPTION_GROUP ) {
			// Log changes to Log Forwarding settings.
			add_filter( 'wp_redirect', [ $this, 'log_forwarding_settings_saved' ], 10, 2 );
		}
	}

	/**
	 * Log when Log Forwarding settings are saved.
	 *
	 * @param string $location URL to redirect to.
	 * @param int    $status HTTP status code.
	 * @return string
	 */
	public function log_forwarding_settings_saved( $location, $status ) {
		$this->info_message( 'log_forwarding_settings_updated' );

		return $location;
	}

	/**
	 * Log when the RSS feed secret is updated.
	 *
	 * @return void
	 */
	public function on_rss_feed_secret_updated() {
		$this->info_message( 'regenerated_rss_feed_secret' );
	}

	/**
	 * Log found changes made on the Simple History settings page.
	 *
	 * @param string $location URL to redirect to.
	 * @param int    $status HTTP status code.
	 * @return string
	 */
	public function commit_log_on_wp_redirect( $location, $status ) {
		if ( count( $this->arr_found_changes ) === 0 ) {
			return $location;
		}

		$context = [];

		foreach ( $this->arr_found_changes as $change ) {
			$option = $change['option'];

			// Remove 'simple_history_' from beginning of string.
			$option = preg_replace( '/^simple_history_/', '', $option );

			$context[ "{$option}_prev" ] = $change['old_value'];
			$context[ "{$option}_new" ]  = $change['new_value'];
		}

		$this->info_message( 'modified_settings', $context );

		return $location;
	}

	/**
	 * Store all changed options in one array.
	 *
	 * @param string $option Option name.
	 * @param mixed  $old_value Old value.
	 * @param mixed  $new_value New value.
	 * @return void
	 */
	public function on_updated_option( $option, $old_value, $new_value ) {
		$this->arr_found_changes[] = [
			'option'    => $option,
			'old_value' => $old_value,
			'new_value' => $new_value,
		];
	}

	/**
	 * Get the log row details for this logger.
	 *
	 * @param object $row Log row.
	 * @return Event_Details_Group
	 */
	public function get_log_row_details_output( $row ) {
		$message_key = $row->context_message_key;

		if ( $message_key === 'purged_events' ) {
			// For message "Removed 24318 events that were older than 60 days"
			// add a text with a link with information on how to modify this.
			// If they already have the plugin, show message with link to settings page.

			if ( ! Helpers::show_promo_boxes() ) {
				$message = sprintf(
					/* translators: 1 is a link to webpage with info about how to modify number of days to keep the log */
					__( '<a href="%1$s">Set number of days the log is kept.</a>', 'simple-history' ),
					esc_url( Helpers::get_settings_page_url() )
				);
			} else {
				$message = sprintf(
				/* translators: 1 is a link to webpage with info about how to modify number of days to keep the log */
					__( '<a href="%1$s" target="_blank" class="sh-ExternalLink">Get Premium to set number of days the log is kept.</a>', 'simple-history' ),
					esc_url( Helpers::get_tracking_url( 'https://simple-history.com/add-ons/premium/', 'premium_logger_purged' ) )
				);
			}

			return '<p>' . wp_kses(
				$message,
				[
					'a' => [
						'href'   => [],
						'target' => [],
						'class'  => [],
					],
				]
			) . '</p>';
		}

		$event_details_group = ( new Event_Details_Group() )
			->add_items(
				[
					new Event_Details_Item(
						[ 'show_on_dashboard' ],
						__( 'Show on dashboard', 'simple-history' ),
					),
					new Event_Details_Item(
						[ 'show_as_page' ],
						__( 'Show as a page', 'simple-history' ),
					),
					new Event_Details_Item(
						[ 'pager_size' ],
						__( 'Items on page', 'simple-history' ),
					),
					new Event_Details_Item(
						[ 'pager_size_dashboard' ],
						__( 'Items on dashboard', 'simple-history' ),
					),
					new Event_Details_Item(
						[ 'enable_rss_feed' ],
						__( 'RSS feed enabled', 'simple-history' ),
					),
					new Event_Details_Item(
						[ 'detective_mode_enabled' ],
						__( 'Detective Mode enabled', 'simple-history' ),
					),
				]
			)
			->set_title( __( 'Changed items', 'simple-history' ) );

		return $event_details_group;
	}
}