__( 'SpaceBay profile and memberships', 'spacebay' ), 'callback' => array( __CLASS__, 'export_profile' ), ); $exporters['spacebay-posts'] = array( 'exporter_friendly_name' => __( 'SpaceBay posts', 'spacebay' ), 'callback' => array( __CLASS__, 'export_posts' ), ); $exporters['spacebay-comments'] = array( 'exporter_friendly_name' => __( 'SpaceBay comments', 'spacebay' ), 'callback' => array( __CLASS__, 'export_comments' ), ); $exporters['spacebay-activity'] = array( 'exporter_friendly_name' => __( 'SpaceBay likes and saved posts', 'spacebay' ), 'callback' => array( __CLASS__, 'export_reactions' ), ); $exporters['spacebay-messages'] = array( 'exporter_friendly_name' => __( 'SpaceBay private messages', 'spacebay' ), 'callback' => array( __CLASS__, 'export_messages' ), ); $exporters['spacebay-notifications'] = array( 'exporter_friendly_name' => __( 'SpaceBay notifications', 'spacebay' ), 'callback' => array( __CLASS__, 'export_notifications' ), ); $exporters['spacebay-achievements'] = array( 'exporter_friendly_name' => __( 'SpaceBay points and badges', 'spacebay' ), 'callback' => array( __CLASS__, 'export_achievements' ), ); $exporters['spacebay-events'] = array( 'exporter_friendly_name' => __( 'SpaceBay event answers', 'spacebay' ), 'callback' => array( __CLASS__, 'export_events' ), ); $exporters['spacebay-own-events'] = array( 'exporter_friendly_name' => __( 'SpaceBay events organized', 'spacebay' ), 'callback' => array( __CLASS__, 'export_own_events' ), ); return $exporters; } /** * Registers the eraser. * * @param array $erasers Erasers. * @return array */ public static function register_erasers( $erasers ) { $erasers['spacebay'] = array( 'eraser_friendly_name' => __( 'SpaceBay', 'spacebay' ), 'callback' => array( __CLASS__, 'erase' ), ); return $erasers; } /** * Exports the profile, email choices, memberships, uploads and reports. * * @param string $email Email address. * @param int $page Page, from 1. * @return array */ public static function export_profile( $email, $page = 1 ) { $user = get_user_by( 'email', $email ); if ( ! $user || $page > 1 ) { return self::done( array() ); } $items = array(); $profile = Profiles::get( $user->ID ); if ( $profile ) { $data = array( array( 'name' => __( 'Headline', 'spacebay' ), 'value' => $profile['headline'], ), array( 'name' => __( 'About me', 'spacebay' ), 'value' => (string) $profile['bio'], ), array( 'name' => __( 'Location', 'spacebay' ), 'value' => $profile['location'], ), array( 'name' => __( 'Website', 'spacebay' ), 'value' => $profile['website'], ), array( 'name' => __( 'Profile visibility', 'spacebay' ), 'value' => $profile['privacy'], ), array( 'name' => __( 'Account status', 'spacebay' ), 'value' => $profile['status'], ), array( 'name' => __( 'Last active', 'spacebay' ), 'value' => $profile['last_activity_at'], ), array( 'name' => __( 'Profile photo', 'spacebay' ), 'value' => Profiles::image_url( $profile, 'avatar' ), ), array( 'name' => __( 'Cover image', 'spacebay' ), 'value' => Profiles::image_url( $profile, 'cover' ), ), ); foreach ( MemberDirectory::display_fields( (array) $profile['custom_fields'] ) as $field ) { $data[] = array( 'name' => $field['label'], 'value' => $field['value'], ); } $items[] = self::item( 'spacebay-profile', 'profile-' . $user->ID, __( 'Community profile', 'spacebay' ), $data ); } $prefs = array(); foreach ( Preferences::get( $user->ID ) as $group => $on ) { $prefs[] = array( 'name' => $group, 'value' => $on ? __( 'Email on', 'spacebay' ) : __( 'Email off', 'spacebay' ), ); } $items[] = self::item( 'spacebay-profile', 'email-preferences-' . $user->ID, __( 'Community email preferences', 'spacebay' ), $prefs ); foreach ( Membership::for_user( $user->ID ) as $space_id => $membership ) { $space = SpaceRepository::find( $space_id ); $items[] = self::item( 'spacebay-spaces', 'space-' . $space_id, __( 'Community space memberships', 'spacebay' ), array( array( 'name' => __( 'Space', 'spacebay' ), 'value' => $space ? $space['title'] : '#' . $space_id, ), array( 'name' => __( 'Role', 'spacebay' ), 'value' => $membership['role'], ), array( 'name' => __( 'Status', 'spacebay' ), 'value' => $membership['status'], ), ) ); } foreach ( self::rows( 'media', 'user_id', $user->ID, 'id, file_path, file_url, object_type, created_at', 1, 1000 ) as $row ) { $items[] = self::item( 'spacebay-media', 'media-' . $row['id'], __( 'Community uploads', 'spacebay' ), array( array( 'name' => __( 'Image', 'spacebay' ), // Private images give their checked address; it opens for the member when logged in. 'value' => Media::url( $row ), ), array( 'name' => __( 'Used for', 'spacebay' ), 'value' => $row['object_type'], ), array( 'name' => __( 'Uploaded', 'spacebay' ), 'value' => $row['created_at'], ), ) ); } foreach ( self::rows( 'reports', 'reporter_id', $user->ID, 'id, object_type, object_id, reason, note, created_at', 1, 1000 ) as $row ) { $items[] = self::item( 'spacebay-reports', 'report-' . $row['id'], __( 'Community reports sent', 'spacebay' ), array( array( 'name' => __( 'Reported', 'spacebay' ), 'value' => $row['object_type'] . ' #' . $row['object_id'], ), array( 'name' => __( 'Reason', 'spacebay' ), 'value' => $row['reason'], ), array( 'name' => __( 'Note', 'spacebay' ), 'value' => (string) $row['note'], ), array( 'name' => __( 'Date', 'spacebay' ), 'value' => $row['created_at'], ), ) ); } return self::done( $items ); } /** * Exports posts, one page at a time. * * @param string $email Email address. * @param int $page Page, from 1. * @return array */ public static function export_posts( $email, $page = 1 ) { $user = get_user_by( 'email', $email ); if ( ! $user ) { return self::done( array() ); } $rows = self::rows( 'posts', 'user_id', $user->ID, 'id, space_id, title, message, status, created_at', $page ); $items = array(); foreach ( $rows as $row ) { $space = SpaceRepository::find( (int) $row['space_id'] ); $items[] = self::item( 'spacebay-posts', 'post-' . $row['id'], __( 'Community posts', 'spacebay' ), array( array( 'name' => __( 'Space', 'spacebay' ), 'value' => $space ? $space['title'] : '', ), array( 'name' => __( 'Title', 'spacebay' ), 'value' => $row['title'], ), array( 'name' => __( 'Text', 'spacebay' ), 'value' => (string) $row['message'], ), array( 'name' => __( 'Status', 'spacebay' ), 'value' => $row['status'], ), array( 'name' => __( 'Date', 'spacebay' ), 'value' => $row['created_at'], ), array( 'name' => __( 'Link', 'spacebay' ), 'value' => Portal::link( 'posts/' . $row['id'] ), ), ) ); } return array( 'data' => $items, 'done' => count( $rows ) < self::PER_PAGE, ); } /** * Exports comments, one page at a time. * * @param string $email Email address. * @param int $page Page, from 1. * @return array */ public static function export_comments( $email, $page = 1 ) { $user = get_user_by( 'email', $email ); if ( ! $user ) { return self::done( array() ); } $rows = self::rows( 'comments', 'user_id', $user->ID, 'id, post_id, message, status, created_at', $page ); $items = array(); foreach ( $rows as $row ) { $items[] = self::item( 'spacebay-comments', 'comment-' . $row['id'], __( 'Community comments', 'spacebay' ), array( array( 'name' => __( 'Text', 'spacebay' ), 'value' => (string) $row['message'], ), array( 'name' => __( 'Status', 'spacebay' ), 'value' => $row['status'], ), array( 'name' => __( 'Date', 'spacebay' ), 'value' => $row['created_at'], ), array( 'name' => __( 'Post', 'spacebay' ), 'value' => Portal::link( 'posts/' . $row['post_id'] ), ), ) ); } return array( 'data' => $items, 'done' => count( $rows ) < self::PER_PAGE, ); } /** * Exports private messages the member sent, one page at a time. * * @param string $email Email address. * @param int $page Page, from 1. * @return array */ public static function export_messages( $email, $page = 1 ) { $user = get_user_by( 'email', $email ); if ( ! $user ) { return self::done( array() ); } $rows = self::rows( 'messages', 'user_id', $user->ID, 'id, conversation_id, type, message, status, created_at', $page ); $items = array(); foreach ( $rows as $row ) { if ( 'text' !== $row['type'] ) { continue; } $items[] = self::item( 'spacebay-messages', 'message-' . $row['id'], __( 'Community private messages', 'spacebay' ), array( array( 'name' => __( 'Text', 'spacebay' ), 'value' => (string) $row['message'], ), array( 'name' => __( 'Status', 'spacebay' ), 'value' => $row['status'], ), array( 'name' => __( 'Date', 'spacebay' ), 'value' => $row['created_at'], ), array( 'name' => __( 'Conversation', 'spacebay' ), 'value' => Portal::link( 'messages/' . $row['conversation_id'] ), ), ) ); } return array( 'data' => $items, 'done' => count( $rows ) < self::PER_PAGE, ); } /** * Exports likes and saved posts, one page at a time. * * @param string $email Email address. * @param int $page Page, from 1. * @return array */ public static function export_reactions( $email, $page = 1 ) { $user = get_user_by( 'email', $email ); if ( ! $user ) { return self::done( array() ); } $rows = self::rows( 'reactions', 'user_id', $user->ID, 'id, object_type, object_id, type, created_at', $page ); $items = array(); foreach ( $rows as $row ) { $items[] = self::item( 'spacebay-activity', 'reaction-' . $row['id'], __( 'Community likes and saved posts', 'spacebay' ), array( array( 'name' => __( 'Type', 'spacebay' ), 'value' => 'bookmark' === $row['type'] ? __( 'Saved', 'spacebay' ) : __( 'Liked', 'spacebay' ), ), array( 'name' => __( 'Item', 'spacebay' ), 'value' => $row['object_type'] . ' #' . $row['object_id'], ), array( 'name' => __( 'Date', 'spacebay' ), 'value' => $row['created_at'], ), ) ); } return array( 'data' => $items, 'done' => count( $rows ) < self::PER_PAGE, ); } /** * Exports notifications the member received, one page at a time. * * @param string $email Email address. * @param int $page Page, from 1. * @return array */ public static function export_notifications( $email, $page = 1 ) { $user = get_user_by( 'email', $email ); if ( ! $user ) { return self::done( array() ); } $rows = self::rows( 'notifications', 'user_id', $user->ID, 'id, user_id, actor_id, action, object_type, object_id, content, url, is_read, created_at', $page ); $items = array(); foreach ( $rows as $row ) { $note = Notifications::format( $row ); $items[] = self::item( 'spacebay-notifications', 'notification-' . $row['id'], __( 'Community notifications', 'spacebay' ), array( array( 'name' => __( 'Notification', 'spacebay' ), 'value' => $note['text'], ), array( 'name' => __( 'Date', 'spacebay' ), 'value' => $row['created_at'], ), ) ); } return array( 'data' => $items, 'done' => count( $rows ) < self::PER_PAGE, ); } /** * Exports a member's points total, level and badges. * * @param string $email Email address. * @param int $page Page, from 1. * @return array */ public static function export_achievements( $email, $page = 1 ) { unset( $page ); $user = get_user_by( 'email', $email ); if ( ! $user ) { return self::done( array() ); } $points = \SpaceBay\Gamification\Points::total( $user->ID ); $items = array( self::item( 'spacebay-achievements', 'points', __( 'Community points and badges', 'spacebay' ), array( array( 'name' => __( 'Points', 'spacebay' ), 'value' => (string) $points, ), array( 'name' => __( 'Level', 'spacebay' ), 'value' => \SpaceBay\Gamification\Levels::for_points( $points )['name'], ), ) ), ); foreach ( \SpaceBay\Gamification\Badges::of_user( $user->ID ) as $badge ) { $items[] = self::item( 'spacebay-achievements', 'badge-' . $badge['key'], __( 'Community points and badges', 'spacebay' ), array( array( 'name' => __( 'Badge', 'spacebay' ), 'value' => $badge['name'], ), array( 'name' => __( 'Date', 'spacebay' ), 'value' => (string) $badge['awarded_at'], ), ) ); } return self::done( $items ); } /** * Exports a member's answers to events, one page at a time. * * @param string $email Email address. * @param int $page Page, from 1. * @return array */ public static function export_events( $email, $page = 1 ) { $user = get_user_by( 'email', $email ); if ( ! $user ) { return self::done( array() ); } $rows = self::rows( 'event_rsvps', 'user_id', $user->ID, 'id, event_id, status, created_at', $page ); $items = array(); foreach ( $rows as $row ) { $event = \SpaceBay\Events\Events::find( (int) $row['event_id'] ); $items[] = self::item( 'spacebay-events', 'rsvp-' . $row['id'], __( 'Community event answers', 'spacebay' ), array( array( 'name' => __( 'Event', 'spacebay' ), 'value' => $event ? $event['title'] : '#' . $row['event_id'], ), array( 'name' => __( 'Answer', 'spacebay' ), 'value' => $row['status'], ), array( 'name' => __( 'Date', 'spacebay' ), 'value' => $row['created_at'], ), ) ); } return array( 'data' => $items, 'done' => count( $rows ) < self::PER_PAGE, ); } /** * Exports the events a member organized. * * @param string $email Email address. * @param int $page Page, from 1. * @return array */ public static function export_own_events( $email, $page = 1 ) { $user = get_user_by( 'email', $email ); if ( ! $user ) { return self::done( array() ); } $rows = self::rows( 'events', 'user_id', $user->ID, '*', $page ); $items = array(); foreach ( $rows as $row ) { $items[] = self::item( 'spacebay-own-events', 'event-' . $row['id'], __( 'Community events organized', 'spacebay' ), array( array( 'name' => __( 'Title', 'spacebay' ), 'value' => $row['title'], ), array( 'name' => __( 'Description', 'spacebay' ), 'value' => $row['description'], ), array( 'name' => __( 'Place', 'spacebay' ), 'value' => 'online' === $row['location_type'] ? $row['online_url'] : $row['location'], ), array( 'name' => __( 'Starts (UTC)', 'spacebay' ), 'value' => $row['starts_at'], ), array( 'name' => __( 'Ends (UTC)', 'spacebay' ), 'value' => $row['ends_at'], ), array( 'name' => __( 'Status', 'spacebay' ), 'value' => $row['status'], ), ) ); } return array( 'data' => $items, 'done' => count( $rows ) < self::PER_PAGE, ); } /** * Erases a member's community data in batches: posts, then comments, * then everything else. * * @param string $email Email address. * @param int $page Page, from 1. * @return array */ public static function erase( $email, $page = 1 ) { unset( $page ); $user = get_user_by( 'email', $email ); $result = array( 'items_removed' => false, 'items_retained' => false, 'messages' => array(), 'done' => true, ); if ( ! $user ) { return $result; } $post_ids = wp_list_pluck( self::rows( 'posts', 'user_id', $user->ID, 'id', 1, 20 ), 'id' ); if ( $post_ids ) { Posts::delete( array_map( 'intval', $post_ids ) ); $result['items_removed'] = true; $result['done'] = false; return $result; } $comment_ids = wp_list_pluck( self::rows( 'comments', 'user_id', $user->ID, 'id', 1, 50 ), 'id' ); if ( $comment_ids ) { foreach ( $comment_ids as $comment_id ) { $comment = Comments::find( (int) $comment_id ); if ( $comment ) { Comments::delete( $comment, true ); } } $result['items_removed'] = true; $result['done'] = false; return $result; } \SpaceBay\Messages\Messages::delete_for_user( $user->ID ); delete_user_option( $user->ID, \SpaceBay\Messages\Rules::BLOCKS ); global $wpdb; // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Custom tables, one-time erasure. $wpdb->delete( Tables::name( 'reactions' ), array( 'user_id' => $user->ID ) ); $wpdb->delete( Tables::name( 'notifications' ), array( 'user_id' => $user->ID ) ); $wpdb->delete( Tables::name( 'notifications' ), array( 'actor_id' => $user->ID ) ); // phpcs:enable Reports::delete_for_user( $user->ID ); Media::delete_for_user( $user->ID ); \SpaceBay\Gamification\Gamification::delete_user_data( $user->ID ); \SpaceBay\Events\Events::delete_user_data( $user->ID ); Profiles::delete_user_data( $user->ID ); delete_user_option( $user->ID, Preferences::META ); delete_user_meta( $user->ID, Preferences::META ); Cache::flush( 'profiles' ); $result['items_removed'] = true; $result['messages'][] = __( 'Community posts, comments, private messages, likes, images, profile details, memberships, notifications, reports, points, badges, events and event answers were removed.', 'spacebay' ); return $result; } /** * Suggests text for the site's privacy policy. * * @return void */ public static function policy_text() { if ( ! function_exists( 'wp_add_privacy_policy_content' ) ) { return; } $text = '

' . esc_html__( 'Suggested text for your community. Change it to match how you run the community.', 'spacebay' ) . '

' . '

' . esc_html__( 'Suggested text:', 'spacebay' ) . ' ' . esc_html__( 'When you join our community we store your profile details (such as your name, photo, cover image, headline, about text, location and website), the spaces you join, and the posts, comments, private messages, likes, saved posts, images and reports you create. Private messages, and the images in them, can be read by the people in the conversation, and by our moderators only if someone reports them; people added to a group chat later see only what is written after they join. Images in spaces that are not public are only shown to people who may read the space. We remove hidden photo details, such as the location, from the images you upload. We store when you were last active, your notifications and your email notification choices. Other members can see your profile unless you hide it, and the posts and comments you write in spaces they can read. When a post contains a link, our site may visit that page once to show a preview. You can export or delete your community data by contacting us.', 'spacebay' ) . '

'; wp_add_privacy_policy_content( 'SpaceBay', wp_kses_post( $text ) ); } /** * Rows of one table for one user, oldest first. * * @param string $table Short table name. * @param string $column User column. * @param int $user_id User ID. * @param string $columns Columns the caller uses (all columns are read). * @param int $page Page, from 1. * @param int $per_page Rows per page. * @return array[] */ private static function rows( $table, $column, $user_id, $columns, $page, $per_page = self::PER_PAGE ) { global $wpdb; $offset = ( max( 1, (int) $page ) - 1 ) * $per_page; unset( $columns ); return (array) $wpdb->get_results( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Privacy export, paged. $wpdb->prepare( 'SELECT * FROM %i WHERE %i = %d ORDER BY id ASC LIMIT %d OFFSET %d', Tables::name( $table ), $column, absint( $user_id ), $per_page, $offset ), ARRAY_A ); } /** * One export item. * * @param string $group_id Group ID. * @param string $item_id Item ID. * @param string $group_label Group label. * @param array $data Name/value pairs. * @return array */ private static function item( $group_id, $item_id, $group_label, array $data ) { return array( 'group_id' => $group_id, 'group_label' => $group_label, 'item_id' => $item_id, 'data' => $data, ); } /** * A finished export page. * * @param array $items Items. * @return array */ private static function done( array $items ) { return array( 'data' => $items, 'done' => true, ); } }