439 lines
12 KiB
PHP
439 lines
12 KiB
PHP
|
|
<?php
|
||
|
|
defined( 'ABSPATH' ) or die( "you do not have access to this page!" );
|
||
|
|
|
||
|
|
if ( ! class_exists( "cmplz_admin_DNSMPD" ) ) {
|
||
|
|
class cmplz_admin_DNSMPD {
|
||
|
|
private static $_this;
|
||
|
|
function __construct() {
|
||
|
|
if ( isset( self::$_this ) ) {
|
||
|
|
wp_die( sprintf( '%s is a singleton class and you cannot create a second instance.',
|
||
|
|
get_class( $this ) ) );
|
||
|
|
}
|
||
|
|
|
||
|
|
self::$_this = $this;
|
||
|
|
|
||
|
|
add_filter( 'cmplz_do_action', array( $this, 'get_datarequests_data' ), 10, 3 );
|
||
|
|
add_action( 'cmplz_install_tables', array( $this, 'update_db_check' ), 10, 2 );
|
||
|
|
add_filter( 'cmplz_warning_types', array($this, 'new_datarequests_notice') );
|
||
|
|
}
|
||
|
|
|
||
|
|
static function this() {
|
||
|
|
return self::$_this;
|
||
|
|
}
|
||
|
|
|
||
|
|
public function sanitize_status($status){
|
||
|
|
$statuses = array('open', 'resolved', 'all');
|
||
|
|
if (in_array($status, $statuses)) return $status;
|
||
|
|
return 'open';
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Get a list of processors
|
||
|
|
* @param array $data
|
||
|
|
* @param string $action
|
||
|
|
* @param WP_REST_Request $request
|
||
|
|
*
|
||
|
|
* @return []
|
||
|
|
*/
|
||
|
|
|
||
|
|
public function get_datarequests_data($data, $action, $request){
|
||
|
|
if ( ! cmplz_user_can_manage() ) {
|
||
|
|
return [];
|
||
|
|
}
|
||
|
|
if ( $action==='get_datarequests' ){
|
||
|
|
$data = $request->get_params();
|
||
|
|
$per_page = $data['per_page'] ?? 10;
|
||
|
|
$page = $data['page'] ?? 1;
|
||
|
|
$search = $data['search'] ?? false;
|
||
|
|
$order = $data['order'] ?? 'ASC';
|
||
|
|
$orderby = $data['orderBy'] ?? 'id';
|
||
|
|
$status = $data['status'] ?? 'open';
|
||
|
|
$offset = $per_page * ( $page - 1 );
|
||
|
|
$args = array(
|
||
|
|
'number' => $per_page,
|
||
|
|
'offset' => $offset,
|
||
|
|
'order' => $order,
|
||
|
|
'orderby' => $orderby,
|
||
|
|
'status' => $this->sanitize_status( $status)
|
||
|
|
);
|
||
|
|
|
||
|
|
|
||
|
|
if ( is_email( $search ) ) {
|
||
|
|
$args['email'] = $search;
|
||
|
|
} else {
|
||
|
|
$args['name'] = $search;
|
||
|
|
}
|
||
|
|
$records = $this->get_requests( $args );
|
||
|
|
foreach ($records as $key => $record ) {
|
||
|
|
$records[ $key ]->type = $this->get_request_type( $record );
|
||
|
|
$records[ $key ]->request_date = date_i18n( get_option( 'date_format' ), $record->request_date );;
|
||
|
|
}
|
||
|
|
$open_args = $args;
|
||
|
|
$open_args['status'] = 'open';
|
||
|
|
$data = [
|
||
|
|
'records' => $records,
|
||
|
|
'totalRecords' => $this->count_requests($args),
|
||
|
|
'totalOpen' => $this->count_requests($open_args),
|
||
|
|
];
|
||
|
|
return $data;
|
||
|
|
} else if ($action==='delete_datarequests') {
|
||
|
|
$records = $request->get_param('records');
|
||
|
|
foreach ($records as $record) {
|
||
|
|
$this->delete($record['ID']);
|
||
|
|
}
|
||
|
|
$data = [];
|
||
|
|
} else if ($action==='resolve_datarequests') {
|
||
|
|
$records = $request->get_param('records');
|
||
|
|
foreach ($records as $record) {
|
||
|
|
|
||
|
|
$this->resolve($record['ID']);
|
||
|
|
}
|
||
|
|
$data = [];
|
||
|
|
|
||
|
|
} else if ( $action === 'export_datarequests' ) {
|
||
|
|
$data = $request->get_params();
|
||
|
|
$dateStart = $data['startDate'] ?? false;
|
||
|
|
$dateEnd = $data['endDate'] ?? false;
|
||
|
|
$statusOnly = $data['statusOnly'] ?? false;
|
||
|
|
|
||
|
|
$data = $this->run_export_to_csv($dateStart, $dateEnd, $statusOnly);
|
||
|
|
}
|
||
|
|
return $data;
|
||
|
|
}
|
||
|
|
|
||
|
|
private function get_request_type($record){
|
||
|
|
$options = COMPLIANZ::$DNSMPD->datarequest_options();
|
||
|
|
if ($record->global_optout) {
|
||
|
|
return isset($options['global_optout']) ? $options['global_optout'] : '';
|
||
|
|
}
|
||
|
|
if ($record->limit_sensitive){
|
||
|
|
return isset($options['limit_sensitive']) ? $options['limit_sensitive'] : '';
|
||
|
|
}
|
||
|
|
if ($record->cross_context){
|
||
|
|
return isset($options['cross_context']) ? $options['cross_context'] : '';
|
||
|
|
}
|
||
|
|
//deprecated
|
||
|
|
if ($record->request_for_access ) {
|
||
|
|
return isset($options['request_for_access']) ? $options['request_for_access'] : '';
|
||
|
|
}
|
||
|
|
if ($record->right_to_be_forgotten){
|
||
|
|
return isset($options['right_to_be_forgotten']) ? $options['right_to_be_forgotten'] : '';
|
||
|
|
}
|
||
|
|
if ($record->right_to_data_portability){
|
||
|
|
return isset($options['right_to_data_portability']) ? $options['right_to_data_portability'] : '';
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Add new datarequests
|
||
|
|
*
|
||
|
|
* @param array $warnings
|
||
|
|
*
|
||
|
|
* @return array
|
||
|
|
*/
|
||
|
|
|
||
|
|
public function new_datarequests_notice($warnings){
|
||
|
|
$warnings['new_datarequest'] = [
|
||
|
|
'warning_condition' => 'admin_DNSMPD->has_open_requests',
|
||
|
|
'include_in_progress' => true,
|
||
|
|
'plus_one' => true,
|
||
|
|
'open' => __( 'You have open data requests.', 'complianz-gdpr' ).' '.cmplz_sprintf(__( 'Please check the data requests <a href="%s">overview page</a>.', 'complianz-gdpr' ), add_query_arg(array('page'=>'complianz#tools/data-requests'),admin_url('admin.php'))),
|
||
|
|
'dismissible' => false,
|
||
|
|
];
|
||
|
|
return $warnings;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Check if there are open requests
|
||
|
|
*
|
||
|
|
* @return bool
|
||
|
|
*/
|
||
|
|
|
||
|
|
public function has_open_requests(){
|
||
|
|
$has_requests = false;
|
||
|
|
if ( cmplz_has_region('us') || cmplz_datarequests_active() ) {
|
||
|
|
global $wpdb;
|
||
|
|
$count = $wpdb->get_var( "SELECT count(*) from {$wpdb->prefix}cmplz_dnsmpd WHERE NOT resolved = 1" );
|
||
|
|
$has_requests = $count > 0;
|
||
|
|
}
|
||
|
|
return $has_requests;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get users
|
||
|
|
* @param array $args
|
||
|
|
*
|
||
|
|
* @return array
|
||
|
|
*/
|
||
|
|
public function get_requests( $args ) {
|
||
|
|
global $wpdb;
|
||
|
|
$defaults = array(
|
||
|
|
'number' => false,
|
||
|
|
'offset' => 0,
|
||
|
|
'order' => 'DESC',
|
||
|
|
'orderby' => 'request_date',
|
||
|
|
'start_date' => 0,
|
||
|
|
'end_date' => false,
|
||
|
|
'search' => false,
|
||
|
|
);
|
||
|
|
|
||
|
|
$args = wp_parse_args( array_filter($args), $defaults );
|
||
|
|
$sql = "SELECT * from {$wpdb->prefix}cmplz_dnsmpd WHERE request_date>0 ";
|
||
|
|
$sql .= $args['end_date'] ? $wpdb->prepare( " AND request_date> %s AND request_date < %s", (int) $args['start_date'], (int) $args['end_date'] ) : "";
|
||
|
|
$sql .= $args['search'] ? " AND (name like='%".esc_sql( $args['search'])."%' OR email like='%".esc_sql( $args['search'])."%' )" : "";
|
||
|
|
// $sql .= isset($args['resolved']) ? $wpdb->prepare( " AND resolved = %d ", (int) $args['resolved'] ) : "";
|
||
|
|
if ( 'all' !== $args['status'] ) {
|
||
|
|
$sql .= $wpdb->prepare( " AND resolved = %d ", $args['status']==='resolved' ? 1 : 0 );
|
||
|
|
}
|
||
|
|
$limit = (int) $args['number'];
|
||
|
|
$orderby = $args['orderby'] ?? 'ID';
|
||
|
|
$order = $args['order'] ?? 'ASC';
|
||
|
|
$orderby = sanitize_title( $orderby );
|
||
|
|
$order = sanitize_title( $order );
|
||
|
|
$sql .= " ORDER BY " . esc_sql( $orderby ) . " " . esc_sql( $order );
|
||
|
|
$sql .= $limit>0 ? " LIMIT " . (int) $limit . " OFFSET " . (int) $args["offset"] : '';
|
||
|
|
return $wpdb->get_results( $sql );
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Count number of users
|
||
|
|
* @param $args
|
||
|
|
*
|
||
|
|
* @return int
|
||
|
|
*/
|
||
|
|
public function count_requests( $args ) {
|
||
|
|
unset( $args['number'] );
|
||
|
|
$users = $this->get_requests( $args );
|
||
|
|
return count( $users );
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Handle resolve request
|
||
|
|
*
|
||
|
|
* @param int $id
|
||
|
|
*/
|
||
|
|
|
||
|
|
public function resolve(int $id): void {
|
||
|
|
if ( !cmplz_user_can_manage() ) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
global $wpdb;
|
||
|
|
$wpdb->update( $wpdb->prefix . 'cmplz_dnsmpd',
|
||
|
|
array(
|
||
|
|
'resolved' => 1
|
||
|
|
),
|
||
|
|
array( 'ID' => (int) $id )
|
||
|
|
);
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Handle delete request
|
||
|
|
* @param int $id
|
||
|
|
*/
|
||
|
|
|
||
|
|
public function delete($id): void {
|
||
|
|
if ( !cmplz_user_can_manage() ) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
global $wpdb;
|
||
|
|
$wpdb->delete( $wpdb->prefix . 'cmplz_dnsmpd', array( 'ID' => (int) $id ) );
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Export all records in the current selection to a csv file
|
||
|
|
*/
|
||
|
|
|
||
|
|
public function run_export_to_csv($dateStart, $dateEnd, $statusOnly = false ){
|
||
|
|
$page_batch = 5;
|
||
|
|
if ( ! cmplz_user_can_manage() ) {
|
||
|
|
return [];
|
||
|
|
}
|
||
|
|
|
||
|
|
$offset = get_option( 'cmplz_current_datarequest_export_offset' ) ?: 0;
|
||
|
|
if ( $statusOnly ) {
|
||
|
|
$progress = get_option( 'cmplz_current_datarequest_export_progress' ) ?: 100;
|
||
|
|
$total = 1;
|
||
|
|
} else {
|
||
|
|
if ($offset===0) {
|
||
|
|
//cleanup old file
|
||
|
|
$file = $this->filepath();
|
||
|
|
if ( file_exists($file) ){
|
||
|
|
unlink($file);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
$args = array(
|
||
|
|
'number' => $page_batch,
|
||
|
|
'offset' => $offset * $page_batch,
|
||
|
|
'start_date' => strtotime($dateStart),
|
||
|
|
'end_date' => strtotime($dateEnd),
|
||
|
|
);
|
||
|
|
$offset++;
|
||
|
|
$pages_completed = $offset * $page_batch;
|
||
|
|
update_option('cmplz_datarequest_export_args', $args, false );
|
||
|
|
update_option('cmplz_current_datarequest_export_offset', $offset , false );
|
||
|
|
$total = $this->count_requests( $args );
|
||
|
|
if ($total>0) {
|
||
|
|
$data = $this->get_requests($args);
|
||
|
|
$add_header = $offset==1;
|
||
|
|
$this->create_csv_file( $data, $add_header);
|
||
|
|
$progress = 100 * ($pages_completed/$total);
|
||
|
|
$progress = $progress>100 ? 100 : $progress;
|
||
|
|
} else {
|
||
|
|
$progress = 100;
|
||
|
|
}
|
||
|
|
update_option('cmplz_current_datarequest_export_progress', $progress, false );
|
||
|
|
}
|
||
|
|
|
||
|
|
if ( $progress === 100 ) {
|
||
|
|
delete_option('cmplz_current_datarequest_export_offset' );
|
||
|
|
delete_option('cmplz_datarequest_export_args');
|
||
|
|
}
|
||
|
|
|
||
|
|
return array(
|
||
|
|
'progress' => round($progress, 0),
|
||
|
|
'exportLink' => $this->fileurl(),
|
||
|
|
'noData' => $total ===0,
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* create csv file from array
|
||
|
|
*
|
||
|
|
* @param array $data
|
||
|
|
* @param bool $add_header
|
||
|
|
* @throws Exception
|
||
|
|
*/
|
||
|
|
|
||
|
|
private function create_csv_file($data, $add_header = true ){
|
||
|
|
$delimiter=",";
|
||
|
|
require_once(ABSPATH . 'wp-admin/includes/file.php');
|
||
|
|
$upload_dir = cmplz_upload_dir();
|
||
|
|
|
||
|
|
//generate random filename for storage
|
||
|
|
if ( !get_option('cmplz_datarequest_file_name') ) {
|
||
|
|
$token = str_shuffle ( time() );
|
||
|
|
update_option('cmplz_datarequest_file_name', $token, false );
|
||
|
|
}
|
||
|
|
$filename = get_option('cmplz_datarequest_file_name');
|
||
|
|
|
||
|
|
//set the path
|
||
|
|
$file = $upload_dir .$filename.".csv";
|
||
|
|
|
||
|
|
//'a' creates file if not existing, otherwise appends.
|
||
|
|
$csv_handle = fopen ($file,'a');
|
||
|
|
|
||
|
|
//create a line with headers
|
||
|
|
if ( $add_header ) {
|
||
|
|
$headers = $this->parse_headers_from_array( $data );
|
||
|
|
fputcsv( $csv_handle, $headers, $delimiter );
|
||
|
|
}
|
||
|
|
|
||
|
|
if ( is_array($data) ) {
|
||
|
|
foreach ( $data as $line ) {
|
||
|
|
$date = $this->localize_date($line->request_date);
|
||
|
|
$line = array_values(get_object_vars($line));
|
||
|
|
$line = array_map( 'sanitize_text_field', $line );
|
||
|
|
$line[] = $date;
|
||
|
|
fputcsv( $csv_handle, $line, $delimiter );
|
||
|
|
}
|
||
|
|
}
|
||
|
|
fclose ($csv_handle);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get headers from an array
|
||
|
|
* @param array $array
|
||
|
|
*
|
||
|
|
* @return array|bool
|
||
|
|
*/
|
||
|
|
|
||
|
|
private function parse_headers_from_array($array){
|
||
|
|
if (!isset($array[0])) return array();
|
||
|
|
$array = $array[0];
|
||
|
|
//parse object property names from object
|
||
|
|
$headers = array_keys(get_object_vars($array));
|
||
|
|
$options = COMPLIANZ::$DNSMPD->datarequest_options();
|
||
|
|
foreach ($headers as $key => $header) {
|
||
|
|
if (isset($options[$header])) {
|
||
|
|
$headers[$key] = $options[$header]['short'];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
$headers[] = __("Date","complianz-gdpr");
|
||
|
|
return $headers;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get a localized date for this row
|
||
|
|
*
|
||
|
|
* @param int $unix
|
||
|
|
*
|
||
|
|
* @return string
|
||
|
|
*/
|
||
|
|
public function localize_date(int $unix): string {
|
||
|
|
return sprintf("%s at %s", date( str_replace( 'F', 'M', get_option('date_format')), $unix ), date( get_option('time_format'), $unix ) );
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get a filepath
|
||
|
|
* @return string
|
||
|
|
*/
|
||
|
|
|
||
|
|
private function filepath(){
|
||
|
|
$upload_dir = cmplz_upload_dir();
|
||
|
|
return $upload_dir .get_option('cmplz_datarequest_file_name').".csv";
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get a file URL
|
||
|
|
* @return string
|
||
|
|
*/
|
||
|
|
|
||
|
|
private function fileurl(){
|
||
|
|
if ( file_exists($this->filepath() ) ) {
|
||
|
|
return untrailingslashit( cmplz_upload_url( get_option('cmplz_datarequest_file_name').".csv" ) );
|
||
|
|
}
|
||
|
|
return '';
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Check if the table needs to be created or updated
|
||
|
|
* @return void
|
||
|
|
*/
|
||
|
|
public function update_db_check() {
|
||
|
|
//only load on front-end if it's a cron job
|
||
|
|
if ( !is_admin() && !wp_doing_cron() ) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!wp_doing_cron() && !cmplz_user_can_manage() ) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if ( get_option( 'cmplz_dnsmpd_db_version' ) != cmplz_version ) {
|
||
|
|
require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
|
||
|
|
global $wpdb;
|
||
|
|
$charset_collate = $wpdb->get_charset_collate();
|
||
|
|
$table_name = $wpdb->prefix . 'cmplz_dnsmpd';
|
||
|
|
$sql = "CREATE TABLE $table_name (
|
||
|
|
`ID` int(11) NOT NULL AUTO_INCREMENT,
|
||
|
|
`name` varchar(255) NOT NULL,
|
||
|
|
`email` varchar(255) NOT NULL,
|
||
|
|
`region` TEXT NOT NULL,
|
||
|
|
`global_optout` int(11) NOT NULL,
|
||
|
|
`cross_context` int(11) NOT NULL,
|
||
|
|
`limit_sensitive` int(11) NOT NULL,
|
||
|
|
`request_for_access` int(11) NOT NULL,
|
||
|
|
`right_to_be_forgotten` int(11) NOT NULL,
|
||
|
|
`right_to_data_portability` int(11) NOT NULL,
|
||
|
|
`request_date` int(11) NOT NULL,
|
||
|
|
`resolved` int(11) NOT NULL,
|
||
|
|
PRIMARY KEY (ID)
|
||
|
|
) $charset_collate;";
|
||
|
|
|
||
|
|
dbDelta( $sql );
|
||
|
|
update_option( 'cmplz_dnsmpd_db_version', cmplz_version, false );
|
||
|
|
}
|
||
|
|
}
|
||
|
|
} //class closure
|
||
|
|
}
|