HEX
Server: LiteSpeed
System: Linux s787.bom1.mysecurecloudhost.com 4.18.0-477.13.1.lve.el8.x86_64 #1 SMP Thu Jun 1 16:40:47 EDT 2023 x86_64
User: mobilech (5348)
PHP: 8.1.34
Disabled: NONE
Upload Files
File: //proc/thread-self/cwd/wp-content/plugins/tutor/GDPR/Controllers/UserConsent.php
<?php
/**
 * GDPR user content controller.
 *
 * @package Tutor\GDPR\Controllers
 * @author Themeum <support@themeum.com>
 * @link https://themeum.com
 * @since 4.0.0
 */

namespace Tutor\GDPR\Controllers;

use Exception;
use Tutor\GDPR\Models\UserConsents;
use Tutor\Helpers\ValidationHelper;
use TUTOR\Input;
use Tutor\Traits\JsonResponse;
use WP_User;

defined( 'ABSPATH' ) || exit;

/**
 * GDPR AJAX controller for user contents CRUD.
 *
 * @since 4.0.0
 */
class UserConsent extends BaseController {

	use JsonResponse;

	/**
	 * User contents model.
	 *
	 * @since 4.0.0
	 *
	 * @var UserContents
	 */
	private $model;

	/**
	 * Constructor.
	 *
	 * @since 4.0.0
	 *
	 * @param bool $register_hooks When to trigger hook or not.
	 */
	public function __construct( bool $register_hooks = true ) {
		$this->model = new UserConsents();

		if ( $register_hooks ) {
			$this->register_hooks();
		}
	}

	/**
	 * Register AJAX hooks.
	 *
	 * @since 4.0.0
	 *
	 * @return void
	 */
	private function register_hooks() {
		add_action( 'tutor_new_user_registered', array( $this, 'store_registration_consent' ), 10, 2 );
		add_action( 'tutor_new_instructor_registered', array( $this, 'store_instructor_registration_consent' ), 10, 2 );
		add_action( 'tutor_after_login_success', array( $this, 'store_login_consent' ), 10, 2 );
		add_action( 'tutor_after_checkout_consent', array( $this, 'store_checkout_consent' ), 10, 2 );
		add_action( 'wp_ajax_tutor_user_consents', array( $this, 'handle_ajax_request' ) );
		add_action( 'tutor_render_consent_logs_button', array( $this, 'render_consent_logs_button' ) );
		add_action( 'tutor_render_consent_logs_modal', array( $this, 'render_consent_logs_modal' ) );
		add_filter( 'manage_users_columns', array( $this, 'add_consent_logs_column' ) );
		add_filter( 'manage_users_custom_column', array( $this, 'render_consent_logs_column' ), 10, 3 );
	}

	/**
	 * Add consent logs column to user list table.
	 *
	 * @since 4.0.0
	 *
	 * @param array $columns User list table columns.
	 *
	 * @return array
	 */
	public function add_consent_logs_column( $columns ) {
		$columns['consent_logs'] = __( 'Consent Logs', 'tutor' );

		ob_start();
		$this->render_consent_logs_modal();
		$columns['consent_logs_modal'] = ob_get_clean();

		return $columns;
	}

	/**
	 * Render consent logs column.
	 *
	 * @since 4.0.0
	 *
	 * @param string $value       Column value.
	 * @param string $column_name Column name.
	 * @param int    $user_id     User ID.
	 */
	public function render_consent_logs_column( $value, $column_name, $user_id ) {
		if ( 'consent_logs' !== $column_name ) {
			return $value;
		}

		$user = get_userdata( $user_id );

		if ( ! $user ) {
			return $value;
		}

		$value = '<button type="button" class="tutor-btn tutor-btn-outline-primary tutor-btn-sm" data-tutor-modal-target="tutor-consent-logs-modal" data-consent-logs-trigger data-user-id="' . esc_attr( $user_id ) . '" data-user-name="' . esc_attr( $user->display_name ) . '" data-user-joined="' . esc_attr( $user->user_registered ) . '" data-user-email="' . esc_attr( $user->user_email ) . '" data-user-login="' . esc_attr( $user->user_login ) . '" data-avatar-src="' . esc_url( tutor_utils()->get_user_avatar_url( $user_id ) ) . '"><i class="tutor-icon-eye-line tutor-mr-8" aria-hidden="true"></i>' . esc_html__( 'View Logs', 'tutor' ) . '</button>';

		return $value;
	}

