__( 'Quote requests', 'primiko' ), 'callback' => array( $this, 'export' ), ); return $exporters; } /** * Registers the eraser. * * @param array $erasers Erasers. * @return array */ public function add_eraser( $erasers ) { $erasers['primiko'] = array( 'eraser_friendly_name' => __( 'Quote requests', 'primiko' ), 'callback' => array( $this, 'erase' ), ); return $erasers; } /** * Exports the requests sent with an email address. * * @param string $email Email address. * @param int $page Page of the process, from 1. * @return array */ public function export( $email, $page = 1 ) { $ids = $this->requests( $email, $page ); $data = array(); foreach ( $ids as $id ) { $items = array( array( 'name' => __( 'Sent on', 'primiko' ), 'value' => get_the_date( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), $id ), ), array( 'name' => __( 'Form', 'primiko' ), 'value' => (string) get_post_meta( $id, '_primiko_trade', true ), ), ); foreach ( (array) get_post_meta( $id, '_primiko_entries', true ) as $step ) { foreach ( isset( $step['items'] ) ? (array) $step['items'] : array() as $item ) { $items[] = array( 'name' => (string) $item['label'], 'value' => (string) $item['value'], ); } } $items[] = array( 'name' => __( 'Consent given on', 'primiko' ), 'value' => (string) get_post_meta( $id, '_primiko_consent_at', true ), ); $data[] = array( 'group_id' => 'primiko', 'group_label' => __( 'Quote requests', 'primiko' ), 'item_id' => 'primiko-request-' . $id, 'data' => $items, ); } return array( 'data' => $data, 'done' => count( $ids ) < self::PER_PAGE, ); } /** * Deletes the requests sent with an email address, with their photos. * * @param string $email Email address. * @return array */ public function erase( $email ) { // Always the first page: the requests of the previous page are gone. $ids = $this->requests( $email, 1 ); $removed = false; foreach ( $ids as $id ) { $removed = (bool) wp_delete_post( $id, true ) || $removed; } return array( 'items_removed' => $removed, 'items_retained' => false, 'messages' => array(), 'done' => count( $ids ) < self::PER_PAGE, ); } /** * Suggests a paragraph for the privacy policy of the site. */ public function add_policy_text() { if ( ! function_exists( 'wp_add_privacy_policy_content' ) ) { return; } $text = ''; foreach ( self::policy_paragraphs() as $paragraph ) { $text .= '
' . esc_html( $paragraph ) . '
'; } wp_add_privacy_policy_content( __( 'Quote requests', 'primiko' ), wp_kses_post( $text ) ); } /** * Paragraphs about quote requests for the privacy policy of the site, * built from the settings. * * @return string[] Plain text paragraphs. */ public static function policy_paragraphs() { $months = (int) Settings::get( 'retention_months' ); $keep = $months ? sprintf( /* translators: %d: number of months. */ _n( 'Requests are deleted automatically %d month after they are sent.', 'Requests are deleted automatically %d months after they are sent.', $months, 'primiko' ), $months ) : __( 'Requests are kept until they are deleted by hand.', 'primiko' ); $paragraphs = array( __( 'A quote request sent through the form of this website holds the name, email address and phone number of the visitor, the address of the work, the answers about the project and the photos added. They are only used to answer the request and prepare a quote.', 'primiko' ), __( 'Each request is saved on this website and sent by email to the business. When the visitor gives an email address, a copy is sent to it. Photos are kept in a folder that cannot be reached from the web, without their location data.', 'primiko' ) . ' ' . $keep, ); if ( Captcha::provider_name() ) { $paragraphs[] = sprintf( /* translators: %s: name of the anti-spam service, such as Cloudflare Turnstile. */ __( 'To protect the form against spam, the page that shows it uses %s, which receives technical data about the browser and the connection of the visitor.', 'primiko' ), Captcha::provider_name() ); if ( 'recaptcha' === Captcha::provider() ) { $paragraphs[] = __( 'Google reCAPTCHA loads as soon as the page opens, before the visitor fills anything in, and sets its own cookies. The privacy policy and terms of service of Google apply to it.', 'primiko' ); } } $paragraphs[] = __( 'Visitors can ask at any time for a copy of their requests or for their deletion.', 'primiko' ); return $paragraphs; } /** * IDs of the requests sent with an email address. * * @param string $email Email address. * @param int $page Page, from 1. * @return int[] */ private function requests( $email, $page ) { $email = sanitize_email( $email ); if ( ! is_email( $email ) ) { return array(); } return get_posts( array( 'post_type' => Post_Types::REQUEST, 'post_status' => array( 'private', 'publish', 'draft', 'pending', 'trash' ), 'fields' => 'ids', 'numberposts' => self::PER_PAGE, 'paged' => max( 1, (int) $page ), 'orderby' => 'ID', 'order' => 'ASC', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Rare admin action, on one email address. 'meta_query' => array( array( 'key' => '_primiko_email', 'value' => $email, ), ), ) ); } }