Server : Apache System : Linux pod-100823:apache2_74:v0.5.7 5.4.0-1138-gcp #147~18.04.1-Ubuntu SMP Mon Oct 7 21:46:26 UTC 2024 x86_64 User : www-data ( 33) PHP Version : 7.4.33.7 Disable Function : apache_child_terminate,apache_get_modules,apache_get_version,apache_getenv,apache_note,apache_setenv,disk_free_space,disk_total_space,diskfreespace,dl,exec,fastcgi_finish_request,link,opcache_compile_file,opcache_get_configuration,opcache_invalidate,opcache_is_script_cached,opcache_reset,passthru,pclose,pcntl_exec,popen,posix_getpid,posix_getppid,posix_getpwuid,posix_kill,posix_mkfifo,posix_setegid,posix_seteuid,posix_setgid,posix_setpgid,posix_setsid,posix_setuid,posix_uname,proc_close,proc_get_status,proc_nice,proc_open,proc_terminate,realpath_cache_get,shell_exec,show_source,symlink,system Directory : /nas/content/live/attorneyexperi/wp-content/plugins/gravityforms/includes/ |
<?php use Gravity_Forms\Gravity_Forms\Settings\Settings; class_exists( 'GFForms' ) || die(); /** * Handles Integration with the WordPress personal data export and erase tools. * * @since 2.4 * * Class GF_Personal_Data */ class GF_Personal_Data { /** * The cached form array. * * @since 2.4 * * @var array $form */ private static $_form; /** * The cached array of forms. * * @since 2.4 * * @var array $forms */ private static $_forms; /** * Stores the current instance of the Settings renderer. * * @since 2.5 * * @var false|Settings */ private static $_settings_renderer = false; /** * Renders the form settings. * * @since 2.4 * * @param $form_id */ public static function form_settings( $form_id ) { if ( ! self::get_settings_renderer() ) { self::initialize_settings_renderer(); } self::get_settings_renderer()->render(); } /** * Get Personal Data settings fields. * * @since 2.5 * * @param int $form_id The current Form ID. * * @return array */ private static function settings_fields( $form_id ) { // Get form object. $form = self::get_form( $form_id ); // Get Identification Field choices. $identification_field_choices = self::get_identification_fields_choices( $form ); return array( array( 'class' => 'gform-settings-panel--full', 'title' => esc_html__( 'General Settings', 'gravityforms' ), 'fields' => array( array( 'name' => 'preventIP', 'type' => 'toggle', 'label' => esc_html__( 'Prevent the storage of IP addresses during form submission', 'gravityforms' ), 'tooltip' => gform_tooltip( 'personal_data_prevent_ip', null, true ), ), array( 'name' => 'retention[policy]', 'type' => 'radio', 'label' => esc_html__( 'Retention Policy', 'gravityforms' ), 'tooltip' => gform_tooltip( 'personal_data_retention_policy', null, true ), 'default_value' => 'retain', 'choices' => array( array( 'label' => esc_html__( 'Retain entries indefinitely', 'gravityforms' ), 'value' => 'retain', ), array( 'label' => esc_html__( 'Trash entries automatically', 'gravityforms' ), 'value' => 'trash', 'onclick' => sprintf( 'alert( %s );', json_encode( __( 'Warning: this will affect all entries that are older than the number of days specified.', 'gravityforms' ) ) ), ), array( 'label' => esc_html__( 'Delete entries permanently automatically', 'gravityforms' ), 'value' => 'delete', 'onclick' => sprintf( 'alert( %s );', json_encode( __( 'Warning: this will affect all entries that are older than the number of days specified.', 'gravityforms' ) ) ), ), ), ), array( 'name' => 'retention[retain_entries_days]', 'label' => esc_html__( 'Number of days to retain entries before trashing/deleting:', 'gravityforms' ), 'type' => 'text', 'input_type' => 'number', 'default_value' => 1, 'dependency' => array( 'live' => true, 'fields' => array( array( 'field' => 'retention[policy]', 'values' => array( 'trash', 'delete' ), ), ), ), 'validation_callback' => function( $field, $value ) { // If value is not numeric or less than one day, set error. if ( ! is_numeric( $value ) || ( is_numeric( $value ) && floatval( $value ) < 1 ) ) { $field->set_error( esc_html__( 'Form entries must be retained for at least one day.', 'gravityforms' ) ); } }, ), ), ), array( 'class' => 'gform-settings-panel--full', 'title' => esc_html__( 'Exporting and Erasing Data', 'gravityforms' ), 'fields' => array( array( 'name' => 'exportingAndErasing[enabled]', 'type' => 'toggle', 'label' => esc_html__( 'Enable integration with the WordPress tools for exporting and erasing personal data.', 'gravityforms' ), 'tooltip' => gform_tooltip( 'personal_data_enable', null, true ), 'disabled' => empty( $identification_field_choices ), 'after_input' => ! empty( $identification_field_choices ) ? '' : sprintf( '<div class="notice-error gf-notice alert error">%s</div>', esc_html__( 'You must add an email address field to the form in order to enable this setting.', 'gravityforms' ) ), ), array( 'name' => 'exportingAndErasing[identificationField]', 'type' => 'select', 'label' => esc_html__( 'Identification Field', 'gravityforms' ), 'tooltip' => gform_tooltip( 'personal_data_identification', null, true ), 'choices' => $identification_field_choices, 'dependency' => array( 'live' => true, 'fields' => array( array( 'field' => 'exportingAndErasing[enabled]', ), ), ), ), array( 'name' => 'exportingAndErasing[columns]', 'type' => 'columns', 'label' => esc_html__( 'Personal Data', 'gravityforms' ), 'tooltip' => gform_tooltip( 'personal_data_field_settings', null, true ), 'callback' => array( 'GF_Personal_Data', 'settings_columns' ), 'dependency' => array( 'live' => true, 'fields' => array( array( 'field' => 'exportingAndErasing[enabled]', ), ), ), ), ), ), ); } /** * Get identification fields as choices. * * @since 2.5 * * @param array $form Form object. * * @return array */ private static function get_identification_fields_choices( $form = array() ) { static $choices; // If choices have already been defined, return. if ( isset( $choices ) ) { return $choices; } // Initialize choices. $choices = array(); // Get Email fields. $email_fields = GFAPI::get_fields_by_type( $form, 'email' ); // Add Email fields as choices. foreach ( $email_fields as $email_field ) { $choices[ (string) $email_field->id ] = $email_field->label; } /** * Allows the list of personal data identification field choices to be modified. Fields values * will be treated as user IDs. * * For example, add the created_by field by returning: * $identification_field_choices['created_by'] = 'Created By'; * * @since 2.4 * * @param array $identification_field_choices An associative array with the field id as the key and the value as the label. * @param array $form The current form. */ $choices = gf_apply_filters( array( 'gform_personal_data_identification_fields', $form['id'], ), $choices, $form ); // Update choices formatting. array_walk( $choices, function( &$label, $value ) { $label = array( 'label' => $label, 'value' => $value ); } ); // If choices exist, return. if ( ! empty( $choices ) ) { return $choices; } // Get current identification field value. $current_value = rgars( $form, 'personalData/exportingAndErasing/identificationField' ); // Add look up choice. if ( $current_value === 'created_by' ) { $choices[] = array( 'label' => esc_html__( 'Created By', 'gravityforms' ), 'value' => 'created_by', ); } else if ( $selected_field = GFAPI::get_field( $form, $current_value ) ) { // Set admin label context. $selected_field->set_context_property( 'use_admin_label', true ); $choices[] = array( 'label' => GFFormsModel::get_label( $selected_field ), 'value' => $current_value, ); } return $choices; } /** * Renders a Personal Data columns table field. * * @since 2.5 * * @param array $props Field properties. * @param bool $echo Output the field markup directly. * * @return string */ public static function settings_columns( $props = array(), $echo = true ) { // Get form object. $form_id = absint( rgget( 'id' ) ); $form = self::get_form( $form_id ); // Open table. $html = sprintf( '<table id="gf_personal_data_field_settings" class="form-table"> <thead> <tr> <th class="gf_personal_data_field_label_title">%s</th> <th class="gf_personal_data_cb_title">%s</th> <th class="gf_personal_data_cb_title">%s</th> </tr> </thead> <tbody>', esc_html__( 'Fields', 'gravityforms' ), esc_html__( 'Export', 'gravityforms' ), esc_html__( 'Erase', 'gravityforms' ) ); // Add Select/Deselect All row. $html .= sprintf( '<tr> <td>%s</td> <td class="gf_personal_data_cb_cell"> <div class="gform-settings-choice"> <input id="gf_personal_data_export_all" type="checkbox" /> </div> </td> <td class="gf_personal_data_cb_cell"> <div class="gform-settings-choice"> <input id="gf_personal_data_erase_all" type="checkbox" /> </div> </td> </tr>', esc_html__( 'Select/Deselect All', 'gravityforms' ) ); // Initialize Personal Data fields array. $pd_fields = array(); // Loop through columns, add to Personal Data fields array. foreach ( self::get_columns() as $key => $label ) { $column_settings = rgars( $form, 'personalData/exportingAndErasing/columns/' . $key ); $pd_fields[] = array( 'key' => $key, 'label' => $label, 'default_values' => array( 'export' => rgar( $column_settings, 'export' ), 'erase' => rgar( $column_settings, 'erase' ), ), ); } /** * Loop through form fields, add to Personal Data fields array. * * @var GF_Field $field */ foreach ( $form['fields'] as $field ) { // Skip display only fields. if ( $field->displayOnly ) { continue; } // Set label context. $field->set_context_property( 'use_admin_label', true ); // Add to Personal Data fields. $pd_fields[] = array( 'key' => absint( $field->id ), 'label' => GFFormsModel::get_label( $field ), 'default_values' => array( 'export' => $field->personalDataExport, 'erase' => $field->personalDataErase, ), ); } // Render Personal Data fields. foreach ( $pd_fields as $pd_field ) { $export_field = $erase_field = null; // Prepare export checkbox. $export_field = \Gravity_Forms\Gravity_Forms\Settings\Fields::create( array( 'name' => sprintf( 'export_fields[%s]', esc_attr( $pd_field['key'] ) ), 'type' => 'checkbox', 'choices' => array( array( 'class' => 'gf_personal_data_cb_export', 'name' => sprintf( 'export_fields[%s]', esc_attr( $pd_field['key'] ) ), 'default_value' => $pd_field['default_values']['export'], ), ), ), self::get_settings_renderer() ); // Prepare erase checkbox. $erase_field = \Gravity_Forms\Gravity_Forms\Settings\Fields::create( array( 'name' => sprintf( 'erase_fields[%s]', esc_attr( $pd_field['key'] ) ), 'type' => 'checkbox', 'choices' => array( array( 'class' => 'gf_personal_data_cb_erase', 'name' => sprintf( 'erase_fields[%s]', esc_attr( $pd_field['key'] ) ), 'default_value' => $pd_field['default_values']['erase'], ), ), ), self::get_settings_renderer() ); // Render field. $html .= sprintf( '<tr> <td>%s</td> <td class="gf_personal_data_cb_cell">%s</td> <td class="gf_personal_data_cb_cell">%s</td> </tr>', esc_html( $pd_field['label'] ), $export_field->markup(), $erase_field->markup() ); } // Get custom items. $custom_items = self::get_custom_items( $form ); // Display custom items. if ( ! empty( $custom_items ) ) { // Add Other Data heading. $html .= sprintf( '<tr><th class="gf_personal_data_field_label_title" colspan="3">%s</th></tr>', esc_html__( 'Other Data', 'gravityforms' ) ); // Loop through custom items, render. foreach ( $custom_items as $key => $custom_item_details ) { $export_field = $erase_field = null; // Get custom items settings. $custom_settings = rgars( $form, 'personalData/exportingAndErasing/custom/' . $key ); // Prepare export checkbox. if ( isset( $custom_item_details['exporter_callback'] ) && is_callable( $custom_item_details['exporter_callback'] ) ) { $export_field = \Gravity_Forms\Gravity_Forms\Settings\Fields::create( array( 'name' => sprintf( 'export_fields[%s]', esc_attr( $key ) ), 'type' => 'checkbox', 'choices' => array( array( 'class' => 'gf_personal_data_cb_export', 'name' => sprintf( 'export_fields[%s]', esc_attr( $key ) ), 'default_value' => rgar( $custom_settings, 'export' ), ), ), ), self::get_settings_renderer() ); } // Prepare erase checkbox. if ( isset( $custom_item_details['eraser_callback'] ) && is_callable( $custom_item_details['eraser_callback'] ) ) { $erase_field = \Gravity_Forms\Gravity_Forms\Settings\Fields::create( array( 'name' => sprintf( 'erase_fields[%s]', esc_attr( $key ) ), 'type' => 'checkbox', 'choices' => array( array( 'class' => 'gf_personal_data_cb_erase', 'name' => sprintf( 'erase_fields[%s]', esc_attr( $key ) ), 'default_value' => rgar( $custom_settings, 'erase' ), ), ), ), self::get_settings_renderer() ); } // Render field. $html .= sprintf( '<tr> <td>%s</td> <td class="gf_personal_data_cb_cell">%s</td> <td class="gf_personal_data_cb_cell">%s</td> </tr>', esc_html( rgar( $custom_item_details, 'label' ) ), $export_field ? $export_field->markup() : null, $erase_field ? $erase_field->markup() : null ); } } // Close table. $html .= '</tbody></table>'; return $html; } /** * Saves the form settings. * * @since 2.4 * * @param array $values Submitted settings values. */ public static function process_form_settings( $values ) { // Get form object. $form = self::get_form( rgget( 'form_id' ) ); // Prevent IP address storage. $form['personalData']['preventIP'] = (bool) rgar( $values, 'preventIP' ); // Retention Policy $form['personalData']['retention'] = rgar( $values, 'retention' ); // Exporting and Erasing $form['personalData']['exportingAndErasing']['enabled'] = (bool) rgars( $values, 'exportingAndErasing/enabled' ); $form['personalData']['exportingAndErasing']['identificationField'] = sanitize_key( rgars( $values, 'exportingAndErasing/identificationField' ) ); // Exporting and Erasing: Columns foreach ( self::get_columns() as $column => $label ) { $form['personalData']['exportingAndErasing']['columns'][ $column ] = array( 'export' => (bool) rgars( $values, 'export_fields/' . $column ), 'erase' => (bool) rgars( $values, 'erase_fields/' . $column ), ); } /** * Exporting and Erasing: Fields * * @var GF_Field $field */ foreach ( $form['fields'] as $f => $field ) { $form['fields'][ $f ]->personalDataExport = (bool) rgars( $values, 'export_fields/' . absint( $field->id ) ); $form['fields'][ $f ]->personalDataErase = (bool) rgars( $values, 'erase_fields/' . absint( $field->id ) ); } // Exporting and Erasing: Custom Items $custom_items = self::get_custom_items( $form ); if ( ! empty( $custom_items ) ) { foreach ( $custom_items as $custom_item => $custom_item_meta ) { $form['personalData']['exportingAndErasing']['custom'][ $custom_item ] = array( 'export' => (bool) rgars( $values, 'export_fields/' . $custom_item ), 'erase' => (bool) rgars( $values, 'erase_fields/' . $custom_item ), ); } } // Save form. GFAPI::update_form( $form ); // Update cached form object. self::$_form = $form; } // # SETTINGS RENDERER --------------------------------------------------------------------------------------------- /** * Initializes the Settings renderer at the beginning of page load. */ public static function initialize_settings_renderer() { // Get form object. $form_id = absint( rgget( 'id' ) ); $form = self::get_form( $form_id ); $renderer = new Settings( array( 'header' => array( 'icon' => 'fa fa-lock', 'title' => esc_html__( 'Personal Data', 'gravityforms' ), ), 'fields' => self::settings_fields( $form_id ), 'initial_values' => rgar( $form, 'personalData' ), 'save_callback' => array( 'GF_Personal_Data', 'process_form_settings' ), 'after_fields' => function() { ?> <script> jQuery( document ).ready( function ( $ ) { $( '#gf_personal_data_export_all' ).change( function () { var checked = $( this ).is( ':checked' ); $( '.gf_personal_data_cb_export' ).each( function() { if ( ( ! $( this ).is( ':checked' ) && checked ) || ( $( this ).is( ':checked' ) && ! checked ) ) { $( this ).trigger( 'click' ); } } ); } ); $( '#gf_personal_data_erase_all' ).change( function () { var checked = $( this ).is( ':checked' ); $( '.gf_personal_data_cb_erase' ).each( function() { if ( ( ! $( this ).is( ':checked' ) && checked ) || ( $( this ).is( ':checked' ) && ! checked ) ) { $( this ).trigger( 'click' ); } } ); } ); } ); </script> <?php } ) ); self::set_settings_renderer( $renderer ); // Process save callback. if ( self::get_settings_renderer()->is_save_postback() ) { self::get_settings_renderer()->process_postback(); } } /** * Gets the current instance of Settings handling settings rendering. * * @since 2.5 * * @return false|\Gravity_Forms\Gravity_Forms\Settings */ private static function get_settings_renderer() { return self::$_settings_renderer; } /** * Sets the current instance of Settings handling settings rendering. * * @since 2.5 * * @param \Gravity_Forms\Gravity_Forms\Settings\Settings $renderer Settings renderer. * * @return bool|WP_Error */ private static function set_settings_renderer( $renderer ) { // Ensure renderer is an instance of Settings if ( ! is_a( $renderer, 'Gravity_Forms\Gravity_Forms\Settings\Settings' ) ) { return new WP_Error( 'Renderer must be an instance of Gravity_Forms\Gravity_Forms\Settings\Settings.' ); } self::$_settings_renderer = $renderer; return true; } /** * Returns the form array for use in the form settings. * * @since 2.4 * * @param int $form_id * * @return array|mixed */ public static function get_form( $form_id ) { if ( empty( self::$_form ) ) { self::$_form = GFAPI::get_form( $form_id ); } return self::$_form; } /** * Returns an assoiative array of the database columns that may contain personal data. * * @since 2.4 * * @return array */ public static function get_columns() { $columns = array( 'ip' => esc_html__( 'IP Address', 'gravityforms' ), 'source_url' => esc_html__( 'Embed URL', 'gravityforms' ), 'user_agent' => esc_html__( 'Browser details', 'gravityforms' ), ); return $columns; } /** * Returns an array with the custom personal data items configurations. * * @since 2.4 * * @param array $form * * @return array */ public static function get_custom_items( $form ) { $custom_items = array(); /** * Allows custom exporter and erasers to be registered. * * Example: * * add_filter( 'gform_personal_data', 'filter_gform_personal_data', 10, 2 ); * function filter_gform_personal_data( $items, $form ) { * $items['test'] = array( * 'label' => 'A custom item', * 'exporter_callback' => 'gf_custom_data_exporter', * 'eraser_callback' => 'gf_custom_data_eraser', * ); * * return $items; * } * * function gf_custom_data_exporter( $form, $entry ) { * $data = array( * 'name' => 'My Custom Value', * 'value' => 'ABC123', * ); * return $data; * } * * function gf_custom_data_eraser( $form, $entry ) { * // Delete or anonymize some data * } * * @since 2.4 * * @param array $custom_items * @param array $form */ $custom_items = apply_filters( 'gform_personal_data', $custom_items, $form ); return $custom_items; } /** * Returns an associative array of all the form metas with the form ID as the key. * * @since 2.4 * * @return array|null */ public static function get_forms() { if ( is_null( self::$_forms ) ) { $form_ids = GFFormsModel::get_form_ids( null ); if ( empty( $form_ids ) ) { return array( 'data' => array(), 'done' => true, ); } $forms_by_id = GFFormsModel::get_form_meta_by_id( $form_ids ); self::$_forms = array(); foreach ( $forms_by_id as $form ) { self::$_forms[ $form['id'] ] = $form; } } return self::$_forms; } /** * Returns all the entries across all forms for the specified email address. * * @since 2.4 * * @param string $email_address * @param int $page * @param int $limit * * @return array */ public static function get_entries( $email_address, $page = 1, $limit = 50 ) { $user = get_user_by( 'email', $email_address ); $forms = self::get_forms(); $form_ids = array(); $query = new GF_Query(); $conditions = array(); foreach ( $forms as $form ) { if ( ! rgars( $form, 'personalData/exportingAndErasing/enabled' ) ) { continue; } $form_ids[] = $form['id']; $identification_field = rgars( $form, 'personalData/exportingAndErasing/identificationField' ); $field = GFAPI::get_field( $form, $identification_field ); if ( $field && $field->get_input_type() == 'email' ) { $conditions[] = new GF_Query_Condition( new GF_Query_Column( $identification_field, $form['id'] ), GF_Query_Condition::EQ, new GF_Query_Literal( $email_address ) ); } else { if ( ! $field && $identification_field != 'created_by' ) { continue; } if ( ! $user ) { continue; } $conditions[] = new GF_Query_Condition( new GF_Query_Column( $identification_field, $form['id'] ), GF_Query_Condition::EQ, new GF_Query_Literal( $user->ID ) ); } } if ( empty( $conditions ) ) { return array(); } $all_conditions = call_user_func_array( array( 'GF_Query_Condition', '_or' ), $conditions ); $entries = $query->from( $form_ids )->where( $all_conditions )->limit( $limit )->page( $page )->get(); return $entries; } /** * Exports personal data specified in the form settings. * * @since 2.4 * * @param string $email_address * @param int $page * * @return array */ public static function data_exporter( $email_address, $page = 1 ) { $export_items = array( 'done' => true, ); $export_data = array(); if ( $page == 1 ) { $export_data = self::get_draft_submissions_export_items( $email_address ); } $export_items['data'] = $export_data; $limit = 50; $columns = self::get_columns(); $forms = self::get_forms(); $entries = self::get_entries( $email_address, $page, $limit ); if ( empty( $entries ) ) { return $export_items; } foreach ( $entries as $entry ) { $data = array(); $form_id = $entry['form_id']; $form = $forms[ $form_id ]; $item_id = "gf-entry-{$entry['id']}"; $group_id = 'gravityforms-entries'; $group_label = __( 'Forms', 'gravityforms' ); $columns_settings = rgars( $forms, $form_id . '/personalData/exportingAndErasing/columns' ); if ( is_array( $columns_settings ) ) { foreach ( $columns_settings as $column_key => $column_settings ) { if ( rgar( $column_settings, 'export' ) ) { $data[] = array( 'name' => $columns[ $column_key ], 'value' => $entry[ $column_key ], ); } } } foreach ( $form['fields'] as $field ) { /* @var GF_Field $field */ if ( $field->personalDataExport ) { $value = GFFormsModel::get_lead_field_value( $entry, $field ); $data[] = array( 'name' => $field->get_field_label( false, $value ), 'value' => $field->get_value_entry_detail( $value, rgar( $entry, 'currency' ), true, 'text' ), ); } } $custom_items = self::get_custom_items( $form ); if ( ! empty( $custom_items ) ) { $all_custom_settings = rgars( $forms, $form_id . '/personalData/exportingAndErasing/custom' ); foreach ( $custom_items as $custom_item_key => $custom_item_details ) { $custom_settings = rgar( $all_custom_settings, $custom_item_key ); if ( rgars( $custom_settings, 'export' ) && isset( $custom_item_details['exporter_callback'] ) && is_callable( $custom_item_details['exporter_callback'] ) ) { $data[] = call_user_func( $custom_item_details['exporter_callback'], $form, $entry ); } } } if ( ! empty( $data ) ) { $export_data[] = array( 'group_id' => $group_id, 'group_label' => $group_label, 'item_id' => $item_id, 'data' => $data, ); } } $done = count( $entries ) < $limit; $export_items = array( 'data' => $export_data, 'done' => $done, ); return $export_items; } /** * Returns the export items for draft submissions. * * @since 2.4 * * @param $email_address * * @return array */ public static function get_draft_submissions_export_items( $email_address ) { $export_items = array(); $forms = self::get_forms(); $columns = self::get_columns(); $draft_submissions = self::get_draft_submissions( $email_address ); foreach ( $draft_submissions as $i => $draft_submission ) { $data = array(); $form_id = $draft_submission['form_id']; $form = $forms[ $form_id ]; $submission_json = $draft_submission['submission']; $submission = json_decode( $submission_json, true ); $entry = $submission['partial_entry']; $item_id = "gf-draft-submission-{$i}"; $group_id = 'gravityforms-draft-submissions'; $group_label = __( 'Draft Forms (Save and Continue Later)', 'gravityforms' ); $columns_settings = rgars( $forms, $form_id . '/personalData/exportingAndErasing/columns' ); if ( is_array( $columns_settings ) ) { foreach ( $columns_settings as $column_key => $column_settings ) { if ( rgar( $column_settings, 'export' ) && isset( $draft_submission[ $column_key ] ) ) { $data[] = array( 'name' => $columns[ $column_key ], 'value' => $draft_submission[ $column_key ], ); } } } foreach ( $form['fields'] as $field ) { /* @var GF_Field $field */ if ( $field->personalDataExport ) { $value = GFFormsModel::get_lead_field_value( $entry, $field ); $data[] = array( 'name' => $field->get_field_label( false, $value ), 'value' => $field->get_value_entry_detail( $value, rgar( $entry, 'currency' ), true, 'text' ), ); } } if ( ! empty( $data ) ) { $export_items[] = array( 'group_id' => $group_id, 'group_label' => $group_label, 'item_id' => $item_id, 'data' => $data, ); } } return $export_items; } /** * Erases personal data specified in the form settings. * * @since 2.4 * * @param string $email_address * @param int $page * * @return array */ public static function data_eraser( $email_address, $page = 1 ) { $limit = 50; $items_removed = $page == 1 ? self::erase_draft_submissions_data( $email_address ) : false; $forms = self::get_forms(); $entries = self::get_entries( $email_address, $page, $limit ); foreach ( $entries as $entry ) { $form_id = $entry['form_id']; $form = $forms[ $form_id ]; $columns_settings = rgars( $forms, $form_id . '/personalData/exportingAndErasing/columns' ); if ( is_array( $columns_settings ) ) { foreach ( $columns_settings as $column_key => $column_settings ) { if ( rgar( $column_settings, 'erase' ) ) { GFAPI::update_entry_property( $entry['id'], $column_key, '' ); $items_removed = true; } } } $has_product_field = false; foreach ( $form['fields'] as $field ) { /* @var GF_Field $field */ if ( $field->personalDataErase ) { $input_type = $field->get_input_type(); if ( $input_type == 'fileupload' ) { GFFormsModel::delete_files( $entry['id'] ); GFAPI::update_entry_field( $entry['id'], $field->id, '' ); continue; } if ( $field->type == 'product' ) { $has_product_field = true; } $value = GFFormsModel::get_lead_field_value( $entry, $field ); if ( is_array( $value ) ) { self::erase_field_values( $value, $entry['id'], $field->id ); $items_removed = true; } else { switch ( $input_type ) { case 'email': $anonymous = 'deleted@site.invalid'; break; case 'website': $anonymous = 'https://site.invalid'; break; case 'date': $anonymous = '0000-00-00'; break; case 'text': case 'textarea': /* translators: deleted text */ $anonymous = __( '[deleted]' ); break; default: $anonymous = ''; } GFAPI::update_entry_field( $entry['id'], $field->id, $anonymous ); $items_removed = true; } } } if ( $has_product_field ) { GFFormsModel::refresh_product_cache( $form, $entry ); } $custom_items = self::get_custom_items( $form ); if ( ! empty( $custom_items ) ) { $all_custom_settings = rgars( $forms, $form_id . '/personalData/exportingAndErasing/custom' ); foreach ( $custom_items as $custom_item_key => $custom_item_details ) { $custom_settings = rgar( $all_custom_settings, $custom_item_key ); if ( rgars( $custom_settings, 'erase' ) && isset( $custom_item_details['eraser_callback'] ) && is_callable( $custom_item_details['eraser_callback'] ) ) { call_user_func( $custom_item_details['eraser_callback'], $form, $entry ); $items_removed = true; } } } } $done = count( $entries ) < $limit; return array( 'items_removed' => $items_removed, 'items_retained' => false, 'messages' => array(), 'done' => $done, ); } public static function erase_field_values( $value, $entry_id, $input_id, $item_index = '' ) { if ( is_array( $value ) ) { $i = 0; foreach ( $value as $key => $val ) { if ( is_array( $val ) ) { foreach ( $val as $k => $v ) { $new_index = $item_index . '_' . $i; self::erase_field_values( $v, $entry_id, $k, $new_index ); } $i++; } else { GFAPI::update_entry_field( $entry_id, $key, '', $item_index ); } } } else { GFAPI::update_entry_field( $entry_id, $input_id, '', $item_index ); } } /** * Returns the draft submissions (save and continue) for the given email address. * * @since 2.4 * * @param $email_address * * @return array */ public static function get_draft_submissions( $email_address ) { $draft_submissions = GFFormsModel::get_draft_submissions(); if ( empty( $draft_submissions ) ) { return array(); } $user = get_user_by( 'email', $email_address ); $return = array(); $forms = self::get_forms(); foreach ( $draft_submissions as $i => $draft_submission ) { $form_id = $draft_submission['form_id']; $form = $forms[ $form_id ]; if ( ! rgars( $form, 'personalData/exportingAndErasing/enabled' ) ) { continue; } $submission_json = $draft_submission['submission']; $submission = json_decode( $submission_json, true ); $entry = $submission['partial_entry']; $identification_field = rgars( $form, 'personalData/exportingAndErasing/identificationField' ); $field = GFAPI::get_field( $form, $identification_field ); if ( ( $field && $field->get_input_type() == 'email' && $entry[ (string) $identification_field ] === $email_address ) || ( $user && $user->ID == rgar( $entry, $identification_field ) ) ) { $return[] = $draft_submission; } } return $return; } /** * Erases the data in the draft submissions. * * @since 2.4 * * @param $email_address * * @return bool */ public static function erase_draft_submissions_data( $email_address ) { $items_removed = false; $forms = self::get_forms(); $draft_entries = self::get_draft_submissions( $email_address ); foreach ( $draft_entries as $draft_entry ) { $entry_dirty = false; $form_id = $draft_entry['form_id']; $resume_token = $draft_entry['uuid']; $date_created = $draft_entry['date_created']; $form = $forms[ $form_id ]; $columns_settings = rgars( $forms, $form_id . '/personalData/exportingAndErasing/columns' ); $submission_json = $draft_entry['submission']; $submission = json_decode( $submission_json, true ); $entry = $submission['partial_entry']; $submitted_values = $submission['submitted_values']; if ( is_array( $columns_settings ) ) { foreach ( $columns_settings as $column_key => $column_settings ) { if ( rgar( $column_settings, 'erase' ) ) { if ( isset( $draft_entry[ $column_key ] ) ) { $draft_entry[ $column_key ] = ''; } if ( isset( $entry[ $column_key ] ) ) { $entry[ $column_key ] = ''; } $entry_dirty = true; } } } foreach ( $form['fields'] as $field ) { /* @var GF_Field $field */ if ( $field->personalDataErase ) { $input_type = $field->get_input_type(); $value = GFFormsModel::get_lead_field_value( $entry, $field ); if ( is_array( $value ) ) { foreach ( $value as $k => $v ) { $entry[ $k ] = ''; $submitted_values[ $field->id ][ $k ] = ''; } $entry_dirty = true; } else { switch ( $input_type ) { case 'email': $anonymous = 'deleted@site.invalid'; break; case 'website': $anonymous = 'https://site.invalid'; break; case 'date': $anonymous = '0000-00-00'; break; case 'text': case 'textarea': /* translators: deleted text */ $anonymous = __( '[deleted]', 'gravityforms' ); break; default: $anonymous = ''; } $submitted_values[ (string) $field->id ] = $anonymous; $entry[ (string) $field->id ] = $anonymous; $entry_dirty = true; } } } $custom_items = self::get_custom_items( $form ); if ( ! empty( $custom_items ) ) { $all_custom_settings = rgars( $forms, $form_id . '/personalData/exportingAndErasing/custom' ); foreach ( $custom_items as $custom_item_key => $custom_item_details ) { $custom_settings = rgar( $all_custom_settings, $custom_item_key ); if ( rgars( $custom_settings, 'erase' ) && isset( $custom_item_details['eraser_callback'] ) && is_callable( $custom_item_details['eraser_callback'] ) ) { call_user_func( $custom_item_details['eraser_callback'], $form, $entry ); $items_removed = true; } } } if ( $entry_dirty ) { $submission['submitted_values'] = $submitted_values; $submission['partial_entry'] = $entry; $submission_json = json_encode( $submission ); GFFormsModel::update_draft_submission( $resume_token, $form, $date_created, $draft_entry['ip'], $draft_entry['source_url'], $submission_json ); $items_removed = true; } } return $items_removed; } /** * Deletes and trashes entries according to the retention policy in each of the form settings. * * @since 2.4 */ public static function cron_task() { self::log_debug( __METHOD__ . '(): starting personal data cron task' ); $forms = self::get_forms(); $trash_form_ids = array(); $trash_conditions = array(); $delete_form_ids = array(); $delete_conditions = array(); foreach ( $forms as $form ) { $retention_policy = rgars( $form, 'personalData/retention/policy', 'retain' ); if ( $retention_policy == 'retain' ) { continue; } $form_conditions = array(); $retention_days = rgars( $form, 'personalData/retention/retain_entries_days' ); $delete_timestamp = time() - ( DAY_IN_SECONDS * $retention_days ); $delete_date = date( 'Y-m-d H:i:s', $delete_timestamp ); $form_conditions[] = new GF_Query_Condition( new GF_Query_Column( 'date_created' ), GF_Query_Condition::LT, new GF_Query_Literal( $delete_date ) ); $form_conditions[] = new GF_Query_Condition( new GF_Query_Column( 'form_id' ), GF_Query_Condition::EQ, new GF_Query_Literal( $form['id'] ) ); if ( ! empty( $form_conditions ) ) { if ( $retention_policy == 'trash' ) { $trash_form_ids[] = $form['id']; $trash_conditions[] = call_user_func_array( array( 'GF_Query_Condition', '_and', ), $form_conditions ); } elseif ( $retention_policy == 'delete' ) { $delete_form_ids[] = $form['id']; $delete_conditions[] = call_user_func_array( array( 'GF_Query_Condition', '_and', ), $form_conditions ); } } } if ( ! empty( $trash_conditions ) ) { $query = new GF_Query(); $all_trash_conditions = array(); $all_trash_conditions[] = call_user_func_array( array( 'GF_Query_Condition', '_or' ), $trash_conditions ); $all_trash_conditions[] = new GF_Query_Condition( new GF_Query_Column( 'status' ), GF_Query_Condition::NEQ, new GF_Query_Literal( 'trash' ) ); $all_trash_conditions = call_user_func_array( array( 'GF_Query_Condition', '_and' ), $all_trash_conditions ); $entry_ids = $query->from( $trash_form_ids )->where( $all_trash_conditions )->get_ids(); self::log_debug( __METHOD__ . '(): trashing entries: ' . join( ', ', $entry_ids ) ); foreach ( $entry_ids as $entry_id ) { GFAPI::update_entry_property( $entry_id, 'status', 'trash' ); } } if ( ! empty( $delete_conditions ) ) { $query = new GF_Query(); $all_delete_conditions = call_user_func_array( array( 'GF_Query_Condition', '_or' ), $delete_conditions ); $entry_ids = $query->from( $delete_form_ids )->where( $all_delete_conditions )->get_ids(); self::log_debug( __METHOD__ . '(): deleting entries: ' . join( ', ', $entry_ids ) ); /** * Allows the array of entry IDs to be modified before automatically deleting according to the * personal data retention policy. * * @since 2.4 * * @param int[] $entry_ids The array of entry IDs to delete. */ $entry_ids = apply_filters( 'gform_entry_ids_automatic_deletion', $entry_ids ); foreach ( $entry_ids as $entry_id ) { GFAPI::delete_entry( $entry_id ); } } self::log_debug( __METHOD__ . '(): done' ); } /** * Writes a message to the debug log * * @since 2.4 * * @param $message */ public static function log_debug( $message ) { GFCommon::log_debug( $message ); } /** * Flushes the forms * * @since 2.4 */ public static function flush_current_forms() { self::$_forms = null; } }