	/**
	 * Store registration consent
	 *
	 * @since 4.0.0
	 *
	 * @param WP_User $user User object.
	 * @param array   $checked_consents The provided consent fields.
	 *
	 * @return void
	 */
	public function store_registration_consent( WP_User $user, array $checked_consents ): void {
		$this->create_user_consent( $user->ID, LegalConsent::DISPLAY_ON_STD_REG, $checked_consents );
	}

	/**
	 * Store instructor registration consent.
	 *
	 * @since 4.0.0
	 *
	 * @param int   $user_id User id.
	 * @param array $checked_consents The provided consent fields.
	 *
	 * @return void
	 */
	public function store_instructor_registration_consent( int $user_id, array $checked_consents ): void {
		$this->create_user_consent( $user_id, LegalConsent::DISPLAY_ON_INS_REG, $checked_consents );
	}

	/**
	 * Store login consent
	 *
	 * @since 4.0.0
	 *
	 * @param int   $user_id User id.
	 * @param array $checked_consents The provided consent fields.
	 *
	 * @return void
	 */
	public function store_login_consent( int $user_id, array $checked_consents ): void {
		$this->create_user_consent( $user_id, LegalConsent::DISPLAY_ON_LOGIN, $checked_consents );
	}

	/**
	 * Store checkout consent.
	 *
	 * @since 4.0.0
	 *
	 * @param int   $user_id User id.
	 * @param array $checked_consents The provided consent fields.
	 *
	 * @return void
	 */
	public function store_checkout_consent( int $user_id, array $checked_consents ): void {
		$this->create_user_consent( $user_id, LegalConsent::DISPLAY_ON_CHECKOUT, $checked_consents );
	}

	/**
	 * Handle ajax request
	 *
	 * @since 4.0.0
	 *
	 * @return void Send json response
	 */
	public function handle_ajax_request(): void {
		$this->validate_ajax_request();

		$user_action = Input::post( 'user_action' );

		switch ( $user_action ) {
			case 'all_consents_given_by_user':
				$user_id = Input::post( 'user_id', 0, Input::TYPE_INT );

				$validate_user = ValidationHelper::validate(
					array( 'user_id' => 'required|is_exists' ),
					array( 'user_id' => $user_id )
				);

				if ( ! $validate_user->success ) {
					$this->response_bad_request( __( 'Invalid user ID', 'tutor' ) );
				}

				$consents = $this->get_all_consents_given_by_user( $user_id );

				$this->json_response(
					__( 'Consent fetched successfully', 'tutor' ),
					$consents
				);

				break;
			default:
				$this->response_bad_request( __( 'Invalid action', 'tutor' ) );
				break;
		}
	}


	/**
	 * Check if the user has already given consent for a specific display key and version.
	 *
	 * @since 4.0.0
	 *
	 * @param int    $user_id     ID of the user.
	 * @param string $display_key Consent display key (e.g., registration, login).
	 * @param array  $checked_consents Checked consent fields.
	 *
	 * @return void
	 */
	private function create_user_consent( int $user_id, string $display_key, array $checked_consents ) {
		$user_data = get_userdata( $user_id );
		if ( $user_data ) {
			$consents = LegalConsent::get_consent_by_display_key( $display_key );

			if ( tutor_utils()->count( $consents ) ) {
				foreach ( $consents as $consent ) {
					$is_active = LegalConsent::is_active( $consent );

					$args = array(
						'source'        => $display_key,
						'version'       => $consent->version,
						'user_id'       => $user_data->ID,
						'consent_title' => $consent->consent_title,
					);

					$already_given = $this->model->get_row( $args );

					if ( ! $is_active || $already_given ) {
						continue;
					}

					$is_text_only = LegalConsent::is_text_only( $consent );
					if ( $is_text_only ) {
						// Store consent.
						$this->build_and_store( $consent, $user_data, $display_key );
					} else {
						$consent_field      = LegalConsent::get_field_name( $consent );
						$is_checked_consent = in_array( $consent_field, $checked_consents, true );

						if ( ! $is_checked_consent ) {
							continue;
						}

						$this->build_and_store( $consent, $user_data, $display_key );
					}
				}
			}
		}
	}

