Your IP : 216.73.216.215


Current Path : /home/flapst5/ekmtoronto.com/wp-content/plugins/wp-defender/src/controller/
Upload File :
Current File : /home/flapst5/ekmtoronto.com/wp-content/plugins/wp-defender/src/controller/class-activity-log.php

<?php
/**
 * The activity logs class.
 *
 * @package WP_Defender\Controller
 */

namespace WP_Defender\Controller;

use Calotes\Component\Request;
use Calotes\Component\Response;
use WP_Defender\Controller;

/**
 * Activites log.
 *
 * Class Activity_Log
 */
class Activity_Log extends Controller {
	/**
	 * Notification data key.
	 *
	 * @var string
	 */
	private static $notification_data_key = 'wp_defender_notifications';

	/**
	 * Maximum number of notifications.
	 *
	 * @var int
	 */
	private static $max_notification = 50;

	/**
	 * Registers routes.
	 */
	public function __construct() {
		$this->register_routes();
	}

	/**
	 * Remove settings.
	 */
	public function remove_settings() {
		delete_site_option( self::$notification_data_key );
	}

	/**
	 * Delete all the data & the cache.
	 */
	public function remove_data() {
	}

	/**
	 * Get data for frontend.
	 *
	 * @return array
	 */
	public function data_frontend(): array {
		return array_merge(
			array(
				'notifications' => $this->get_notifications_for_ui(),
			),
			$this->dump_routes_and_nonces()
		);
	}

	/**
	 * Export to array.
	 *
	 * @return array
	 */
	public function to_array(): array {
		return array();
	}

	/**
	 * Import data.
	 *
	 * @param array $data The data to import.
	 */
	public function import_data( $data ) {}

	/**
	 * Export strings
	 *
	 * @return array
	 */
	public function export_strings() {
		return array();
	}

	/**
	 * Record a notification atomically.
	 *
	 * Uses a single JSON_ARRAY_APPEND database query so concurrent background
	 * processes never overwrite each other's entries.
	 *
	 * @param mixed $notification Notification data.
	 *
	 * @return bool True if the notification was successfully stored, false otherwise.
	 */
	public function record_log( $notification ) {
		$sanitized_notification = $this->sanitize_notification( $notification );
		if ( array() === $sanitized_notification ) {
			return false;
		}

		$notifications   = $this->get_notifications();
		$notifications[] = $sanitized_notification;

		$result = update_site_option(
			self::$notification_data_key,
			$notifications
		);

		return false !== $result;
	}

	/**
	 * Add a notification from the UI.
	 *
	 * @param Request $request The request object containing filter parameters.
	 *
	 * @return Response
	 * @defender_route
	 */
	public function add_notification( Request $request ): Response {
		$data = $request->get_data(
			array(
				'notification' => array(
					'type'     => 'array',
					'sanitize' => 'sanitize_text_field',
				),
			)
		);

		if ( ! is_array( $data['notification'] ) ) {
			return new Response(
				false,
				array(
					'message' => esc_html__( 'Invalid notification data.', 'wpdef' ),
				)
			);
		}

		$success = $this->record_log( $data['notification'] );

		if ( ! $success ) {
			return new Response(
				false,
				array(
					'message' => esc_html__( 'Failed to save notification.', 'wpdef' ),
				)
			);
		}

		return new Response(
			true,
			array(
				'notifications' => $this->get_notifications_for_ui(),
			)
		);
	}

	/**
	 * Get the notifications.
	 *
	 * Reads directly from the database (bypasses object cache) to ensure
	 * writes from other background processes are visible.
	 *
	 * @return array
	 */
	public function get_notifications(): array {
		$notifications = get_site_option( self::$notification_data_key, array() );

		return is_array( $notifications ) ? $notifications : array();
	}

	/**
	 * Sanitize a notification.
	 *
	 * @param mixed $notification Notification data.
	 *
	 * @return array{id: string, timestamp: string|int, type: string, content: string, url: string}
	 */
	private function sanitize_notification( $notification ): array {
		if ( ! isset( $notification['content'] ) || '' === $notification['content'] ) {
			return array();
		}
		// Sanitize and ensure all expected fields exist.
		$sanitized              = array();
		$sanitized['id']        = isset( $notification['id'] ) ? sanitize_text_field( $notification['id'] ) : uniqid( 'defender_notification_' );
		$sanitized['timestamp'] = isset( $notification['timestamp'] ) ? sanitize_text_field( $notification['timestamp'] ) : microtime( true );
		$sanitized['type']      = isset( $notification['type'] ) ? sanitize_text_field( $notification['type'] ) : 'info';
		$sanitized['module']    = isset( $notification['module'] ) ? sanitize_text_field( $notification['module'] ) : '';
		$sanitized['content']   = isset( $notification['content'] ) ? sanitize_text_field( $notification['content'] ) : '';
		$sanitized['url']       = isset( $notification['url'] ) ? sanitize_text_field( $notification['url'] ) : '';

		return $sanitized;
	}

	/**
	 * Sanitize a notification for display in the UI.
	 * Translates the notification content.
	 *
	 * @param array $notification Notification data.
	 *
	 * @return array Sanitized notification data.
	 */
	private function sanitize_notification_for_ui( $notification ) {
		$sanitized = $this->sanitize_notification( $notification );
		if ( ! isset( $sanitized['content'] ) || '' === $sanitized['content'] ) {
			return array();
		}

		return $sanitized;
	}

	/**
	 * Get notifications.
	 *
	 * @return array
	 */
	private function get_notifications_for_ui(): array {
		$notifications = $this->get_notifications();

		if ( array() === $notifications ) {
			$this->record_log(
				array(
					'module'  => 'defender',
					'content' => esc_html__( 'Welcome to Defender', 'wpdef' ),
				)
			);
			$notifications = $this->get_notifications();
		} else {
			$notifications = $this->get_limited_notifications( $notifications );
		}

		return array_map( array( $this, 'sanitize_notification_for_ui' ), $notifications );
	}

	/**
	 * Fetch the latest notifications for the UI.
	 * Called after a long-running background process completes or dies on-page,
	 * so the frontend can replace optimistic entries with server-written ones.
	 *
	 * @return Response
	 * @defender_route
	 */
	public function fetch_notifications(): Response {
		return new Response(
			true,
			array(
				'notifications' => $this->get_notifications_for_ui(),
			)
		);
	}

	/**
	 * Get the stored list to the maximum allowed size, keeping the most recent entries.
	 *
	 * @param array $notifications Notification data.
	 *
	 * @return array
	 */
	private function get_limited_notifications( array $notifications ): array {
		// Keep only valid notification entries (arrays with expected keys).
		$notifications = array_values( array_filter( $notifications, 'is_array' ) );

		if ( count( $notifications ) <= self::$max_notification ) {
			return $notifications;
		}

		// Sort newest-first, then keep only the allowed maximum.
		usort(
			$notifications,
			function ( $a, $b ) {
				return ( $b['timestamp'] ?? 0 ) <=> ( $a['timestamp'] ?? 0 );
			}
		);

		return array_slice( $notifications, 0, self::$max_notification );
	}
}