Index: src/wp-admin/admin-ajax.php
===================================================================
--- src/wp-admin/admin-ajax.php	(revision 42995)
+++ src/wp-admin/admin-ajax.php	(working copy)
@@ -131,6 +131,7 @@
 	'edit-theme-plugin-file',
 	'wp-privacy-export-personal-data',
 	'wp-privacy-erase-personal-data',
+	'consent-log-remove-consents',
 );
 
 // Deprecated
Index: src/wp-admin/includes/admin-filters.php
===================================================================
--- src/wp-admin/includes/admin-filters.php	(revision 42995)
+++ src/wp-admin/includes/admin-filters.php	(working copy)
@@ -48,6 +48,7 @@
 // Privacy tools
 add_action( 'account_action_failed', '_wp_privacy_account_request_failed' );
 add_action( 'admin_menu', '_wp_privacy_hook_requests_page' );
+add_action( 'admin_menu', '_wp_privacy_add_consent_log_submenu_page' );
 
 // Prerendering.
 if ( ! is_customize_preview() ) {
Index: src/wp-admin/includes/ajax-actions.php
===================================================================
--- src/wp-admin/includes/ajax-actions.php	(revision 42995)
+++ src/wp-admin/includes/ajax-actions.php	(working copy)
@@ -4637,3 +4637,45 @@
 
 	wp_send_json_success( $response );
 }
+
+/**
+ * Ajax for removing consents of a User ID
+ *
+ * @since 4.9.6
+ *
+ * @uses sanitize_text_field()
+ * @uses WP_Query()
+ * @uses have_posts()
+ * @uses the_post()
+ * @uses wp_delete_post()
+ * @uses get_the_ID()
+ * @uses wp_send_json_success()
+ */
+function wp_ajax_consent_log_remove_consents() {
+
+	$uid = sanitize_text_field( $_POST['uid'] );
+
+	$args = array(
+		'post_type'      => 'consent_log',
+		'posts_per_page' => '-1',
+		'post_status'    => array( 'consent_accepted', 'consent_declined' ),
+		'meta_query'     => array(
+			'_user_email' => array(
+				'key'   => '_cl_uid',
+				'value' => $uid,
+			),
+		),
+	);
+
+	$query = new WP_Query( $args );
+
+	if ( $query->have_posts() ) {
+		while ( $query->have_posts() ) {
+			$query->the_post();
+
+			wp_delete_post( get_the_ID() );
+		}
+	}
+
+	wp_send_json_success();
+}
Index: src/wp-admin/includes/user.php
===================================================================
--- src/wp-admin/includes/user.php	(revision 42995)
+++ src/wp-admin/includes/user.php	(working copy)
@@ -1523,3 +1523,315 @@
 	}
 
 }