	/**
	 * Retrieve all consents given by a specific user.
	 *
	 * @since 4.0.0
	 *
	 * @param int $user_id ID of the user.
	 *
	 * @return array Array of user consent records.
	 */
	private function get_all_consents_given_by_user( int $user_id ): array {
		$where = array(
			'user_id' => $user_id,
		);

		$records = $this->model->get_all( $where );
		if ( ! is_array( $records ) ) {
			return array();
		}

		$records = array_map(
			function ( $record ) {
				if ( ! isset( $record->created_at_gmt ) ) {
					return $record;
				}

				$created_at = strtotime( $record->created_at_gmt . ' UTC' );
				if ( false === $created_at ) {
					return $record;
				}

				$record->time_ago = sprintf(
					/* translators: %s human-readable time difference. */
					__( '%s ago', 'tutor' ),
					human_time_diff( $created_at, time() )
				);

				return $record;
			},
			$records
		);

		return $records;
	}

	/**
	 * Build and store give consent
	 *
	 * @since 4.0.0
	 *
	 * @param Object  $consent Consent object.
	 * @param WP_User $user_data User data object.
	 * @param string  $display_key Display key.
	 */
	private function build_and_store( $consent, $user_data, $display_key ) {
		$build_consent = LegalConsent::build_consent_snapshot( $consent );
		if ( ! empty( $build_consent ) ) {
			$build_consent['user_id']    = $user_data->ID;
			$build_consent['user_email'] = $user_data->user_email;
			$build_consent['source']     = $display_key;

			try {
				$this->create( $build_consent );
			} catch ( \Throwable $th ) {
				tutor_log( $th );
			}
		}
	}

	/**
	 * Create user content entry.
	 *
	 * @since 4.0.0
	 *
	 * @throws Exception If failed to store consent.
	 *
	 * @param array $data Request data.
	 *
	 * @return int On success consent id
	 */
	private function create( array $data ) {
		$user_consent_id = $this->model->create( $data );
		if ( ! $user_consent_id ) {
			throw new Exception( esc_html__( 'Failed to store consent', 'tutor' ) );
		}

		return $user_consent_id;
	}

	/**
	 * Check if a user already gave consent for a display key and version.
	 *
	 * @since 4.0.0
	 *
	 * @param string $display_key Consent display key.
	 * @param string $version     Consent version.
	 * @param int    $user_id     User ID. Defaults to current user.
	 *
	 * @return bool
	 */
	private function is_consent_given_by_user( string $display_key, string $version, int $user_id ): bool {
		$user_data = get_userdata( $user_id );
		if ( ! $user_data ) {
			return false;
		}

		$given_consent = $this->model->is_consent_given_by_user( $user_id, $display_key, $version );

		return $given_consent;
	}

	/**
	 * Render consent logs button.
	 * Called via action hook.
	 *
	 * @since 4.0.0
	 *
	 * @param object $user_data User list item object.
	 */
	public function render_consent_logs_button( $user_data ): void {
		$user_id = $user_data->ID ?? 0;
		if ( ! $user_id ) {
			return;
		}

		$user_name   = $user_data->display_name ?? '';
		$user_joined = $user_data->user_registered ?? '';
		$user_email  = $user_data->user_email ?? '';
		$user_login  = $user_data->user_login ?? '';
		$avatar_src  = get_avatar_url( $user_id, array( 'size' => 40 ) );
		?>
		<div class="tutor-dropdown-parent">
			<button type="button" class="tutor-iconic-btn" action-tutor-dropdown="toggle">
				<span class="tutor-icon-kebab-menu" aria-hidden="true"></span>
			</button>
			<div id="user-actions-<?php echo esc_attr( $user_id ); ?>" class="tutor-dropdown tutor-dropdown-dark tutor-text-left">
				<button
					type="button"
					class="tutor-dropdown-item"
					data-tutor-modal-target="tutor-consent-logs-modal"
					data-consent-logs-trigger
					data-user-id="<?php echo esc_attr( $user_id ); ?>"
					data-user-name="<?php echo esc_attr( $user_name ); ?>"
					data-user-joined="<?php echo esc_attr( $user_joined ); ?>"
					data-user-email="<?php echo esc_attr( $user_email ); ?>"
					data-user-login="<?php echo esc_attr( $user_login ); ?>"
					data-avatar-src="<?php echo esc_url( $avatar_src ); ?>"
				>
					<i class="tutor-icon-file-text tutor-mr-8" aria-hidden="true"></i>
					<span><?php esc_html_e( 'Consent Logs', 'tutor' ); ?></span>
				</button>
			</div>
		</div>
		<?php
	}

	/**
	 * Render consent logs modal.
	 * Called via action hook.
	 *
	 * @since 4.0.0
	 */
	public function render_consent_logs_modal(): void {
		include tutor()->path . 'views/templates/consent-logs-modal.php';
	}
}