includes/privacy/class-llms-privacy-erasers.php
<?php
/**
* LifterLMS Privacy Eraser
*
* @package LifterLMS/Privacy/Classes
*
* @since 3.18.0
* @version 3.30.3
*/
defined( 'ABSPATH' ) || exit;
/**
* LifterLMS Privacy Eraser class
*
* @since 3.18.0
* @since 3.30.3 Fixed spelling errors.
*/
class LLMS_Privacy_Erasers extends LLMS_Privacy {
/**
* Erase student certificate data by email address
*
* @param string $email_address email address of the user to retrieve data for
* @param int $page process page number
* @return array
* @since 3.18.0
* @version 3.18.0
*/
public static function achievement_data( $email_address, $page ) {
$ret = self::get_return();
$student = parent::get_student_by_email( $email_address );
if ( ! $student ) {
return $ret;
}
$messages = array();
$achievements = self::get_student_achievements( $student );
if ( $achievements ) {
foreach ( $achievements as $achievement ) {
$messages[] = sprintf( 'Achievement %d deleted.', $achievement->get( 'id' ) );
$achievement->delete();
}
}
return self::get_return( $messages, true, ( $messages ) );
}
/**
* Setup anonymous values for anonymized data
*
* @param string $val default anonymous value ('')
* @param string $prop key name of the property
* @param obj $obj related object
* @return mixed
* @since 3.18.0
* @version 3.18.0
*/
public static function anonymize_prop( $val, $prop, $obj = null ) {
switch ( $prop ) {
case 'user_id':
$val = 0;
break;
}
return $val;
}
/**
* Erase student certificate data by email address
*
* @param string $email_address email address of the user to retrieve data for
* @param int $page process page number
* @return array
* @since 3.18.0
* @version 3.18.0
*/
public static function certificate_data( $email_address, $page ) {
$ret = self::get_return();
$student = parent::get_student_by_email( $email_address );
if ( ! $student ) {
return $ret;
}
$messages = array();
$certs = self::get_student_certificates( $student );
if ( $certs ) {
foreach ( $certs as $cert ) {
$messages[] = sprintf( 'Certificate %d deleted.', $cert->get( 'id' ) );
$cert->delete();
}
}
return self::get_return( $messages, true, ( $messages ) );
}
/**
* Return export data to an exporter
*
* @param array $messages array of messages
* @return array
* @since 3.18.0
* @version 3.18.0
*/
private static function get_return( $messages = array(), $done = true, $removed = false, $retained = false ) {
return array(
'messages' => $messages,
'done' => $done,
'items_removed' => $removed,
'items_retained' => $retained,
);
}
/**
* Erase notifications for a student
*
* @param LLMS_Student $student
* @return array
* @since 3.18.0
* @version 3.18.0
*/
private static function erase_notification_data( $student ) {
$messages = array();
global $wpdb;
$deleted = $wpdb->query(
$wpdb->prepare(
"DELETE FROM {$wpdb->prefix}lifterlms_notifications WHERE user_id = %d OR subscriber = %d",
$student->get( 'id' ),
$student->get( 'id' )
)
);
if ( $deleted ) {
// Translators: %d = number of notifications.
$messages[] = sprintf( __( 'Removed %d notifications.', 'lifterlms' ), $deleted );
}
return apply_filters( 'llms_privacy_erase_notification_data', $messages, $student );
}
/**
* Erase and anonymize an order
*
* @since 3.18.0
* @since 3.30.3 Fixed spelling error.
*
* @param LLMS_Order $order Order object.
* @return void
*/
private static function erase_order_data( $order ) {
// Cancel recurring orders.
if ( $order->is_recurring() && in_array( $order->get( 'status' ), array( 'llms-on-hold', 'llms-active', 'llms-pending-cancel' ) ) ) {
$order->set_status( 'cancelled' );
$order->add_note( __( 'Order cancelled during personal data erasure.', 'lifterlms' ) );
}
$props = array_keys( self::get_order_data_props( 'erasure' ) );
foreach ( $props as $prop ) {
$val = self::get_anon_prop_value( $prop );
$order->set( $prop, $val );
}
$order->set( 'anonymized', 'yes' );
$order->add_note( __( 'Personal data removed during personal data erasure.', 'lifterlms' ) );
}
/**
* Erase student data
*
* @param LLMS_Student $student
* @return array
* @since 3.18.0
* @version 3.18.0
*/
private static function erase_student_data( $student ) {
$messages = array();
$props = parent::get_student_data_props();
foreach ( $props as $prop => $name ) {
$erased = false;
$val = $student->get( $prop );
if ( $val ) {
$student->set( $prop, '' );
$erased = true;
}
if ( apply_filters( 'llms_privacy_erase_student_data_prop', $erased, $prop, $student ) ) {
/* Translators: %s Prop name. */
$messages[] = sprintf( __( 'Removed student "%s"', 'lifterlms' ), $name );
}
}
return apply_filters( 'llms_privacy_erase_student_data', $messages, $student );
}
/**
* Erase student notification data by email address
*
* @param string $email_address email address of the user to retrieve data for
* @param int $page process page number
* @return [type]
* @since 3.18.0
* @version 3.18.0
*/
public static function notification_data( $email_address, $page ) {
$ret = self::get_return();
$student = parent::get_student_by_email( $email_address );
if ( ! $student ) {
return $ret;
}
$messages = self::erase_notification_data( $student );
return self::get_return( $messages, true, ( $messages ) );
}
/**
* Erase student order data by email address
*
* @param string $email_address email address of the user to retrieve data for
* @param int $page process page number
* @return array
* @since 3.18.0
* @version 3.18.0
*/
public static function order_data( $email_address, $page ) {
$ret = self::get_return();
$student = parent::get_student_by_email( $email_address );
if ( ! $student ) {
return $ret;
}
$enabled = llms_parse_bool( get_option( 'llms_erasure_request_removes_order_data', 'no' ) );
$orders = self::get_student_orders( $student, $page );
foreach ( $orders['orders'] as $order ) {
if ( apply_filters( 'llms_privacy_erase_order_data', $enabled, $order ) ) {
self::erase_order_data( $order );
/* Translators: %d Order number. */
$ret['messages'][] = sprintf( __( 'Removed personal data from order #%d.', 'lifterlms' ), $order->get( 'id' ) );
$ret['items_removed'] = true;
} else {
/* Translators: %d Order number. */
$ret['messages'][] = sprintf( __( 'Personal data within order #%d has been retained.', 'lifterlms' ), $order->get( 'id' ) );
$ret['items_retained'] = true;
}
}
$ret['done'] = isset( $orders['done'] ) ? $orders['done'] : true;
return $ret;
}
/**
* Erase student postmeta data by email address
*
* @param string $email_address email address of the user to retrieve data for
* @param int $page process page number
* @return [type]
* @since 3.18.0
* @version 3.18.0
*/
public static function postmeta_data( $email_address, $page ) {
$ret = self::get_return();
$student = parent::get_student_by_email( $email_address );
if ( ! $student ) {
return $ret;
}
$messages = array();
$enabled = llms_parse_bool( get_option( 'llms_erasure_request_removes_lms_data', 'no' ) );
if ( apply_filters( 'llms_privacy_erase_postmeta_data', $enabled, $attempt ) ) {
global $wpdb;
$deleted = $wpdb->query(
$wpdb->prepare(
"DELETE FROM {$wpdb->prefix}lifterlms_user_postmeta WHERE user_id = %d",
$student->get( 'id' )
)
);
$ret['messages'][] = __( 'Removed all student course and membership enrollment and activity data.', 'lifterlms' );
$ret['items_removed'] = true;
} else {
$ret['messages'][] = __( 'Retained all student course and membership enrollment and activity data.', 'lifterlms' );
$ret['items_retained'] = true;
}
return $ret;
}
/**
* Erase student quiz attempt data by email address
*
* @param string $email_address email address of the user to retrieve data for
* @param int $page process page number
* @return array
* @since 3.18.0
* @version 3.18.0
*/
public static function quiz_data( $email_address, $page ) {
$ret = self::get_return();
$student = parent::get_student_by_email( $email_address );
if ( ! $student ) {
return $ret;
}
$enabled = llms_parse_bool( get_option( 'llms_erasure_request_removes_lms_data', 'no' ) );
$query = self::get_student_quizzes( $student, $page );
foreach ( $query->get_attempts() as $attempt ) {
if ( apply_filters( 'llms_privacy_erase_quiz_data', $enabled, $attempt ) ) {
/* Translators: %d quiz attempt id. */
$ret['messages'][] = sprintf( __( 'Quiz attempt #%d removed.', 'lifterlms' ), $attempt->get_id() );
$ret['items_removed'] = true;
$attempt->delete();
} else {
/* Translators: %d quiz attempt id. */
$ret['messages'][] = sprintf( __( 'Quiz attempt #%d retained.', 'lifterlms' ), $attempt->get_id() );
$ret['items_retained'] = true;
}
}
$ret['done'] = $query->has_results() ? $query->is_last_page() : true;
return $ret;
}
/**
* Erase student data by email address
*
* @param string $email_address email address of the user to retrieve data for
* @param int $page process page number
* @return [type]
* @since 3.18.0
* @version 3.18.0
*/
public static function student_data( $email_address, $page ) {
$ret = self::get_return();
$student = parent::get_student_by_email( $email_address );
if ( ! $student ) {
return $ret;
}
$messages = self::erase_student_data( $student );
return self::get_return( $messages, true, ( $messages ) );
}
}