+
+
+/**
+ * Add Tools submenu page for Consent Log.
+ *
+ * @since 4.9.6
+ *
+ * @uses add_submenu_page()
+ *
+ */
+function _wp_privacy_add_consent_log_submenu_page() {
+	add_submenu_page( 'tools.php', __( 'Consent Log' ), __( 'Consent Log' ), 'manage_options', 'consent_log', '_wp_privacy_consent_log_page' );
+}
+
+/**
+ * Creates the page for Consent Log
+ *
+ * @since 4.9.6
+ *
+ * @uses esc_attr_e()
+ * @uses WP_Query()
+ * @uses have_posts()
+ * @uses the_post()
+ * @uses esc_html()
+ * @uses get_post_meta()
+ * @uses get_the_ID()
+ * @uses get_the_date()
+ * @uses get_the_time()
+ *
+ * @return void
+ */
+
+function _wp_privacy_consent_log_page() {
+
+	if ( ! current_user_can( 'delete_users' ) ) {
+		wp_die( esc_html__( 'Sorry, you are not allowed to manage privacy on this site.' ) );
+	}
+
+	// "Borrow" xfn.js for now so we don't have to create new files.
+	wp_enqueue_script( 'xfn' );
+
+	?>
+	<div class="wrap">
+	<h1>Consent Log</h1>
+	<hr class="wp-header-end">
+	<form method="post" class="consents-log-remove-form" id="consents-log-remove-form">
+		<h2><?php esc_html_e( 'Remove all the consents of a user.' ); ?></h2>
+		<label for="user_id_to_remove_consent"><?php esc_html_e( 'User ID' ); ?></label>
+		<input type="text" required class="regular-text" id="user_id_to_remove_consent" name="user_id_to_remove_consent" />
+		<?php submit_button( __( 'Remove' ), 'secondary', 'submit', false ); ?>
+	</form>
+	<hr />
+	<table class="widefat striped">
+		<thead>
+			<tr>
+				<th><?php esc_attr_e( 'User ID' ); ?></th>
+				<th><?php esc_attr_e( 'Consent ID' ); ?></th>
+				<th><?php esc_attr_e( 'Status' ); ?></th>
+				<th><?php esc_attr_e( 'Date - Time' ); ?></th>
+			</tr>
+		</thead>
+		<tfoot>
+			<tr>
+				<td><?php esc_attr_e( 'User ID' ); ?></td>
+				<td><?php esc_attr_e( 'Consent ID' ); ?></td>
+				<td><?php esc_attr_e( 'Status' ); ?></td>
+				<td><?php esc_attr_e( 'Date - Time' ); ?></td>
+			</tr>
+		</tfoot>
+		<?php
+		$args = array(
+			'post_type'      => 'consent_log',
+			'posts_per_page' => '-1',
+			'post_status'    => array( 'consent_accepted', 'consent_declined' ),
+		);
+
+		$query = new WP_Query( $args );
+
+		if ( $query->have_posts() ) {
+			while ( $query->have_posts() ) {
+				$query->the_post();
+			?>
+				<tr>
+					<td class="row-title">
+						<?php
+						echo esc_html( get_post_meta( get_the_ID(), '_cl_uid', true ) );
+						?>
+					</td>
+					<td>
+						<?php
+						echo esc_html( get_post_meta( get_the_ID(), '_cl_cid', true ) );
+						?>
+					</td>
+					<td>
+						<?php
+						if ( 'consent_accepted' === get_post_status( get_the_ID() ) ) {
+							_e( 'Accepted' );
+						} else {
+							_e( 'Declined' );
+						}
+						?>
+					</td>
+					<td>
+						<?php
+						echo get_the_date() . ' - ' . get_the_time();
+						?>
+					</td>
+				</tr>
+			<?php
+			}
+		}
+		?>
+	</table>
+	<?php
+}
+
+/**
+ * Consent Log class - A class for logging consents in a consent_log custom post-type.
+ *
+ * @since 4.9.6
+ */
+class Consent_Log {
+
+	/**
+	 * Checks if the consent exists in the CPT
+	 *
+	 * @uses sanitize_text_field()
+	 * @uses WP_Query
+	 * @uses have_posts()
+	 * @uses the_post()
+	 * @uses get_the_ID()
+	 *
+	 * @param string $uid The user's email address.
+	 * @param string $cid Consent ID.
+	 *
+	 * @return mixed consent id if exists | false if there's no consent
+	 */
+	public function consent_exists( $uid, $cid ) {
+
+		$uid = sanitize_text_field( $uid );
+		$cid = sanitize_text_field( $cid );
+
+		$args = array(
+			'post_type'      => 'consent_log',
+			'posts_per_page' => '1',
+			'post_status'    => array( 'consent_accepted', 'consent_declined' ),
+			'meta_query'     => array(
+				'relation'            => 'AND',
+				'_user_email'         => array(
+					'key'   => '_cl_uid',
+					'value' => $uid,
+				),
+				'_consent_identifier' => array(
+					'key'   => '_cl_cid',
+					'value' => $cid,
+				),
+			),
+		);
+
+		$query = new WP_Query( $args );
+
+		if ( $query->have_posts() ) {
+			while ( $query->have_posts() ) {
+				$query->the_post();
+				return get_the_ID();
+			}
+		}
+
+		return false;
+	}
+
+	/**
+	 * Checks the consent is of status 1=accepted
+	 *
+	 * @uses sanitize_text_field()
+	 * @uses Consent_Log::consent_exists()
+	 * @uses get_post_meta()
+	 *
+	 * @param string $uid The user's email address.
+	 * @param string $cid Consent ID.
+	 *
+	 * @return boolean true/false depending if the consent is accepted
+	 */
+	public function has_consent( $uid, $cid ) {
+
+		$uid = sanitize_text_field( $uid );
+		$cid = sanitize_text_field( $cid );
+
+		$exists = $this->consent_exists( $uid, $cid );
+
+		if ( $exists ) {
+			if ( 'consent_accepted' === get_post_status( $exists ) ) {
+				return true;
+			}
+		}
+
+		return false;
+	}
+
+	/**
+	 * Adds a new consent in the CPT
+	 *
+	 * @uses sanitize_text_field()
+	 * @uses intval()
+	 * @uses Consent_Log::consent_exists()
+	 * @uses wp_insert_post()
+	 * @uses current_time()
+	 * @uses update_post_meta()
+	 *
+	 * @param string $uid The user's email address.
+	 * @param string $cid Consent ID.
+	 * @param mixed  $sid Consent Status.
+	 *
+	 * @return boolean true/false depending if the consent is updated
+	 */
+	public function create_consent( $uid, $cid, $sid ) {
+
+		$uid = sanitize_text_field( $uid );
+		$cid = sanitize_text_field( $cid );
+		$sid = sanitize_text_field( $sid );
+
+		$exists = $this->consent_exists( $uid, $cid );
+
+		if ( ! $exists ) {
+
+			$user_id = 0;
+
+			$consent = wp_insert_post(
+				array(
+					'post_author'   => $user_id,
+					'post_status'   => $sid,
+					'post_type'     => 'consent_log',
+					'post_date'     => current_time( 'mysql', false ),
+					'post_date_gmt' => current_time( 'mysql', true ),
+				), true
+			);
+
+			update_post_meta( $consent, '_cl_uid', $uid );
+			update_post_meta( $consent, '_cl_cid', $cid );
+
+			return true;
+		}
+
+		return false;
+	}
+
+	/**
+	 * Delete a consent from the CPT
+	 *
+	 * @uses sanitize_text_field()
+	 * @uses Consent_Log::consent_exists()
+	 * @uses wp_delete_post()
+	 * @uses current_time()
+	 *
+	 * @param string $uid The user's email address.
+	 * @param string $cid Consent ID.
+	 *
+	 * @return boolean true/false depending if the consent is deleted
+	 */
+	public function remove_consent( $uid, $cid ) {
+
+		$uid = sanitize_text_field( $uid );
+		$cid = sanitize_text_field( $cid );
+
+		$exists = $this->consent_exists( $uid, $cid );
+
+		if ( $exists ) {
+
+			wp_delete_post( $exists );
+
+			return true;
+		}
+
+		return false;
+	}
+
+	/**
+	 * Update a consent from the CPT
+	 *
+	 * @uses sanitize_text_field()
+	 * @uses intval()
+	 * @uses Consent_Log::consent_exists()
+	 * @uses wp_update_post()
+	 *
+	 * @param string $uid The user's email address.
+	 * @param string $cid Consent ID.
+	 * @param mixed  $sid Consent Status.
+	 *
+	 * @return boolean true/false depending if the consent is updated
+	 */
+	public function update_consent( $uid, $cid, $sid ) {
+
+		$uid = sanitize_text_field( $uid );
+		$cid = sanitize_text_field( $cid );
+		$sid = sanitize_text_field( $sid );
+
+		$exists = $this->consent_exists( $uid, $cid );
+
+		if ( $exists ) {
+
+			$args = array(
+				'ID'          => $exists,
+				'post_status' => $sid,
+			);
+
+			wp_update_post( $args );
+
+			return true;
+		}
+		return false;
+	}
+}
Index: src/wp-admin/js/xfn.js
===================================================================
--- src/wp-admin/js/xfn.js	(revision 42995)
+++ src/wp-admin/js/xfn.js	(working copy)
@@ -140,4 +140,25 @@
 
 		do_next_erasure( 1, 1 );
 	} );
