' . __( 'When you submit a form on this site that is connected to Send4, the phone number, name and email address you entered, together with your other answers and this site\'s address, are sent to Send4 (send4.ai) so that we can follow up with you by text message. If Send4 cannot be reached at that moment, the submission is stored on this site and retried for up to about a day and a half; undeliverable submissions are deleted after seven days.', 'send4' ) . '
'; wp_add_privacy_policy_content( __( 'Send4', 'send4' ), wp_kses_post( wpautop( $content, false ) ) ); } /** * Register the exporter. * * @param array $exporters Existing exporters. * @return array */ public static function register_exporter( $exporters ) { $exporters['send4'] = array( 'exporter_friendly_name' => __( 'Send4 form submissions', 'send4' ), 'callback' => array( __CLASS__, 'export' ), ); return $exporters; } /** * Register the eraser. * * @param array $erasers Existing erasers. * @return array */ public static function register_eraser( $erasers ) { $erasers['send4'] = array( 'eraser_friendly_name' => __( 'Send4 form submissions', 'send4' ), 'callback' => array( __CLASS__, 'erase' ), ); return $erasers; } /** * Export whatever is still queued for this email address. * * @param string $email_address Email address. * @param int $page Page number, 1-indexed. * @return array */ public static function export( $email_address, $page = 1 ) { list( $rows, $done ) = self::page_of_rows( 'export', $email_address, $page ); $items = array(); foreach ( $rows as $row ) { $envelope = Send4_Queue::envelope( $row ); $lead = isset( $envelope['lead'] ) && is_array( $envelope['lead'] ) ? $envelope['lead'] : array(); $data = array( array( 'name' => __( 'Form', 'send4' ), 'value' => $row->form_key, ), array( 'name' => __( 'Submitted', 'send4' ), 'value' => $row->created_at . ' UTC', ), ); $labels = array( 'name' => __( 'Name', 'send4' ), 'email' => __( 'Email', 'send4' ), 'phone' => __( 'Phone', 'send4' ), ); foreach ( $labels as $key => $label ) { if ( ! empty( $lead[ $key ] ) ) { $data[] = array( 'name' => $label, 'value' => $lead[ $key ], ); } } if ( ! empty( $envelope['data'] ) && is_array( $envelope['data'] ) ) { foreach ( $envelope['data'] as $key => $value ) { $data[] = array( 'name' => (string) $key, 'value' => is_scalar( $value ) ? (string) $value : wp_json_encode( $value ), ); } } $items[] = array( 'group_id' => 'send4-queue', 'group_label' => __( 'Send4 form submissions awaiting delivery', 'send4' ), 'item_id' => 'send4-' . $row->id, 'data' => $data, ); } return array( 'data' => $items, 'done' => $done, ); } /** * Delete whatever is still queued for this email address. * * @param string $email_address Email address. * @param int $page Page number, 1-indexed. * @return array */ public static function erase( $email_address, $page = 1 ) { list( $rows, $done ) = self::page_of_rows( 'erase', $email_address, $page ); $removed = 0; foreach ( $rows as $row ) { Send4_Queue::delete( (int) $row->id ); ++$removed; } return array( 'items_removed' => $removed > 0, 'items_retained' => false, 'messages' => array(), 'done' => $done, ); } /** * One page of the queue rows whose envelope carries this email address. * * The envelope is JSON in a longtext column, so this is a scan rather than * an indexed lookup. The queue is normally empty or tiny, and a privacy * request is rare; indexing lead email would mean storing it twice. * * The scan covers the whole table, not the newest few hundred rows as it * once did. An eraser that stops early answers "done" while the site still * holds the person's data, which is the one thing a privacy tool must * never do — and a queue that swelled during a long outage is exactly when * somebody's request is most likely to be sitting past the cut-off. * * It covers it a batch per call, though, which is what WordPress's page * number is for. Walking the whole table inside one call meant decoding * every envelope, and holding every match, in a single request: on the * queue an outage or a spam run leaves behind, that is a timeout, and a * privacy request that times out has exported or erased nothing. * * Each page starts after the last id the page before it read. An id, not an * offset, because the eraser deletes as it goes and the worker delivers * underneath it: rows that disappear behind the cursor cannot shift the rows * ahead of it out of view. WordPress hands a callback nothing but the * address and the page number, so where each page starts is kept in a * transient, per page, which also makes repeating a page harmless. * * When that record is missing (it expired, or the object cache dropped it) * the call does not guess: it walks everything that is left and answers * "done". Slower, as it used to be, but it can neither skip a row nor ask * to be called again for ever. * * @param string $kind `export` or `erase`: the two never share a cursor. * @param string $email_address Email address. * @param int $page Page number, 1-indexed. * @return array{0:array