PluginProbe
Double Opt-In for Contact Form 7 – Secure, GDPR-Compliant Email Verification / 5.5.0
Double Opt-In for Contact Form 7 – Secure, GDPR-Compliant Email Verification v5.5.0
5.5.0 5.4.0 5.3.2 5.3.1 5.1.6 5.1.5 trunk 2.1.5 2.11 2.12 2.13 2.15 3.0.0 3.0.1 3.0.2 3.0.3 3.0.5 3.0.51 3.0.60 3.0.61 3.0.62 3.0.70 3.0.71 3.0.72 3.1.0 All 34 releases
double-opt-in / src / Service / PrivacyIntegration.php

PrivacyIntegration.php in Double Opt-In for Contact Form 7 – Secure, GDPR-Compliant Email Verification 5.5.0, at src/Service/PrivacyIntegration.php

256 lines 6.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Privacy Integration
4 *
5 * Integrates with WordPress Privacy Tools (Data Export + Data Erasure).
6 *
7 * @package Forge12\DoubleOptIn\Service
8 * @since 3.2.0
9 */
10
11 namespace Forge12\DoubleOptIn\Service;
12
13 use Forge12\DoubleOptIn\Entity\OptIn;
14 use Forge12\DoubleOptIn\Repository\OptInRepositoryInterface;
15 use Forge12\Shared\LoggerInterface;
16
17 if ( ! defined( 'ABSPATH' ) ) {
18 exit;
19 }
20
21 /**
22 * Class PrivacyIntegration
23 *
24 * Registers the plugin with WordPress Privacy Tools for GDPR compliance.
25 */
26 class PrivacyIntegration {
27
28 private LoggerInterface $logger;
29 private OptInRepositoryInterface $repository;
30
31 public function __construct( LoggerInterface $logger, OptInRepositoryInterface $repository ) {
32 $this->logger = $logger;
33 $this->repository = $repository;
34 }
35
36 /**
37 * Register privacy hooks.
38 *
39 * @return void
40 */
41 public function register(): void {
42 add_filter( 'wp_privacy_personal_data_exporters', array( $this, 'registerExporter' ) );
43 add_filter( 'wp_privacy_personal_data_erasers', array( $this, 'registerEraser' ) );
44
45 $this->logger->debug(
46 'Privacy integration hooks registered',
47 array(
48 'plugin' => 'double-opt-in',
49 )
50 );
51 }
52
53 /**
54 * Register the personal data exporter.
55 *
56 * @param array $exporters Existing exporters.
57 *
58 * @return array
59 */
60 public function registerExporter( array $exporters ): array {
61 $exporters['double-opt-in'] = array(
62 'exporter_friendly_name' => __( 'Double Opt-In Records', 'double-opt-in' ),
63 'callback' => array( $this, 'exportPersonalData' ),
64 );
65
66 return $exporters;
67 }
68
69 /**
70 * Register the personal data eraser.
71 *
72 * @param array $erasers Existing erasers.
73 *
74 * @return array
75 */
76 public function registerEraser( array $erasers ): array {
77 $erasers['double-opt-in'] = array(
78 'eraser_friendly_name' => __( 'Double Opt-In Records', 'double-opt-in' ),
79 'callback' => array( $this, 'erasePersonalData' ),
80 );
81
82 return $erasers;
83 }
84
85 /**
86 * Export personal data for a given email.
87 *
88 * @param string $email The email address.
89 * @param int $page The current page (pagination).
90 *
91 * @return array WordPress privacy export response.
92 */
93 public function exportPersonalData( string $email, int $page = 1 ): array {
94 $optIns = $this->repository->findByEmail( $email );
95 $items = array();
96
97 $dateFormat = get_option( 'date_format' ) . ' ' . get_option( 'time_format' );
98
99 foreach ( $optIns as $optIn ) {
100 $data = array(
101 array(
102 'name' => __( 'Email', 'double-opt-in' ),
103 'value' => $optIn->getEmail(),
104 ),
105 array(
106 'name' => __( 'Confirmed', 'double-opt-in' ),
107 'value' => $optIn->isConfirmed()
108 ? __( 'Yes', 'double-opt-in' )
109 : __( 'No', 'double-opt-in' ),
110 ),
111 array(
112 'name' => __( 'Consent Text', 'double-opt-in' ),
113 'value' => $optIn->getConsentText() ?: __( '(not recorded)', 'double-opt-in' ),
114 ),
115 array(
116 'name' => __( 'Registration Date', 'double-opt-in' ),
117 'value' => $optIn->getCreateTime() > 0
118 ? wp_date( $dateFormat, $optIn->getCreateTime() )
119 : '',
120 ),
121 array(
122 'name' => __( 'Confirmation Date', 'double-opt-in' ),
123 'value' => $optIn->getUpdateTime() > 0 && $optIn->isConfirmed()
124 ? wp_date( $dateFormat, $optIn->getUpdateTime() )
125 : '',
126 ),
127 array(
128 'name' => __( 'Opt-Out Date', 'double-opt-in' ),
129 'value' => $optIn->getOptOutTime() > 0
130 ? wp_date( $dateFormat, $optIn->getOptOutTime() )
131 : '',
132 ),
133 array(
134 'name' => __( 'Registration IP', 'double-opt-in' ),
135 'value' => $optIn->getIpRegister(),
136 ),
137 array(
138 'name' => __( 'Confirmation IP', 'double-opt-in' ),
139 'value' => $optIn->getIpConfirmation(),
140 ),
141 array(
142 'name' => __( 'Opt-Out IP', 'double-opt-in' ),
143 'value' => $optIn->getIpOptOut(),
144 ),
145 array(
146 'name' => __( 'Form ID', 'double-opt-in' ),
147 'value' => (string) $optIn->getFormId(),
148 ),
149 );
150
151 $items[] = array(
152 'group_id' => 'double-opt-in',
153 'group_label' => __( 'Double Opt-In Records', 'double-opt-in' ),
154 'item_id' => 'doi-' . $optIn->getId(),
155 'data' => $data,
156 );
157 }
158
159 $this->logger->info(
160 'Personal data exported',
161 array(
162 'plugin' => 'double-opt-in',
163 'email' => $email,
164 'count' => count( $items ),
165 )
166 );
167
168 return array(
169 'data' => $items,
170 'done' => true,
171 );
172 }
173
174 /**
175 * Erase personal data for a given email.
176 *
177 * Anonymizes PII (email, IP addresses, form content, form HTML, mail body)
178 * while retaining the consent record (timestamps, confirmed status, consent
179 * text, form ID) as proof of consent per GDPR Art. 7.
180 *
181 * @param string $email The email address.
182 * @param int $page The current page (pagination).
183 *
184 * @return array WordPress privacy eraser response.
185 */
186 public function erasePersonalData( string $email, int $page = 1 ): array {
187 $optIns = $this->repository->findByEmail( $email );
188 $retained = 0;
189 $messages = array();
190
191 foreach ( $optIns as $optIn ) {
192 $anonymized = $this->anonymizeOptIn( $optIn );
193
194 try {
195 $this->repository->save( $anonymized );
196 ++$retained;
197 } catch ( \RuntimeException $e ) {
198 $this->logger->error(
199 'Failed to anonymize OptIn',
200 array(
201 'plugin' => 'double-opt-in',
202 'id' => $optIn->getId(),
203 'error' => $e->getMessage(),
204 )
205 );
206 }
207 }
208
209 if ( $retained > 0 ) {
210 $messages[] = sprintf(
211 /* translators: %d: number of anonymized records */
212 __( '%d opt-in record(s) anonymized. Consent proof retained per GDPR Art. 7.', 'double-opt-in' ),
213 $retained
214 );
215 }
216
217 $this->logger->info(
218 'Personal data anonymized',
219 array(
220 'plugin' => 'double-opt-in',
221 'email' => $email,
222 'retained' => $retained,
223 )
224 );
225
226 return array(
227 'items_removed' => 0,
228 'items_retained' => $retained,
229 'messages' => $messages,
230 'done' => true,
231 );
232 }
233
234 /**
235 * Anonymize PII fields on an OptIn entity.
236 *
237 * Clears: email, IP addresses, form content, form HTML, mail body.
238 * Retains: ID, form ID, hash, timestamps, confirmed status, consent text, category.
239 *
240 * @param OptIn $optIn The original OptIn entity.
241 *
242 * @return OptIn The anonymized entity.
243 */
244 private function anonymizeOptIn( OptIn $optIn ): OptIn {
245 return $optIn
246 ->withEmail( 'anonymized-' . $optIn->getId() . '@deleted.invalid' )
247 ->withIpRegister( '0.0.0.0' )
248 ->withIpConfirmation( '0.0.0.0' )
249 ->withIpOptOut( '0.0.0.0' )
250 ->withContent( '' )
251 ->withForm( '' )
252 ->withMailOptIn( '' )
253 ->withFiles( '' );
254 }
255 }
256