$tab_contents ) { $tab_sections = edd_get_settings_tab_sections( $tab_key ); foreach ( $tab_sections as $section_key => $section_title ) { $section_setting_types = edd_get_registered_settings_types( $tab_key, $section_key ); if ( ! empty( $settings[ $tab_key ] ) && ! empty( $settings[ $tab_key ][ $section_key ] ) ) { $section_settings = $settings[ $tab_key ][ $section_key ]; foreach ( $section_settings as $setting_key => $setting ) { $value = $this->get_setting_value( $tab_key, $section_key, $setting_key ); // If the value is null, it's a skipped setting. if ( is_null( $value ) ) { continue; } $setting_id = isset( $setting['id'] ) ? $setting['id'] : sanitize_title( $setting['name'] ); if ( is_array( $value ) ) { foreach ( $value as $v ) { $data[ "{$setting_id}_{$v}" ] = 1; } } else { $data[ $setting_id ] = $value; } } } } } return $data; } /** * Gets the id and value for an individual setting. * * @param string $tab_key * @param string $section_key * @param string $setting_key * @return mixed */ private function get_setting_value( $tab_key, $section_key, $setting_key ) { $setting = edd_get_registered_setting_details( $tab_key, $section_key, $setting_key ); if ( ! $this->can_include_setting( $setting ) ) { return null; } $default = isset( $setting['std'] ) ? $setting['std'] : ''; $value = edd_get_option( $setting['id'], $default ); if ( in_array( $setting['type'], array( 'checkbox', 'checkbox_description' ), true ) ) { return (int) (bool) $value; } if ( empty( $value ) && 'currency' === $setting['id'] ) { return edd_get_currency(); } if ( in_array( $setting['type'], $this->text_settings(), true ) ) { return $this->anonymize_setting( $value ); } if ( $this->should_populate_array( $setting ) ) { return $this->update_setting_value_array( $value, $setting ); } return $value; } /** * Evaluates whether a setting can be included in the telemetry data. * * @since 3.1.1 * @param array $setting * @return bool */ private function can_include_setting( $setting ) { if ( empty( $setting ) ) { return false; } // If the setting is marked readonly then it's not really a setting. if ( ! empty( $setting['args']['readonly'] ) ) { return false; } // Certain types of settings should always be skipped. if ( in_array( $setting['type'], $this->skipped_settings_types(), true ) ) { return false; } // Settings known to be PII are excluded. if ( in_array( $setting['id'], $this->sensitive_settings(), true ) ) { return false; } // Text settings are always excluded unless specifically included. if ( in_array( $setting['type'], $this->text_settings(), true ) && ! in_array( $setting['id'], $this->allowed_text_settings(), true ) ) { return false; } return true; } /** * These settings types are either not settings or nearly always full of sensitive data/PII. * * @since 3.1.1 * @return array */ private function skipped_settings_types() { return array_merge( edd_get_non_setting_types(), array( 'rich_editor', 'upload', 'color', 'recapture', ) ); } /** * These settings are known to be sensitive/PII and are not otherwise excluded. * * @since 3.1.1 * @return array */ private function sensitive_settings() { return array( 'base_state', 'paypal_live_client_id', 'paypal_live_client_secret', 'paypal_sandbox_client_id', 'paypal_sandbox_client_secret', ); } /** * We assume that any text field should be excluded unless it's in this array. * * @since 3.1.1 * @return array */ private function allowed_text_settings() { return array(); } /** * Settings types which will be strings and which should be evaluated for PII. * * @since 3.1.1 * @return array */ private function text_settings() { return array( 'text', 'textarea', 'email', ); } /** * Whether an array of settings should be populated, due to the setting type. * * @since 3.1.1 * @param array $setting * @return bool */ private function should_populate_array( $setting ) { $settings = array( 'gateways', 'accepted_cards' ); return 'multicheck' === $setting['type'] || in_array( $setting['id'], $settings, true ); } /** * Attempts to anonymize a setting value. * @todo check how we want to replace--values or empty strings? * * @since 3.1.1 * @param string $value * @return string */ private function anonymize_setting( $value ) { $admin_email = get_bloginfo( 'admin_email' ); $value = str_replace( $admin_email, 'email@website.dev', $value ); $site_name = get_bloginfo( 'name' ); $value = str_replace( $site_name, 'Site Name', $value ); $home_url = get_home_url(); $value = str_replace( $home_url, 'website.dev', $value ); return $value; } /** * Updates the an array setting value to include all options. * * @since 3.1.1 * @param mixed $saved_value The actual saved value (can be empty). * @param array $setting The setting definition. * @return array */ private function update_setting_value_array( $saved_value, $setting ) { $value = array(); foreach ( $setting['options'] as $key => $label ) { if ( is_array( $saved_value ) && ! empty( $saved_value[ $key ] ) ) { $value[] = $key; } } return $value; } }