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-recipients.php

<?php
/**
 * Manages notification recipients.
 *
 * @package WP_Defender\Controller
 */

namespace WP_Defender\Controller;

use WP_Defender\Event;
use Calotes\Helper\HTTP;
use Calotes\Component\Request;
use Calotes\Component\Response;
use WP_Defender\Component\Recipient_Directory;
use WP_Defender\Component\Recipient_Verification;

/**
 * Handles recipient routes.
 */
class Recipients extends Event {

	/**
	 * Route slug.
	 *
	 * @var string
	 */
	protected $slug = 'wdf-recipients';

	/**
	 * Notification service.
	 *
	 * @var \WP_Defender\Component\Notification
	 */
	protected $service;

	/**
	 * Recipient service.
	 *
	 * @var \WP_Defender\Component\Recipients
	 */
	protected $recipient_service;

	/**
	 * Recipient directory service.
	 *
	 * @var Recipient_Directory
	 */
	protected $recipient_directory;

	/**
	 * Recipient verification service.
	 *
	 * @var Recipient_Verification
	 */
	protected $recipient_verification;

	/**
	 * Register routes and hooks.
	 */
	public function __construct() {
		$this->register_routes();
		$this->service                = wd_di()->get( \WP_Defender\Component\Notification::class );
		$this->recipient_service      = wd_di()->get( \WP_Defender\Component\Recipients::class );
		$this->recipient_directory    = wd_di()->get( Recipient_Directory::class );
		$this->recipient_verification = wd_di()->get( Recipient_Verification::class );
		add_action( 'wp_ajax_' . Notification::SLUG_SUBSCRIBE, array( $this, 'verify_subscriber' ) );
		add_action( 'wp_ajax_nopriv_' . Notification::SLUG_SUBSCRIBE, array( $this, 'verify_subscriber' ) );
	}

	/**
	 * Add a recipient to selected notification modules.
	 *
	 * @param Request $request Request data.
	 * @return Response Response data.
	 * @defender_route
	 */
	public function add_recipient( Request $request ): Response {
		if ( ! $this->check_permission() ) {
			return new Response( false, array( 'message' => esc_html__( 'You do not have permission to perform this action.', 'wpdef' ) ) );
		}

		$data = $request->get_data(
			array(
				'name'     => array(
					'type'     => 'string',
					'sanitize' => 'sanitize_text_field',
				),
				'email'    => array(
					'type'     => 'string',
					'sanitize' => 'sanitize_email',
				),
				'statuses' => array( 'type' => 'array' ),
				'_inHouse' => array( 'type' => 'bool' ),
				'id'       => array( 'type' => 'integer' ),
			)
		);

		$name     = trim( (string) ( $data['name'] ?? '' ) );
		$email    = trim( (string) ( $data['email'] ?? '' ) );
		$statuses = array_map( 'sanitize_key', is_array( $data['statuses'] ?? null ) ? $data['statuses'] : array() );
		$user_id  = absint( $data['id'] ?? 0 );
		$in_house = (bool) ( $data['_inHouse'] ?? false ) && $user_id > 0;

		if ( '' === $name || ! filter_var( $email, FILTER_VALIDATE_EMAIL ) ) {
			return new Response( false, array( 'message' => esc_html__( 'Invalid recipient data.', 'wpdef' ) ) );
		}

		if ( ! $in_house && ! preg_match( '/^[\p{L}\s\-\']+$/u', $name ) ) {
			return new Response( false, array( 'message' => esc_html__( 'Only letters, spaces, hyphens and apostrophes allowed.', 'wpdef' ) ) );
		}

		// Block duplicate recipient emails. Messages render as React text, so keep them unescaped.
		if ( $this->recipient_directory->email_exists( $email ) ) {
			return new Response(
				false,
				array(
					'message' => __( 'This email address is already added as a recipient. Edit the existing recipient to add more report types.', 'wpdef' ),
					'code'    => 'recipient_notice',
				)
			);
		}

		// Registered users must be added via user search, not invited by email.
		if ( ! $in_house && get_user_by( 'email', $email ) ) {
			return new Response(
				false,
				array(
					'message' => __( "Registered user email can't be invited, you can add them directly at Search WordPress user tab.", 'wpdef' ),
					'code'    => 'recipient_notice',
				)
			);
		}

		if ( array() === $statuses ) {
			return new Response( false, array( 'message' => esc_html__( 'Please enable at least one notification module before inviting a recipient.', 'wpdef' ) ) );
		}

		$subscriber = array(
			'name'  => $name,
			'email' => $email,
		);
		if ( $in_house ) {
			$subscriber['id'] = $user_id;
		}

		$this->recipient_service->upsert_recipient_to_modules( $statuses, $subscriber );

		return new Response( true, $this->data_frontend() );
	}