+
+	$( '#consents-log-remove-form' ).submit( function( e ) {
+		var data,
+			uid = $( '#user_id_to_remove_consent' ).val();
+
+		e.preventDefault();
+
+		data = {
+			'action': 'consent-log-remove-consents',
+			'uid': uid,
+		};
+
+		$.post(
+			ajaxurl,
+			data,
+			function( response ) {
+				if ( true === response.success ) {
+					window.location.href = window.location.href;
+				}
+			});
+	});
 } );
Index: src/wp-includes/post.php
===================================================================
--- src/wp-includes/post.php	(revision 42995)
+++ src/wp-includes/post.php	(working copy)
@@ -258,6 +258,22 @@
 		)
 	);
 
+	register_post_type(
+		'consent_logs', array(
+			'labels'           => array(
+				'name'          => __( 'Consent Log' ),
+				'singular_name' => __( 'Consent Log' ),
+			),
+			'public'           => false,
+			'_builtin'         => true, /* internal use only. don't use this when registering your own post type. */
+			'hierarchical'     => false,
+			'rewrite'          => false,
+			'query_var'        => false,
+			'can_export'       => false,
+			'delete_with_user' => false,
+		)
+	);
+
 	register_post_status(
 		'publish', array(
 			'label'       => _x( 'Published', 'post status' ),
@@ -365,6 +381,24 @@
 			'exclude_from_search' => false,
 		)
 	);
+
+	register_post_status(
+		'consent-accepted', array(
+			'label'               => _x( 'Accepted', 'consent status' ),
+			'internal'            => true,
+			'_builtin'            => true, /* internal use only. */
+			'exclude_from_search' => false,
+		)
+	);
+
+	register_post_status(
+		'consent-declined', array(
+			'label'               => _x( 'Declined', 'consent status' ),
+			'internal'            => true,
+			'_builtin'            => true, /* internal use only. */
+			'exclude_from_search' => false,
+		)
+	);
 }
 
 /**
