__( 'Extrova (order messages)', 'extrova' ), 'callback' => array( $this, 'export' ), ); return $exporters; } /** * Registers the eraser (`wp_privacy_personal_data_erasers`). * * @param array $erasers Erasers. */ public function register_eraser( $erasers ): array { $erasers = is_array( $erasers ) ? $erasers : array(); $erasers['extrova'] = array( 'eraser_friendly_name' => __( 'Extrova (order messages)', 'extrova' ), 'callback' => array( $this, 'erase' ), ); return $erasers; } /** * Orders of a customer (same query as WooCommerce's own exporter; HPOS compatible). * * @param string $email Email. * @param int $page Page. * @return \WC_Order[] */ private function orders( string $email, int $page ): array { $customer = array( $email ); $user = get_user_by( 'email', $email ); if ( $user instanceof \WP_User ) { $customer[] = (int) $user->ID; } $orders = wc_get_orders( array( 'limit' => self::BATCH, 'page' => max( 1, $page ), 'customer' => $customer, ) ); return is_array( $orders ) ? array_filter( $orders, static fn( mixed $o ): bool => $o instanceof \WC_Order ) : array(); } /** * Text items of an order snapshot. * * @param \WC_Order $order Order. */ private function text_items( \WC_Order $order ): array { $snapshot = $order->get_meta( OrderWriter::META_SNAPSHOT, true ); if ( ! is_array( $snapshot ) || ! isset( $snapshot['items'] ) || ! is_array( $snapshot['items'] ) ) { return array(); } return array_filter( $snapshot['items'], static fn( mixed $item ): bool => is_array( $item ) && 'text' === ( $item['type'] ?? '' ) && '' !== (string) ( $item['display_value'] ?? '' ) ); } /** * Exporter callback. * * @param string $email Email. * @param int $page Page. */ public function export( $email, $page = 1 ): array { $orders = $this->orders( (string) $email, (int) $page ); $data = array(); foreach ( $orders as $order ) { $fields = array(); foreach ( $this->text_items( $order ) as $item ) { $fields[] = array( 'name' => (string) $item['label'], 'value' => (string) $item['display_value'], ); } if ( array() !== $fields ) { $data[] = array( 'group_id' => 'extrova', 'group_label' => __( 'Order extras', 'extrova' ), 'item_id' => 'extrova-order-' . $order->get_id(), 'data' => $fields, ); } } return array( 'data' => $data, 'done' => count( $orders ) < self::BATCH, ); } /** * Eraser callback. Respects WooCommerce's "Remove personal data from orders on request" setting. * * @param string $email Email. * @param int $page Page. */ public function erase( $email, $page = 1 ): array { $orders = $this->orders( (string) $email, (int) $page ); $allowed = 'yes' === get_option( 'woocommerce_erasure_request_removes_order_data', 'no' ); $removed = false; $retained = false; $messages = array(); foreach ( $orders as $order ) { if ( array() === $this->text_items( $order ) ) { continue; } if ( ! $allowed ) { $retained = true; continue; } $this->anonymize( $order ); $order->save(); $removed = true; } if ( $retained ) { $messages[] = __( 'Order messages were retained because order data is not removed on erasure requests (WooCommerce > Settings > Accounts & Privacy).', 'extrova' ); } return array( 'items_removed' => $removed, 'items_retained' => $retained, 'messages' => $messages, 'done' => count( $orders ) < self::BATCH, ); } /** * Anonymises text values (`woocommerce_privacy_remove_order_personal_data`). WooCommerce saves the order afterwards. * * @param mixed $order Order. */ public function anonymize( $order ): void { if ( ! $order instanceof \WC_Order ) { return; } $snapshot = $order->get_meta( OrderWriter::META_SNAPSHOT, true ); if ( ! is_array( $snapshot ) || ! isset( $snapshot['items'] ) || ! is_array( $snapshot['items'] ) ) { return; } $changed = false; $ids = array(); foreach ( $snapshot['items'] as $index => $item ) { if ( is_array( $item ) && 'text' === ( $item['type'] ?? '' ) ) { $snapshot['items'][ $index ]['value'] = wp_privacy_anonymize_data( 'text', '' ); $snapshot['items'][ $index ]['display_value'] = wp_privacy_anonymize_data( 'text', '' ); $ids[ (string) ( $item['extra_id'] ?? '' ) ] = true; $changed = true; } } if ( ! $changed ) { return; } $order->update_meta_data( OrderWriter::META_SNAPSHOT, $snapshot ); foreach ( $order->get_fees() as $fee_item ) { if ( $fee_item instanceof \WC_Order_Item_Fee && isset( $ids[ (string) $fee_item->get_meta( OrderWriter::ITEM_META_ID, true ) ] ) ) { $fee_item->update_meta_data( OrderWriter::ITEM_META_VALUE, wp_privacy_anonymize_data( 'text', '' ) ); $fee_item->save(); } } } }