	/**
	 * Update a recipient across notification modules.
	 *
	 * @param Request $request Request.
	 * @return Response Response data.
	 * @defender_route
	 */
	public function update_recipient( Request $request ): Response {
		if ( ! $this->check_permission() ) {
			return new Response( false, array( 'message' => esc_html__( 'You do not have permission to perform this action.', 'wpdef' ) ) );
		}

		$data = $request->get_data(
			array(
				'id'           => array(
					'type'     => 'string',
					'sanitize' => 'sanitize_text_field',
				),
				'statuses'     => array( 'type' => 'array' ),
				'profile'      => array( 'type' => 'array' ),
				'unsubscribed' => array( 'type' => 'array' ),
			)
		);

		$recipient_id = trim( (string) ( $data['id'] ?? '' ) );
		$statuses     = array_map( 'sanitize_key', is_array( $data['statuses'] ?? null ) ? $data['statuses'] : array() );
		$profile      = is_array( $data['profile'] ?? null ) ? $data['profile'] : array();
		$unsubscribed = array_map( 'sanitize_key', is_array( $data['unsubscribed'] ?? null ) ? $data['unsubscribed'] : array() );

		if ( '' === $recipient_id ) {
			return new Response( false, array( 'message' => esc_html__( 'Invalid recipient ID.', 'wpdef' ) ) );
		}

		$name     = sanitize_text_field( trim( (string) ( $profile['name'] ?? '' ) ) );
		$email    = sanitize_email( trim( (string) ( $profile['email'] ?? '' ) ) );
		$in_house = is_numeric( $recipient_id );

		if ( '' === $name || ! filter_var( $email, FILTER_VALIDATE_EMAIL ) ) {
			return new Response( false, array( 'message' => esc_html__( 'Invalid recipient data.', 'wpdef' ) ) );
		}

		if ( ! $in_house && ! preg_match( '/^[\p{L}\s\-\']+$/u', $name ) ) {
			return new Response( false, array( 'message' => esc_html__( 'Only letters, spaces, hyphens and apostrophes allowed.', 'wpdef' ) ) );
		}

		$subscriber = array(
			'name'  => $name,
			'email' => $email,
		);
		if ( $in_house ) {
			$subscriber['id']         = (int) $recipient_id;
			$subscriber['first_name'] = sanitize_text_field( trim( (string) ( $profile['firstName'] ?? '' ) ) );
			$subscriber['last_name']  = sanitize_text_field( trim( (string) ( $profile['lastName'] ?? '' ) ) );
		}

		$this->recipient_service->upsert_recipient_to_modules( $statuses, $subscriber, $unsubscribed );

		return new Response( true, $this->data_frontend() );
	}

	/**
	 * Delete a recipient from all notification modules.
	 *
	 * @param Request $request Request data.
	 * @return Response Response data.
	 * @defender_route
	 */
	public function delete_recipient( Request $request ): Response {
		if ( ! $this->check_permission() ) {
			return new Response( false, array( 'message' => esc_html__( 'You do not have permission to perform this action.', 'wpdef' ) ) );
		}

		$data = $request->get_data(
			array(
				'id' => array(
					'type'     => 'string',
					'sanitize' => 'sanitize_text_field',
				),
			)
		);

		$recipient_id = trim( (string) ( $data['id'] ?? '' ) );
		if ( '' === $recipient_id ) {
			return new Response( false, array( 'message' => esc_html__( 'Invalid recipient ID.', 'wpdef' ) ) );
		}

		$this->recipient_directory->delete_recipient( $recipient_id );

		return new Response( true, $this->data_frontend() );
	}

