PATH:
home
/
letacommog
/
laindinois
/
wp-content
/
plugins
/
gravityforms.2.4.15.11
/
includes
/
webapi
/
v2
/
includes
<?php if ( ! class_exists( 'GFForms' ) ) { die(); } /** * Manages the entry results cache expiry and rebuild. * * GF_Results_Cache::get_results() will attempt to calculate the results inside the time_limit arg. * If incomplete then a WP Cron task is kicked off. * If the cron task is unable to finish within time_limit_cron then another task is scheduled until the results are complete. * * @package Gravity Forms * @subpackage GF_Results_Cache * @access public */ class GF_Results_Cache { /** * GF_Results_Cache constructor. * * @since 2.4-beta-1 */ public function __construct() { if ( defined( 'DOING_CRON' ) && DOING_CRON ) { add_action( 'gravityforms_results_cron', array( $this, 'results_cron' ), 10, 4 ); return; } add_action( 'gform_entry_created', array( $this, 'entry_created' ), 10, 2 ); add_action( 'gform_after_update_entry', array( $this, 'entry_updated' ), 10, 2 ); add_action( 'gform_update_status', array( $this, 'update_entry_status' ), 10, 2 ); add_action( 'gform_after_save_form', array( $this, 'after_save_form' ), 10, 2 ); } /** * Contains an instance of this class, if available. * * @since 2.4-beta-1 * * @var GF_Results_Cache $_instance If available, contains an instance of this class. */ private static $_instance = null; /** * Returns an instance of this class, and stores it in the $_instance property. * * @since 2.4-beta-1 * * @return GF_Results_Cache $_instance */ public static function get_instance() { if ( self::$_instance == null ) { self::$_instance = new GF_Results_Cache(); } return self::$_instance; } /** * Returns the default args for the results cache process. * * @since 2.4-beta-1 * * time_limit - Max seconds allowed per batch. * time_limit_cron - Max seconds allowed per batch while inside the cron task. * page_size - Page size for each batch search results. * callbacks - An array of callbacks. One supported callback: 'calculation' $cache_data, $form, $fields, $entries * wait - Time in seconds to wait between each cron task. * field_ids - An array of field IDs to include in the results. * * @return array */ public function get_default_args() { return array( 'time_limit' => 15, // Max seconds for the initial attempt. 'time_limit_cron' => 15, // Max seconds for the cron task. 'page_size' => 100, 'callbacks' => array(), 'wait' => 10, 'field_ids' => false, 'labels' => true, ); } /** * Callback for the gform_update_status action. * * @since 2.4-beta-1 * * @param $entry_id */ public function update_entry_status( $entry_id ) { $entry = GFAPI::get_entry( $entry_id ); $form_id = $entry['form_id']; $form = GFFormsModel::get_form_meta( $form_id ); $this->maybe_update_results_cache_meta( $form ); } /** * Callback for the gform_after_update_entry action. * * @since 2.4-beta-1 * * @param $form * @param $entry_id */ public function entry_updated( $form, $entry_id ) { $this->maybe_update_results_cache_meta( $form ); } /** * Callback for the gform_entry_created action. * * @since 2.4-beta-1 * * @param $entry * @param $form */ public function entry_created( $entry, $form ) { $this->maybe_update_results_cache_meta( $form ); } /** * Callback for the gform_after_save_form action. * * @since 2.4-beta-1 * * @param $form * @param $is_new */ public function after_save_form( $form, $is_new ) { if ( $is_new ) { return; } $form_id = $form['id']; // only need to update the cache meta when cached results exist if ( ! $this->cached_results_exists( $form_id ) ) { return; } $fields = rgar( $form, 'fields' ); $current_fields_hash = wp_hash( json_encode( $fields ) ); $cache_meta = $this->get_results_cache_meta( $form_id ); $cached_fields_hash = rgar( $cache_meta, 'fields_hash' ); if ( ! hash_equals( $current_fields_hash, $cached_fields_hash ) ) { // delete the meta for this form $this->delete_results_cache_meta( $form_id ); // delete all cached results for this form $this->delete_cached_results( $form_id ); } } /** * When entries are added or updated the cache needs to be expired and rebuilt. * * This cache meta records the last updated time for each form and a hash of the fields array. * Each time results are requested this value is checked to make sure the cache is still valid. * * @since 2.4-beta-1 * * @param $form */ private function maybe_update_results_cache_meta( $form ) { $form_id = $form['id']; // Only need to update the cache meta when cached results exist. if ( ! $this->cached_results_exists( $form_id ) ) { return; } $this->update_results_cache_meta( $form_id, rgar( $form, 'fields' ) ); } /** * Updates the results cache meta containing a hash of the all the fields and a timestamp. * * @since 2.4-beta-1 * * @param $form_id * @param $fields */ private function update_results_cache_meta( $form_id, $fields ) { $data = array( 'fields_hash' => wp_hash( json_encode( $fields ) ), 'timestamp' => time(), ); $key = $this->get_results_cache_meta_key( $form_id ); $this->update_results_cache( $key, $data ); } /** * Deletes the cache meta. * * @since 2.4-beta-1 * * * @param $form_id */ private function delete_results_cache_meta( $form_id ) { $key = $this->get_results_cache_meta_key( $form_id ); delete_option( $key ); } /** * Returns the cache meta key. * * @since 2.4-beta-1 * * @param $form_id * * @return string */ private function get_results_cache_meta_key( $form_id ) { $key = 'gf-results-cache-meta-form-' . $form_id; return $key; } /** * Returns the cache meta. * * @since 2.4-beta-1 * * @param $form_id * * @return mixed|void */ private function get_results_cache_meta( $form_id ) { $key = $this->get_results_cache_meta_key( $form_id ); $cache_meta = get_option( $key ); return $cache_meta; } /** * Updates the results cache. * * @since 2.4-beta-1 * * @param $key * @param $data * * @return bool */ private function update_results_cache( $key, $data ) { /* From: https://codex.wordpress.org/Function_Reference/add_option * * Until version 4.2, you could not specify autoload='no' if you use update_option(). * If you need to specify autoload='no', and you are not sure whether the option already exists, * then call delete_option() first before calling add_option(). */ delete_option( $key ); $result = add_option( $key, $data, '', 'no' ); return $result; } /** * Checks whether a cache exists for the given form ID. * * @since 2.4-beta-1 * * @param $form_id * * @return bool */ private function cached_results_exists( $form_id ) { global $wpdb; $key = $this->get_results_cache_key_prefix( $form_id ); $key = '%' . GFCommon::esc_like( $key ) . '%'; $sql = $wpdb->prepare( "SELECT count(option_id) FROM $wpdb->options WHERE option_name LIKE %s", $key ); $result = $wpdb->get_var( $sql ); return $result > 0; } /** * Deletes all the cached results for the given form ID. * * @since 2.4-beta-1 * * @param $form_id * * @return false|int|void */ public function delete_cached_results( $form_id ) { global $wpdb; $form = GFAPI::get_form( $form_id ); if ( ! ( $form ) || ! is_array( $form ) ) { return; } $key = $this->get_results_cache_key_prefix( $form_id ); $key = '%' . GFCommon::esc_like( $key ) . '%'; $sql = $wpdb->prepare( "DELETE FROM $wpdb->options WHERE option_name LIKE %s", $key ); $result = $wpdb->query( $sql ); return $result; } /** * Returns the prefix for the results cache option name. * * @since 2.4-beta-1 * * @param $form_id * * @return string */ public function get_results_cache_key_prefix( $form_id ) { $key = sprintf( 'gf-results-cache-%s-', $form_id ); // The option_name column in the options table has a max length of 64 chars. // Truncate the key if it's too long for column and allow space for the 'tmp' prefix $key = substr( $key, 0, 60 ); return $key; } /** * Generates a unique key for the cache meta based on form ID, fields and * * @since 2.4-beta-1 * * @param $form_id * @param $search_criteria * * @return string */ public function get_results_cache_key( $form_id, $search_criteria = array() ) { $key = $this->get_results_cache_key_prefix( $form_id ); $key .= wp_hash( json_encode( $search_criteria ) ); return $key; } /** * Recursive wp_cron task to continue the calculation of results. * * @since 2.4-beta-1 * * @param $form_id * @param $search_criteria * @param $args */ public function results_cron( $form_id, $search_criteria, $args ) { $args = wp_parse_args( $args, $this->get_default_args() ); $form = GFAPI::get_form( $form_id ); $key = $this->get_results_cache_key( $form_id, $search_criteria ); $key_tmp = 'tmp' . $key; $state = get_option( $key_tmp, array() ); if ( ! empty( $state ) ) { $results = $this->calculate( $form, $search_criteria, $state, $args ); if ( 'complete' == $results['status'] ) { if ( isset( $results['progress'] ) ) { unset( $results['progress'] ); } $this->update_results_cache( $key, $results ); if ( false == empty( $state ) ) { delete_option( $key_tmp ); } } else { $this->update_results_cache( $key_tmp, $results ); $data = get_option( $key ); if ( $data ) { $data['progress'] = $results['progress']; $this->update_results_cache( $key, $data ); } $this->schedule_results_cron( $form_id, $search_criteria, $args ); } } } /** * Schedules the cron task. * * @since 2.4-beta-1 * * @param $form_id * @param $search_criteria * @param $args */ private function schedule_results_cron( $form_id, $search_criteria, $args ) { $args = wp_parse_args( $args, $this->get_default_args() ); $cron_args = array( $form_id, $search_criteria, $args ); $delay_in_seconds = $args['wait']; wp_schedule_single_event( time() + $delay_in_seconds, $this->get_results_cron_hook(), $cron_args ); } /** * Checks if the results cron job is currently scheduled * * @since 2.4-beta-1 * * @param $form_id * @param $search_criteria * @param $args * * @return false|int */ public function results_cron_is_scheduled( $form_id, $search_criteria, $args ) { $args = wp_parse_args( $args, $this->get_default_args() ); $cron_args = array( $form_id, $search_criteria, $args ); return wp_next_scheduled( $this->get_results_cron_hook(), $cron_args ); } /** * Returs the results cron hook name * * @since 2.4-beta-1 * * @return string */ public function get_results_cron_hook() { return 'gravityforms_results_cron'; } /** * Returns an array with the results for all the fields in the form. * * If the results can be calculated within the time allowed in GFResults then the results are returned and nothing is cached. * If the calculation has not finished then a single recursive wp_cron task will be scheduled for immediate execution. * While the cache is being built by the wp_cron task this function will return the expired cache results if available or the latest step in the cache build. * Add-On-specific results are not included e.g. grade frequencies in the Quiz Add-On. * * @since 2.4-beta-1 * * @param int $form_id * @param array $search_criteria * @param array $args * * @return array|mixed|void */ public function get_results( $form_id, $search_criteria = array(), $args = array() ) { $args = wp_parse_args( $args, $this->get_default_args() ); $form = GFAPI::get_form( $form_id ); if ( ! $form ) { return new WP_Error( 'not_found', __( 'Form not found', 'gravityforms' ) ); } $fields = rgar( $form, 'fields' ); $form_id = $form['id']; $key = $this->get_results_cache_key( $form_id, $search_criteria ); $key_tmp = 'tmp' . $key; $data = get_option( $key, array() ); $cache_meta = $this->get_results_cache_meta( $form_id ); // add the cache meta early so form editor updates can test for valid field hash if ( empty( $cache_meta ) ) { $this->update_results_cache_meta( $form_id, $fields ); } $cache_expiry = rgar( $cache_meta, 'timestamp' ); $cache_timestamp = isset( $data['timestamp'] ) ? $data['timestamp'] : 0; $cache_expired = $cache_expiry ? $cache_expiry > $cache_timestamp : false; // check for valid cached results first if ( ! empty( $data ) && 'complete' == rgar( $data, 'status' ) && ! $cache_expired ) { $results = $data; if ( isset( $results['progress'] ) ) { unset( $results['progress'] ); } } else { $state = get_option( $key_tmp ); if ( empty( $state ) || ( 'complete' == rgar( $data, 'status' ) && $cache_expired ) ) { $results = $this->calculate( $form, $search_criteria, $state, $args ); if ( rgar( $results, 'status' ) == 'complete' ) { if ( false == empty( $state ) ) { delete_option( $key_tmp ); } } else { if ( ! empty( $data ) && rgar( $data, 'status' ) == 'complete' && $cache_expired ) { $data['status'] = 'expired'; $data['progress'] = $results['progress']; $this->update_results_cache( $key, $data ); } $this->update_results_cache( $key_tmp, $results ); $this->schedule_results_cron( $form_id, $search_criteria, $args ); if ( $data ) { $results = $data; } } } else { // The cron task is recursive, not periodic, so system restarts, script timeouts and memory issues can prevent the cron from restarting. // Check timestamp and kick off the cron again if it appears to have stopped $state_timestamp = rgar( $state, 'timestamp' ); $state_age = time() - $state_timestamp; if ( $state_age > 180 && ! $this->results_cron_is_scheduled( $form, $search_criteria, $args ) ) { $this->schedule_results_cron( $form_id, $search_criteria, $args ); } if ( ! empty( $data ) && rgar( $data, 'status' ) == 'expired' ) { $results = $data; } else { $results = $state; } } } $field_data = rgar( $results, 'field_data' ); if ( ! empty( $field_data ) && $args['labels'] ) { // add choice labels to the results so the client doesn't need to cross-reference with the form object $results['labels'] = $this->get_labels( $form, $args ); } return $results; } /** * Calculate a batch of entry results. * * @since 2.4-beta-1 * * @param $form * @param array $search_criteria * @param array $state_array * @param $args * * @return array|mixed */ public function calculate( $form, $search_criteria = array(), $state_array = array(), $args ) { $args = wp_parse_args( $args, $this->get_default_args() ); $max_execution_time = defined( 'DOING_CRON' ) && DOING_CRON ? $args['time_limit_cron'] : $args['time_limit']; $page_size = $args['page_size']; $callbacks = $args['callbacks']; $time_start = microtime( true ); $form_id = $form['id']; $data = array(); $offset = 0; $entry_count = 0; $field_data = array(); $fields = $this->filter_fields( $form, $args['field_ids'] ); if ( $state_array ) { // get counts from state $data = $state_array; $offset = (int) rgar( $data, 'offset' ); unset( $data['offset'] ); $entry_count = $offset; $field_data = rgar( $data, 'field_data' ); } else { // initialize counts foreach ( $fields as $field ) { /* @var GF_Field $field */ $field_type = $field->get_input_type(); if ( ! isset( $field->choices ) ) { $field_data[ $field->id ] = 0; continue; } $choices = $field->choices; if ( $field_type == 'likert' && $field->gsurveyLikertEnableMultipleRows ) { foreach ( $field->gsurveyLikertRows as $row ) { foreach ( $choices as $choice ) { $field_data[ $field->id ][ $row['value'] ][ $choice['value'] ] = 0; } if ( $field->gsurveyLikertEnableScoring ) { $field_data[ $field->id ][ $row['value'] ]['row_score_sum'] = 0; } } } else { if ( ! empty( $choices ) && is_array( $choices ) ) { foreach ( $choices as $choice ) { $field_data[ $field->id ][ $choice['value'] ] = 0; } } else { $field_data[ $field->id ] = 0; } } if ( $field_type == 'likert' && rgar( $field, 'gsurveyLikertEnableScoring' ) ) { $field_data[ $field->id ]['sum_of_scores'] = 0; } } } $count_search_entries = GFAPI::count_entries( $form_id, $search_criteria ); $data['entry_count'] = $count_search_entries; if ( $count_search_entries == 0 ) { $data['status'] = 'complete'; } $entries_left = $count_search_entries - $offset; while ( $entries_left > 0 ) { $paging = array( 'offset' => $offset, 'page_size' => $page_size, ); $search_entries_time_start = microtime( true ); $entries = GFAPI::get_entries( $form_id, $search_criteria, null, $paging ); $search_entries_time_end = microtime( true ); $search_entries_time = $search_entries_time_end - $search_entries_time_start; $entries_in_search = count( $entries ); $entry_count += $entries_in_search; $entries_processed = 0; foreach ( $entries as $entry ) { $entry_time_start = microtime( true ); foreach ( $fields as $field ) { $field_type = $field->get_input_type(); $field_id = $field->id; $value = GFFormsModel::get_lead_field_value( $entry, $field ); if ( $field_type == 'likert' && rgar( $field, 'gsurveyLikertEnableMultipleRows' ) ) { if ( empty( $value ) ) { continue; } foreach ( $value as $value_vector ) { if ( empty( $value_vector ) ) { continue; } list( $row_val, $col_val ) = explode( ':', $value_vector, 2 ); if ( isset( $field_data[ $field->id ][ $row_val ] ) && isset( $field_data[ $field->id ][ $row_val ][ $col_val ] ) ) { $field_data[ $field->id ][ $row_val ][ $col_val ] ++; if ( $field->gsurveyLikertEnableScoring ) { $field_data[ $field->id ][ $row_val ]['row_score_sum'] += $this->get_likert_row_score( $row_val, $field, $entry ); } } } } elseif ( $field_type == 'rank' ) { $score = count( rgar( $field, 'choices' ) ); $values = explode( ',', $value ); foreach ( $values as $ranked_value ) { $field_data[ $field->id ][ $ranked_value ] += $score; $score --; } } else { if ( empty( $field->choices ) ) { if ( ( ! is_array( $value ) && ! empty( $value ) ) || ( is_array( $value ) && ! GFCommon::is_empty_array( $value ) ) ) { $field_data[ $field_id ] ++; } continue; } $choices = $field->choices; foreach ( $choices as $choice ) { $choice_is_selected = false; if ( is_array( $value ) ) { $choice_value = rgar( $choice, 'value' ); if ( in_array( $choice_value, $value ) ) { $choice_is_selected = true; } } else { if ( GFFormsModel::choice_value_match( $field, $choice, $value ) ) { $choice_is_selected = true; } } if ( $choice_is_selected ) { $field_data[ $field_id ][ $choice['value'] ] ++; } } } if ( $field_type == 'likert' && rgar( $field, 'gsurveyLikertEnableScoring' ) ) { $field_data[ $field->id ]['sum_of_scores'] += $this->get_likert_score( $field, $entry ); } } $entries_processed ++; $entry_time_end = microtime( true ); $total_execution_time = $entry_time_end - $search_entries_time_start; $entry_execution_time = $entry_time_end - $entry_time_start; if ( $total_execution_time + $entry_execution_time > $max_execution_time ) { break; } } $data['field_data'] = $field_data; if ( isset( $callbacks['calculation'] ) && is_callable( $callbacks['calculation'] ) ) { $data = call_user_func( $callbacks['calculation'], $data, $form, $fields, $entries ); $field_data = $data['field_data']; } $offset += $entries_processed; $entries_left -= $entries_processed; $time_end = microtime( true ); $execution_time = ( $time_end - $time_start ); if ( $entries_left > 0 && $execution_time + $search_entries_time > $max_execution_time ) { $data['status'] = 'incomplete'; $data['offset'] = $offset; $progress = $data['entry_count'] > 0 ? round( $data['offset'] / $data['entry_count'] * 100 ) : 0; $data['progress'] = $progress; break; } if ( $entries_left <= 0 ) { $data['status'] = 'complete'; } } $data['timestamp'] = time(); return $data; } /** * Returns the likert field row score * * @since 2.4-beta-1 * * * @param $row_val * @param $field * @param $entry * * @return float|int */ private function get_likert_row_score( $row_val, $field, $entry ) { return is_callable( array( 'GFSurvey', 'get_likert_row_score', ) ) ? GFSurvey::get_likert_row_score( $row_val, $field, $entry ) : 0; } /** * Returns the likert field score * * @since 2.4-beta-1 * * * @param $field * @param $entry * * @return float|int */ private function get_likert_score( $field, $entry ) { return is_callable( array( 'GFSurvey', 'get_field_score', ) ) ? GFSurvey::get_field_score( $field, $entry ) : 0; } /** * Returns an array with field labels and choice labels * * @since 2.4-beta-1 * * * @param $form * @param $args * * @return array */ private function get_labels( $form, $args ) { $args = wp_parse_args( $args, $this->get_default_args() ); $fields = $this->filter_fields( $form, $args['field_ids'] ); $labels = array(); // replace the values/ids with text labels foreach ( $fields as $field ) { $field_id = $field->id; $field = GFFormsModel::get_field( $form, $field_id ); if ( is_array( $field->choices ) ) { $label = array(); $choice_labels = array(); foreach ( $field->choices as $choice ) { $choice_labels[ $choice['value'] ] = $choice['text']; } if ( $field instanceof GF_Field_Likert && $field->gsurveyLikertEnableMultipleRows ) { /* @var GF_Field_Likert $field */ $label = array( 'label' => $field->label, 'cols' => $choice_labels, 'rows' => array(), ); foreach ( $field->gsurveyLikertRows as $row ) { $label['rows'][ $row['value'] ] = $row['text']; } } else { $label['label'] = $field->label; $label['choices'] = $choice_labels; } } else { $label = $field['label']; } $labels[ $field->id ] = $label; } return $labels; } /** * Filters the form array, returning only the fields matching the specified list of $field_ids * * @since 2.4-beta-1 * * @param $form The form array to be filtered * @param $field_ids The list of field ids to be returned * * @return array Returns a filtered form array only containing fields that match the $field_ids list */ private function filter_fields( $form, $field_ids ) { $fields = $form['fields']; if ( is_array( $field_ids ) && ! empty( $field_ids ) ) { foreach ( $fields as $key => $field ) { if ( ! in_array( $field->id, $field_ids ) ) { unset( $fields[ $key ] ); } } $fields = array_values( $fields ); } return $fields; } } /** * @return GF_Results_Cache */ function gf_results_cache() { return GF_Results_Cache::get_instance(); } gf_results_cache();
[+]
..
[-] index.php
[edit]
[-] class-results-cache.php
[edit]
[+]
controllers