PluginProbe ʕ •ᴥ•ʔ
Tutor LMS – eLearning and online course solution / 4.0.0
Tutor LMS – eLearning and online course solution v4.0.0
4.0.0 3.9.15 3.9.14 3.9.13 3.9.12 3.9.11 trunk 1.0.0 1.0.0-alpha 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.1.1 1.2.0 1.2.1 1.2.11 1.2.12 1.2.13 1.2.20 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.3.7 1.3.8 1.3.9 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.4.8 1.4.9 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.5.9 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.6.9 1.7.0 1.7.1 1.7.2 1.7.3 1.7.4 1.7.5 1.7.6 1.7.7 1.7.8 1.7.9 1.8.0 1.8.1 1.8.10 1.8.2 1.8.3 1.8.4 1.8.5 1.8.6 1.8.7 1.8.8 1.8.9 1.9.0 1.9.1 1.9.10 1.9.11 1.9.12 1.9.13 1.9.14 1.9.15 1.9.16 1.9.2 1.9.3 1.9.4 1.9.5 1.9.6 1.9.7 1.9.8 1.9.9 2.0.0 2.0.1 2.0.10 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8 2.0.9 2.1.0 2.1.1 2.1.10 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.1.9 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.3.0 2.4.0 2.5.0 2.6.0 2.6.1 2.6.2 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 2.7.7 3.0.0 3.0.1 3.0.2 3.1.0 3.2.0 3.2.1 3.2.2 3.2.3 3.3.0 3.3.1 3.4.0 3.4.1 3.4.2 3.5.0 3.6.0 3.6.1 3.6.2 3.6.3 3.6.4 3.7.0 3.7.1 3.7.2 3.7.3 3.7.4 3.8.0 3.8.1 3.8.2 3.8.3 3.9.0 3.9.1 3.9.10 3.9.2 3.9.3 3.9.4 3.9.5 3.9.6 3.9.7 3.9.8 3.9.9
tutor / GDPR / Controllers / UserConsent.php
tutor / GDPR / Controllers Last commit date
BaseController.php 1 day ago LegalConsent.php 1 day ago UserConsent.php 1 day ago
UserConsent.php
431 lines
1 <?php
2 /**
3 * GDPR user content controller.
4 *
5 * @package Tutor\GDPR\Controllers
6 * @author Themeum <support@themeum.com>
7 * @link https://themeum.com
8 * @since 4.0.0
9 */
10
11 namespace Tutor\GDPR\Controllers;
12
13 use Exception;
14 use Tutor\GDPR\Models\UserConsents;
15 use Tutor\Helpers\ValidationHelper;
16 use TUTOR\Input;
17 use Tutor\Traits\JsonResponse;
18 use WP_User;
19
20 defined( 'ABSPATH' ) || exit;
21
22 /**
23 * GDPR AJAX controller for user contents CRUD.
24 *
25 * @since 4.0.0
26 */
27 class UserConsent extends BaseController {
28
29 use JsonResponse;
30
31 /**
32 * User contents model.
33 *
34 * @since 4.0.0
35 *
36 * @var UserContents
37 */
38 private $model;
39
40 /**
41 * Constructor.
42 *
43 * @since 4.0.0
44 *
45 * @param bool $register_hooks When to trigger hook or not.
46 */
47 public function __construct( bool $register_hooks = true ) {
48 $this->model = new UserConsents();
49
50 if ( $register_hooks ) {
51 $this->register_hooks();
52 }
53 }
54
55 /**
56 * Register AJAX hooks.
57 *
58 * @since 4.0.0
59 *
60 * @return void
61 */
62 private function register_hooks() {
63 add_action( 'tutor_new_user_registered', array( $this, 'store_registration_consent' ), 10, 2 );
64 add_action( 'tutor_new_instructor_registered', array( $this, 'store_instructor_registration_consent' ), 10, 2 );
65 add_action( 'tutor_after_login_success', array( $this, 'store_login_consent' ), 10, 2 );
66 add_action( 'tutor_after_checkout_consent', array( $this, 'store_checkout_consent' ), 10, 2 );
67 add_action( 'wp_ajax_tutor_user_consents', array( $this, 'handle_ajax_request' ) );
68 add_action( 'tutor_render_consent_logs_button', array( $this, 'render_consent_logs_button' ) );
69 add_action( 'tutor_render_consent_logs_modal', array( $this, 'render_consent_logs_modal' ) );
70 add_filter( 'manage_users_columns', array( $this, 'add_consent_logs_column' ) );
71 add_filter( 'manage_users_custom_column', array( $this, 'render_consent_logs_column' ), 10, 3 );
72 }
73
74 /**
75 * Add consent logs column to user list table.
76 *
77 * @since 4.0.0
78 *
79 * @param array $columns User list table columns.
80 *
81 * @return array
82 */
83 public function add_consent_logs_column( $columns ) {
84 $columns['consent_logs'] = __( 'Consent Logs', 'tutor' );
85
86 ob_start();
87 $this->render_consent_logs_modal();
88 $columns['consent_logs_modal'] = ob_get_clean();
89
90 return $columns;
91 }
92
93 /**
94 * Render consent logs column.
95 *
96 * @since 4.0.0
97 *
98 * @param string $value Column value.
99 * @param string $column_name Column name.
100 * @param int $user_id User ID.
101 */
102 public function render_consent_logs_column( $value, $column_name, $user_id ) {
103 if ( 'consent_logs' !== $column_name ) {
104 return $value;
105 }
106
107 $user = get_userdata( $user_id );
108
109 if ( ! $user ) {
110 return $value;
111 }
112
113 $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>';
114
115 return $value;
116 }
117
118 /**
119 * Store registration consent
120 *
121 * @since 4.0.0
122 *
123 * @param WP_User $user User object.
124 * @param array $checked_consents The provided consent fields.
125 *
126 * @return void
127 */
128 public function store_registration_consent( WP_User $user, array $checked_consents ): void {
129 $this->create_user_consent( $user->ID, LegalConsent::DISPLAY_ON_STD_REG, $checked_consents );
130 }
131
132 /**
133 * Store instructor registration consent.
134 *
135 * @since 4.0.0
136 *
137 * @param int $user_id User id.
138 * @param array $checked_consents The provided consent fields.
139 *
140 * @return void
141 */
142 public function store_instructor_registration_consent( int $user_id, array $checked_consents ): void {
143 $this->create_user_consent( $user_id, LegalConsent::DISPLAY_ON_INS_REG, $checked_consents );
144 }
145
146 /**
147 * Store login consent
148 *
149 * @since 4.0.0
150 *
151 * @param int $user_id User id.
152 * @param array $checked_consents The provided consent fields.
153 *
154 * @return void
155 */
156 public function store_login_consent( int $user_id, array $checked_consents ): void {
157 $this->create_user_consent( $user_id, LegalConsent::DISPLAY_ON_LOGIN, $checked_consents );
158 }
159
160 /**
161 * Store checkout consent.
162 *
163 * @since 4.0.0
164 *
165 * @param int $user_id User id.
166 * @param array $checked_consents The provided consent fields.
167 *
168 * @return void
169 */
170 public function store_checkout_consent( int $user_id, array $checked_consents ): void {
171 $this->create_user_consent( $user_id, LegalConsent::DISPLAY_ON_CHECKOUT, $checked_consents );
172 }
173
174 /**
175 * Handle ajax request
176 *
177 * @since 4.0.0
178 *
179 * @return void Send json response
180 */
181 public function handle_ajax_request(): void {
182 $this->validate_ajax_request();
183
184 $user_action = Input::post( 'user_action' );
185
186 switch ( $user_action ) {
187 case 'all_consents_given_by_user':
188 $user_id = Input::post( 'user_id', 0, Input::TYPE_INT );
189
190 $validate_user = ValidationHelper::validate(
191 array( 'user_id' => 'required|is_exists' ),
192 array( 'user_id' => $user_id )
193 );
194
195 if ( ! $validate_user->success ) {
196 $this->response_bad_request( __( 'Invalid user ID', 'tutor' ) );
197 }
198
199 $consents = $this->get_all_consents_given_by_user( $user_id );
200
201 $this->json_response(
202 __( 'Consent fetched successfully', 'tutor' ),
203 $consents
204 );
205
206 break;
207 default:
208 $this->response_bad_request( __( 'Invalid action', 'tutor' ) );
209 break;
210 }
211 }
212
213
214 /**
215 * Check if the user has already given consent for a specific display key and version.
216 *
217 * @since 4.0.0
218 *
219 * @param int $user_id ID of the user.
220 * @param string $display_key Consent display key (e.g., registration, login).
221 * @param array $checked_consents Checked consent fields.
222 *
223 * @return void
224 */
225 private function create_user_consent( int $user_id, string $display_key, array $checked_consents ) {
226 $user_data = get_userdata( $user_id );
227 if ( $user_data ) {
228 $consents = LegalConsent::get_consent_by_display_key( $display_key );
229
230 if ( tutor_utils()->count( $consents ) ) {
231 foreach ( $consents as $consent ) {
232 $is_active = LegalConsent::is_active( $consent );
233
234 $args = array(
235 'source' => $display_key,
236 'version' => $consent->version,
237 'user_id' => $user_data->ID,
238 'consent_title' => $consent->consent_title,
239 );
240
241 $already_given = $this->model->get_row( $args );
242
243 if ( ! $is_active || $already_given ) {
244 continue;
245 }
246
247 $is_text_only = LegalConsent::is_text_only( $consent );
248 if ( $is_text_only ) {
249 // Store consent.
250 $this->build_and_store( $consent, $user_data, $display_key );
251 } else {
252 $consent_field = LegalConsent::get_field_name( $consent );
253 $is_checked_consent = in_array( $consent_field, $checked_consents, true );
254
255 if ( ! $is_checked_consent ) {
256 continue;
257 }
258
259 $this->build_and_store( $consent, $user_data, $display_key );
260 }
261 }
262 }
263 }
264 }
265
266 /**
267 * Retrieve all consents given by a specific user.
268 *
269 * @since 4.0.0
270 *
271 * @param int $user_id ID of the user.
272 *
273 * @return array Array of user consent records.
274 */
275 private function get_all_consents_given_by_user( int $user_id ): array {
276 $where = array(
277 'user_id' => $user_id,
278 );
279
280 $records = $this->model->get_all( $where );
281 if ( ! is_array( $records ) ) {
282 return array();
283 }
284
285 $records = array_map(
286 function ( $record ) {
287 if ( ! isset( $record->created_at_gmt ) ) {
288 return $record;
289 }
290
291 $created_at = strtotime( $record->created_at_gmt . ' UTC' );
292 if ( false === $created_at ) {
293 return $record;
294 }
295
296 $record->time_ago = sprintf(
297 /* translators: %s human-readable time difference. */
298 __( '%s ago', 'tutor' ),
299 human_time_diff( $created_at, time() )
300 );
301
302 return $record;
303 },
304 $records
305 );
306
307 return $records;
308 }
309
310 /**
311 * Build and store give consent
312 *
313 * @since 4.0.0
314 *
315 * @param Object $consent Consent object.
316 * @param WP_User $user_data User data object.
317 * @param string $display_key Display key.
318 */
319 private function build_and_store( $consent, $user_data, $display_key ) {
320 $build_consent = LegalConsent::build_consent_snapshot( $consent );
321 if ( ! empty( $build_consent ) ) {
322 $build_consent['user_id'] = $user_data->ID;
323 $build_consent['user_email'] = $user_data->user_email;
324 $build_consent['source'] = $display_key;
325
326 try {
327 $this->create( $build_consent );
328 } catch ( \Throwable $th ) {
329 tutor_log( $th );
330 }
331 }
332 }
333
334 /**
335 * Create user content entry.
336 *
337 * @since 4.0.0
338 *
339 * @throws Exception If failed to store consent.
340 *
341 * @param array $data Request data.
342 *
343 * @return int On success consent id
344 */
345 private function create( array $data ) {
346 $user_consent_id = $this->model->create( $data );
347 if ( ! $user_consent_id ) {
348 throw new Exception( esc_html__( 'Failed to store consent', 'tutor' ) );
349 }
350
351 return $user_consent_id;
352 }
353
354 /**
355 * Check if a user already gave consent for a display key and version.
356 *
357 * @since 4.0.0
358 *
359 * @param string $display_key Consent display key.
360 * @param string $version Consent version.
361 * @param int $user_id User ID. Defaults to current user.
362 *
363 * @return bool
364 */
365 private function is_consent_given_by_user( string $display_key, string $version, int $user_id ): bool {
366 $user_data = get_userdata( $user_id );
367 if ( ! $user_data ) {
368 return false;
369 }
370
371 $given_consent = $this->model->is_consent_given_by_user( $user_id, $display_key, $version );
372
373 return $given_consent;
374 }
375
376 /**
377 * Render consent logs button.
378 * Called via action hook.
379 *
380 * @since 4.0.0
381 *
382 * @param object $user_data User list item object.
383 */
384 public function render_consent_logs_button( $user_data ): void {
385 $user_id = $user_data->ID ?? 0;
386 if ( ! $user_id ) {
387 return;
388 }
389
390 $user_name = $user_data->display_name ?? '';
391 $user_joined = $user_data->user_registered ?? '';
392 $user_email = $user_data->user_email ?? '';
393 $user_login = $user_data->user_login ?? '';
394 $avatar_src = get_avatar_url( $user_id, array( 'size' => 40 ) );
395 ?>
396 <div class="tutor-dropdown-parent">
397 <button type="button" class="tutor-iconic-btn" action-tutor-dropdown="toggle">
398 <span class="tutor-icon-kebab-menu" aria-hidden="true"></span>
399 </button>
400 <div id="user-actions-<?php echo esc_attr( $user_id ); ?>" class="tutor-dropdown tutor-dropdown-dark tutor-text-left">
401 <button
402 type="button"
403 class="tutor-dropdown-item"
404 data-tutor-modal-target="tutor-consent-logs-modal"
405 data-consent-logs-trigger
406 data-user-id="<?php echo esc_attr( $user_id ); ?>"
407 data-user-name="<?php echo esc_attr( $user_name ); ?>"
408 data-user-joined="<?php echo esc_attr( $user_joined ); ?>"
409 data-user-email="<?php echo esc_attr( $user_email ); ?>"
410 data-user-login="<?php echo esc_attr( $user_login ); ?>"
411 data-avatar-src="<?php echo esc_url( $avatar_src ); ?>"
412 >
413 <i class="tutor-icon-file-text tutor-mr-8" aria-hidden="true"></i>
414 <span><?php esc_html_e( 'Consent Logs', 'tutor' ); ?></span>
415 </button>
416 </div>
417 </div>
418 <?php
419 }
420
421 /**
422 * Render consent logs modal.
423 * Called via action hook.
424 *
425 * @since 4.0.0
426 */
427 public function render_consent_logs_modal(): void {
428 include tutor()->path . 'views/templates/consent-logs-modal.php';
429 }
430 }
431