	/**
	 * Search WordPress users by name or email.
	 *
	 * @param Request $request Request data.
	 * @return Response Response data.
	 * @defender_route
	 */
	public function search_users( Request $request ): Response {
		if ( ! $this->check_permission() ) {
			return new Response( false, array( 'message' => esc_html__( 'You do not have permission to perform this action.', 'wpdef' ) ) );
		}

		$data   = $request->get_data(
			array(
				'search' => array(
					'type'     => 'string',
					'sanitize' => 'sanitize_text_field',
				),
			)
		);
		$search = trim( (string) ( $data['search'] ?? '' ) );

		return new Response( true, array( 'users' => $this->recipient_directory->search_users( $search ) ) );
	}

	/**
	 * Resend verification email for a pending recipient.
	 *
	 * @param Request $request Request data.
	 * @return Response Response data.
	 * @defender_route
	 */
	public function resend_verification( Request $request ): Response {
		if ( ! $this->check_permission() ) {
			return new Response( false, array( 'message' => esc_html__( 'You do not have permission to perform this action.', 'wpdef' ) ) );
		}

		$data = $request->get_data(
			array(
				'id'      => array(
					'type'     => 'string',
					'sanitize' => 'sanitize_text_field',
				),
				'pending' => array( 'type' => 'array' ),
			)
		);

		$result = $this->recipient_verification->resend_verification( $data );

		return new Response( $result['success'], $result['data'] );
	}

	/**
	 * Confirm a subscriber from an email link.
	 *
	 * @return void
	 */
	public function verify_subscriber(): void {
		$hash    = HTTP::get( 'hash', '' );
		$slug    = HTTP::get( 'uid', '' );
		$uids    = HTTP::get( 'uids', '' );
		$inhouse = HTTP::get( 'inhouse', '0' );

		if ( '1' === $inhouse && ! is_user_logged_in() ) {
			auth_redirect();
		}
		if ( ! is_string( $hash ) || '' === trim( $hash ) ) {
			wp_die( esc_html__( 'You shall not pass.', 'wpdef' ) );
		}

		$slugs = $this->recipient_verification->parse_subscription_slugs( $slug, $uids );
		if ( array() === $slugs ) {
			wp_die( esc_html__( 'You shall not pass.', 'wpdef' ) );
		}

		$is_bulk = is_string( $uids ) && '' !== trim( $uids );
		$result  = $this->recipient_verification->confirm_subscriptions( $slugs, $hash, $inhouse, $is_bulk );

		if ( ! $result['found_module'] ) {
			wp_die( esc_html__( 'You shall not pass.', 'wpdef' ) );
		}

		$this->redirect_after_verify( $inhouse, $result );
		exit; // Required after wp_safe_redirect().
	}

	/**
	 * Redirect the user after subscription verification.
	 *
	 * @param string $inhouse Whether the recipient is in-house ('1' or '0').
	 * @param array  $result  Confirmation result from recipient service.
	 * @return void
	 */
	private function redirect_after_verify( string $inhouse, array $result ): void {
		if ( '1' === $inhouse && $result['processed'] ) {
			wp_safe_redirect(
				add_query_arg(
					array(
						'slug'    => $result['redirect_slug'],
						'context' => 'subscribed',
					),
					get_edit_profile_url()
				)
			);
		} elseif ( $result['processed'] ) {
			wp_safe_redirect(
				add_query_arg(
					array(
						'defender_subscription' => 'confirmed',
						'slug'                  => $result['redirect_slug'],
						'hash'                  => HTTP::get( 'hash', '' ),
					),
					home_url()
				)
			);
		} else {
			wp_safe_redirect( home_url() );
		}
	}

	/**
	 * Get recipient data for the frontend.
	 *
	 * @return array Frontend recipient data.
	 */
	public function data_frontend(): array {
		return array_merge(
			$this->dump_routes_and_nonces(),
			array(
				'recipients'    => $this->recipient_directory->get_all_recipients(),
				'notifications' => $this->service->get_modules(),
			)
		);
	}

	/**
	 * Remove saved settings.
	 *
	 * @return void
	 */
	public function remove_settings(): void {
	}

	/**
	 * Remove stored data.
	 *
	 * @return void
	 */
	public function remove_data(): void {
	}

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

	/**
	 * Import controller data.
	 *
	 * @param array $data Import data.
	 * @return void
	 */
	public function import_data( array $data ): void {
	}

	/**
	 * Get translatable strings.
	 *
	 * @return array Translatable strings.
	 */
	public function export_strings(): array {
		return array();
	}
}