From 04aa126e41cc076c05a6e6fdf7d5e5dcc4170abb Mon Sep 17 00:00:00 2001 From: Gravity Forms Date: Mon, 22 Apr 2024 13:01:36 +0000 Subject: [PATCH] Updates to 1.7 --- change_log.txt | 67 + class-gf-partial-entries.php | 1436 +++++++++++++++++ images/menu-icon.svg | 1 + js/admin.js | 38 + js/admin.min.js | 1 + js/feed-settings.js | 36 + js/feed-settings.min.js | 1 + js/partial-entries.js | 80 + js/partial-entries.min.js | 1 + languages/gravityformspartialentries-ar.mo | Bin 0 -> 476 bytes languages/gravityformspartialentries-ca.mo | Bin 0 -> 948 bytes languages/gravityformspartialentries-da_DK.mo | Bin 0 -> 3008 bytes languages/gravityformspartialentries-de_DE.mo | Bin 0 -> 3178 bytes ...gravityformspartialentries-de_DE_formal.mo | Bin 0 -> 830 bytes languages/gravityformspartialentries-en_AU.mo | Bin 0 -> 2942 bytes languages/gravityformspartialentries-en_GB.mo | Bin 0 -> 2947 bytes languages/gravityformspartialentries-es_ES.mo | Bin 0 -> 3099 bytes languages/gravityformspartialentries-fi.mo | Bin 0 -> 3039 bytes languages/gravityformspartialentries-fr_CA.mo | Bin 0 -> 415 bytes languages/gravityformspartialentries-fr_FR.mo | Bin 0 -> 3251 bytes languages/gravityformspartialentries-he_IL.mo | Bin 0 -> 2893 bytes languages/gravityformspartialentries-hi_IN.mo | Bin 0 -> 3942 bytes languages/gravityformspartialentries-hu_HU.mo | Bin 0 -> 420 bytes languages/gravityformspartialentries-it_IT.mo | Bin 0 -> 3063 bytes languages/gravityformspartialentries-ja.mo | Bin 0 -> 2989 bytes languages/gravityformspartialentries-nb_NO.mo | Bin 0 -> 939 bytes languages/gravityformspartialentries-nl_BE.mo | Bin 0 -> 416 bytes languages/gravityformspartialentries-nl_NL.mo | Bin 0 -> 3131 bytes languages/gravityformspartialentries-pt_BR.mo | Bin 0 -> 3077 bytes languages/gravityformspartialentries-pt_PT.mo | Bin 0 -> 3115 bytes languages/gravityformspartialentries-ru_RU.mo | Bin 0 -> 3846 bytes languages/gravityformspartialentries-sv_SE.mo | Bin 0 -> 2931 bytes languages/gravityformspartialentries-tr_TR.mo | Bin 0 -> 2711 bytes languages/gravityformspartialentries-zh_CN.mo | Bin 0 -> 906 bytes languages/gravityformspartialentries.pot | 140 ++ partialentries.php | 46 + 36 files changed, 1847 insertions(+) create mode 100644 change_log.txt create mode 100644 class-gf-partial-entries.php create mode 100644 images/menu-icon.svg create mode 100644 js/admin.js create mode 100644 js/admin.min.js create mode 100644 js/feed-settings.js create mode 100644 js/feed-settings.min.js create mode 100644 js/partial-entries.js create mode 100644 js/partial-entries.min.js create mode 100644 languages/gravityformspartialentries-ar.mo create mode 100644 languages/gravityformspartialentries-ca.mo create mode 100644 languages/gravityformspartialentries-da_DK.mo create mode 100644 languages/gravityformspartialentries-de_DE.mo create mode 100644 languages/gravityformspartialentries-de_DE_formal.mo create mode 100644 languages/gravityformspartialentries-en_AU.mo create mode 100644 languages/gravityformspartialentries-en_GB.mo create mode 100644 languages/gravityformspartialentries-es_ES.mo create mode 100644 languages/gravityformspartialentries-fi.mo create mode 100644 languages/gravityformspartialentries-fr_CA.mo create mode 100644 languages/gravityformspartialentries-fr_FR.mo create mode 100644 languages/gravityformspartialentries-he_IL.mo create mode 100644 languages/gravityformspartialentries-hi_IN.mo create mode 100644 languages/gravityformspartialentries-hu_HU.mo create mode 100644 languages/gravityformspartialentries-it_IT.mo create mode 100644 languages/gravityformspartialentries-ja.mo create mode 100644 languages/gravityformspartialentries-nb_NO.mo create mode 100644 languages/gravityformspartialentries-nl_BE.mo create mode 100644 languages/gravityformspartialentries-nl_NL.mo create mode 100644 languages/gravityformspartialentries-pt_BR.mo create mode 100644 languages/gravityformspartialentries-pt_PT.mo create mode 100644 languages/gravityformspartialentries-ru_RU.mo create mode 100644 languages/gravityformspartialentries-sv_SE.mo create mode 100644 languages/gravityformspartialentries-tr_TR.mo create mode 100644 languages/gravityformspartialentries-zh_CN.mo create mode 100644 languages/gravityformspartialentries.pot create mode 100644 partialentries.php diff --git a/change_log.txt b/change_log.txt new file mode 100644 index 0000000..ebd9ff3 --- /dev/null +++ b/change_log.txt @@ -0,0 +1,67 @@ +### 1.7 | 2022-04-07 +- Added the [gform_partialentries_pre_update](https://docs.gravityforms.com/gform_partialentries_pre_update/) filter to allow modifying the partial entry data before it is updated. +- Fixed an issue that causes an unsaved changes notice to display each time partial entries is enabled or disabled in the form settings. + +### 1.6 | 2020-09-24 +- Added support for Gravity Forms 2.5. + +### 1.5 | 2020-05-11 +- Added translations for Hebrew, Hindi, Japanese, and Turkish. +- Fixed an issue where values for fields located in sections or pages which are hidden by conditional logic are being saved to the partial entry. + + +### 1.4 | 2019-08-07 +- Added security enhancements. +- Added support for anti-spam honeypot validation prior to creation of partial entry. +- Fixed "?gf_token" being saved as the value of the resume_url entry meta when a draft submission has not been saved. + + +### 1.3 | 2018-12-19 +- Updated minimum Gravity Forms version to 2.3. +- Fixed issues with conditional logic evaluation when the entry is saved during form submission. +- Fixed a PHP notice with Gravity Forms 2.3+ when save and continue is enabled for the form. +- Fixed an issue where a completed entry could be overridden by a partial entry when an expired resume token is used. +- Fixed a PHP notice which could occur during submission if the form contains one or more product fields. +- Fixed issue where entry count did not match list of entries in certain scenarios. + + +### 1.2 | 2018-03-27 +- Added security enhancements. +- Fixed an issue preventing automatic upgrades. + + +### 1.1 | 2018-01-29 +- Added Author URI to plugin header. +- Added support for Gravity Forms 2.3. +- Updated Plugin URI in plugin header to be https. +- Updated the feed conditional logic progress choices to support the "is" operator. +- Updated the "Complete" choice of the "Progress" entry meta item to support the "is not" operator. +- Updated text "Enable Condition" to "Enable" in making Add-Ons consistent. +- Updated partial-entries.js to include heartbeat.js as a dependency. +- Fixed the handling of the "No Duplicates" field setting where validation fails on submission if a partial entry contains a duplicate value. The "No Duplicates" setting is now ignored when saving partial entries. Partial entries are now ignored when checking for duplicates on final submission - only complete entries are checked. +- Fixed a PHP notice while viewing the entry list with WordPress 4.8.3. +- Fixed a PHP notice during partial entry creation if the form includes a fileupload type field. + + +### 1.0 | 2016-09-23 +- Added the [form_partialentries_post_{$event}](https://docs.gravityforms.com/gform_partialentries_post_event/) hook enabling custom actions to be performed once a partial entry has been saved or updated. +- Updated to set entry meta (e.g. Quiz Score Total) when a partial entry is saved/updated. +- Fixed issues with saving partial entries and with submitting the form when one of the fields has the "No Duplicates" setting enabled. +- Fixed an issue with the form submission where previously saved values are not deleted when subsequently hidden by conditional logic. +- Fixed an issue which caused the form to be validated twice during page changes. +- Fixed an issue which caused GFFormDisplay::is_last_page() to return an incorrect result causing payment add-ons to perform credit card field validation on the wrong page. + + +### 1.0-beta-2 | 2016-03-30 +- Added support for Gravity Forms 2.0-beta-2. +- Added the Last Saved date and link to the entry detail page. +- Added the gform_pre_render filter before creating the partial entry. +- Added support for notification events (sending notifications when the partial entry is saved and updated). +- Added logging statements. +- Fixed PHP warning on notifications list and edit pages. +- Fixed an issue with the warning message. +- Fixed the text domains used for some of the translatable strings. + + +### 1.0-beta-1 | 2016-03-17 +- All new! diff --git a/class-gf-partial-entries.php b/class-gf-partial-entries.php new file mode 100644 index 0000000..3ba8a78 --- /dev/null +++ b/class-gf-partial-entries.php @@ -0,0 +1,1436 @@ +get_base_path() . '/images/menu-icon.svg' ); + + } + + public function init() { + parent::init(); + + add_action( 'gform_enqueue_scripts', array( $this, 'action_gform_enqueue_scripts' ), 10, 2 ); + add_filter( 'heartbeat_received', array( $this, 'filter_heartbeat_received' ), 10, 2 ); + add_filter( 'heartbeat_nopriv_received', array( $this, 'filter_heartbeat_received' ), 10, 2 ); + add_action( 'gform_entry_id_pre_save_lead', array( $this, 'filter_gform_entry_id_pre_save_lead' ), 9, 2 ); + add_action( 'gform_is_duplicate', array( $this, 'filter_gform_is_duplicate' ), 10, 4 ); + add_action( 'gform_post_process', array( $this, 'action_gform_post_process' ), 10, 3 ); + } + + public function init_admin() { + parent::init_admin(); + + add_filter( 'gform_filter_links_entry_list', array( $this, 'filter_gform_filter_links_entry_list' ), 10, 3 ); + + add_filter( 'gform_entries_field_value', array( $this, 'filter_gform_entries_field_value' ), 10, 4 ); + add_filter( 'gform_entry_info', array( $this, 'action_gform_entry_info' ), 10, 2 ); + + if ( ! method_exists( 'GFForms', 'set_screen_options' ) ) { + // Required for Gravity Forms < 2.0 + if ( $this->is_entry_list() && ! isset( $_GET['filter'] ) ) { + $default_filter = $this->get_default_filter(); + if ( $default_filter !== 'all' ) { + $url = add_query_arg( array( 'filter' => $default_filter ) ); + $url = esc_url_raw( $url ); + wp_safe_redirect( $url ); + } + } + add_filter( 'screen_settings', array( $this, 'show_screen_options' ), 10, 2 ); + add_filter( 'set-screen-option', array( $this, 'set_screen_options' ), 11, 3 ); + add_action( 'load-forms_page_gf_entries', array( $this, 'load_screen_options' ) ); + add_filter( 'gform_entry_page_size', array( $this, 'filter_gform_entry_page_size' ) ); + } + + } + + public function init_ajax() { + parent::init_ajax(); + add_filter( 'gform_filter_links_entry_list', array( $this, 'filter_gform_filter_links_entry_list' ), 10, 3 ); + } + + /** + * Target for the gform_enqueue_scripts filter. + * + * @param $form + * @param $is_ajax + */ + public function action_gform_enqueue_scripts( $form, $is_ajax ) { + + $form_id = absint( $form['id'] ); + + $default_warning_message = $this->get_default_warning_message(); + + $feed_settings = $this->get_feed_settings( $form['id'] ); + if ( ! rgar( $feed_settings, 'enable' ) ) { + return; + } + + $warning_message = rgar( $feed_settings, 'warning_message' ); + + if ( empty( $warning_message ) ) { + $warning_message = $default_warning_message; + } + + $warning_message = apply_filters( 'gform_partialentries_warning_message', $warning_message ); + $warning_message = apply_filters( 'gform_partialentries_warning_message_' . $form_id, $warning_message ); + + $min = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG || isset( $_GET['gform_debug'] ) ? '' : '.min'; + wp_enqueue_script( 'gf_partial_entries', $this->get_base_url() . "/js/partial-entries{$min}.js", array( + 'heartbeat', + 'jquery' + ), $this->_version, true ); + wp_localize_script( 'gf_partial_entries', 'gf_partial_entries_strings_' . $form_id, array( + 'warningMessage' => $warning_message, + ) ); + + add_filter( 'gform_form_tag', array( $this, 'filter_gform_form_tag' ), 10, 2 ); + } + + /** + * Set up the scripts. + * + * @return array + */ + public function scripts() { + $min = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG || isset( $_GET['gform_debug'] ) ? '' : '.min'; + $scripts = array( + array( + 'handle' => 'google_charts', + 'src' => 'https://www.google.com/jsapi', + 'version' => GFCommon::$version, + 'enqueue' => array( + array( 'admin_page' => array( 'entry_list', 'entry_view' ) ), + ), + ), + array( + 'handle' => 'gf_partial_entries_admin', + 'src' => $this->get_base_url() . "/js/admin{$min}.js", + 'deps' => array( 'jquery', 'google_charts' ), + 'version' => $this->_version, + 'enqueue' => array( + array( 'admin_page' => array( 'entry_list', 'entry_view' ) ), + ), + ), + array( + 'handle' => 'gf_partial_entries_feed_settings', + 'src' => $this->get_base_url() . "/js/feed-settings{$min}.js", + 'deps' => array( 'jquery' ), + 'version' => $this->_version, + 'enqueue' => array( + array( 'query' => 'page=gf_edit_forms&view=settings&subview=gravityformspartialentries&id=_notempty_' ), + ), + 'strings' => array( + 'fields' => wp_strip_all_tags( __( 'Fields', 'gravityformspartialentries' ) ), + 'allFields' => wp_strip_all_tags( __( 'All Fields', 'gravityformspartialentries' ) ), + 'requiredFields' => wp_strip_all_tags( __( 'Required Fields', 'gravityformspartialentries' ) ), + 'progress' => wp_strip_all_tags( __( 'Progress', 'gravityformspartialentries' ) ), + ), + ), + ); + + return array_merge( parent::scripts(), $scripts ); + } + + /** + * Return the feed settings config. + * + * @return array + */ + function feed_settings_fields() { + $enabled_dependency = array( + 'live' => true, + 'fields' => array( + array( + 'field' => 'enable_partial', + ), + ), + ); + + if ( ! $this->is_gravityforms_supported( '2.5-rc-2' ) ) { + $enabled_dependency = array( + 'field' => 'enable', + 'values' => array( 1 ), + ); + } + + return array( + array( + 'title' => __( 'Partial Entries', 'gravityformspartialentries' ), + 'fields' => array( + array( + 'name' => 'enable_partial', + 'label' => esc_html__( 'Partial Entries', 'gravityformspartialentries' ), + 'type' => 'checkbox', + 'onclick' => ! $this->is_gravityforms_supported( '2.5-rc-2' ) ? "jQuery(this).closest('form').submit();" : null, + 'choices' => array( + array( + 'label' => esc_html__( 'Enable', 'gravityformspartialentries' ), + 'name' => 'enable', + ), + ), + ), + array( + 'name' => 'fields_message', + 'label' => '', + 'type' => 'fields_message', + 'dependency' => $enabled_dependency, + ), + array( + 'name' => 'warning_message', + 'label' => esc_html__( 'Warning Message', 'gravityformspartialentries' ), + 'class' => 'fieldwidth-3 fieldheight-2', + 'type' => 'textarea', + 'default_value' => $this->get_default_warning_message(), + 'tooltip' => esc_html__( "In the interest of transparency and out of respect for users' privacy this notice will appear at the top of the form below the description.", 'gravityformspartialentries' ), + 'required' => true, + 'dependency' => $enabled_dependency, + ), + array( + 'name' => 'enable', + 'label' => esc_html__( 'Conditional Logic', 'gravityformspartialentries' ), + 'type' => 'feed_condition', + 'checkbox_label' => esc_html__( 'Enable', 'gravityformspartialentries' ), + 'instructions' => esc_html__( 'Add/update partial entry if', 'gravityformspartialentries' ), + 'dependency' => $enabled_dependency, + ), + ), + ), + ); + } + + /** + * Output the threshold setting. + * + * @param $field + * + * @return string + */ + public function settings_threshold( $field ) { + $percent_choices = array(); + for ( $i = 10; $i <= 100; $i += 10 ) { + $percent_choices[] = array( 'label' => $i . '%', 'value' => $i ); + } + + $percent_field = array( + 'name' => 'threshold_percent', + 'type' => 'select', + 'default_value' => 10, + 'choices' => $percent_choices, + ); + + $type_field = array( + 'name' => 'threshold_type', + 'type' => 'select', + 'default_value' => 'all_fields', + 'choices' => array( + array( 'label' => esc_html__( 'all fields', 'gravityflow' ), 'value' => 'all_fields' ), + array( 'label' => esc_html__( 'required fields', 'gravityflow' ), 'value' => 'required_fields' ), + ), + ); + + $percent_field_html = $this->settings_select( $percent_field, false ); + $type_field_html = $this->settings_select( $type_field, false ); + + $html = sprintf( esc_html_x( '%s of %s', 'Threshold setting', 'gravityformspartialentries' ), $percent_field_html, $type_field_html ); + + echo $html; + + return $html; + } + + /** + * Configure entry meta. + * + * @param array $entry_meta + * @param int $form_id + * + * @return array + */ + public function get_entry_meta( $entry_meta, $form_id ) { + + $form = GFAPI::get_form( $form_id ); + $feed_settings = $this->get_feed_settings( $form_id ); + if ( ! rgar( $feed_settings, 'enable' ) ) { + return $entry_meta; + } + + $entry_meta['partial_entry_id'] = array( + 'label' => __( 'Partial Entry ID', 'gravityformspartialentries' ), + 'is_numeric' => false, + 'is_default_column' => false, + ); + + $entry_meta['partial_entry_percent'] = array( + 'label' => __( 'Progress', 'gravityformspartialentries' ), + 'is_numeric' => true, + 'is_default_column' => true, + 'update_entry_meta_callback' => array( $this, 'callback_update_partial_entry_percent_entry_meta' ), + 'filter' => array( + 'operators' => array( 'is', 'isnot', '>' ), + 'choices' => array( + array( 'text' => 'Complete', 'value' => '', 'operators' => array( 'is', 'isnot' ) ), + array( 'text' => '30%', 'value' => '30', 'operators' => array( '>' ) ), + array( 'text' => '60%', 'value' => '60', 'operators' => array( '>' ) ), + ), + ), + ); + + $entry_meta['required_fields_percent_complete'] = array( + 'label' => esc_html__( 'Progress: required fields', 'gravityformspartialentries' ), + 'is_numeric' => true, + 'is_default_column' => false, + 'update_entry_meta_callback' => array( $this, 'callback_update_partial_entry_percent_entry_meta' ), + ); + + + if ( rgars( $form, 'save/enabled' ) ) { + $entry_meta['resume_token'] = array( + 'label' => __( 'Save and Continue Token', 'gravityformspartialentries' ), + 'is_numeric' => false, + 'is_default_column' => false, + ); + $entry_meta['resume_url'] = array( + 'label' => __( 'Save and Continue URL', 'gravityformspartialentries' ), + 'is_numeric' => false, + 'is_default_column' => false, + ); + $entry_meta['date_saved'] = array( + 'label' => __( 'Saved', 'gravityformspartialentries' ), + 'is_numeric' => false, + 'is_default_column' => true, + ); + } + + return $entry_meta; + } + + /** + * Target for the callback_update_partial_entry_percent_entry_meta callback. + * + * @param $key + * @param $entry + * @param $form + * + * @return string + */ + public function callback_update_partial_entry_percent_entry_meta( $key, $entry, $form ) { + return ''; + } + + + /** + * Saves the entry if the conditions are met. + * + * @param $form_id + * + * @return bool|string + */ + function maybe_save_partial_entry( $form_id ) { + + if ( ! isset( $_POST['partial_entry_id'] ) ) { + return false; + } + + require_once( GFCommon::get_base_path() . '/form_display.php' ); + + $this->log_debug( __METHOD__ . '() Running.' ); + + $partial_entry_id = sanitize_key( rgpost( 'partial_entry_id' ) ); + + $form = GFAPI::get_form( $form_id ); + + // Ensure that the honeypot field id is calculated correctly when the gform_review_page filter has been used to add the extra page and html fields. + $form = GFFormDisplay::maybe_add_review_page( $form ); + + $form = apply_filters( 'gform_pre_render', $form, false, array() ); + $form = apply_filters( 'gform_pre_render_' . $form['id'], $form, false, array() ); + + // create lead + // Save uuid with entry + + $failed_honeypot = false; + + // Get the honeypot field if enabled and check if it's empty. + if ( rgar( $form, 'enableHoneypot' ) ) { + $honeypot_id = GFFormDisplay::get_max_field_id( $form ) + 1; + $failed_honeypot = ! rgempty( "input_{$honeypot_id}" ); + } + + // If honeypot contains a value, don't save partial entry. + if ( $failed_honeypot ) { + $this->log_debug( __METHOD__ . '() Aborting: Honeypot Validation Failed' ); + + return false; + } + + $partial_entry = $this->create_partial_entry( $form ); + + if ( empty( $partial_entry ) ) { + return false; + } + + if ( $partial_entry['partial_entry_percent'] == 0 ) { + $this->log_debug( __METHOD__ . '() Aborting; 0% completed.' ); + + return false; + } + + $notification_event = 'partial_entry_saved'; + + if ( empty( $partial_entry_id ) || $partial_entry_id == 'pending' ) { + $partial_entry_id = GFFormsModel::get_uuid(); + $partial_entry['partial_entry_id'] = $partial_entry_id; + $result = GFAPI::add_entry( $partial_entry ); + + if ( is_wp_error( $result ) ) { + $message = 'Error: ' . $result->get_error_message(); + } else { + $partial_entry['id'] = $result; + $message = "Success. partial_entry_id = {$partial_entry_id}. Entry ID = {$result}."; + } + $this->log_debug( __METHOD__ . '(): Saving new partial entry. ' . $message ); + } else { + + $search_criteria = array( + 'status' => 'active', + 'field_filters' => array( + array( 'key' => 'partial_entry_id', 'value' => $partial_entry_id ), + ), + ); + + $entries = GFAPI::get_entries( $form_id, $search_criteria ); + + $partial_entry['partial_entry_id'] = $partial_entry_id; + + if ( empty( $entries ) ) { + $this->log_debug( __METHOD__ . '(): Partial entry not found.' ); + + //$partial_entry_id = GFFormsModel::get_uuid(); + $result = GFAPI::add_entry( $partial_entry ); + + if ( is_wp_error( $result ) ) { + $message = 'Error: ' . $result->get_error_message(); + } else { + $partial_entry['id'] = $result; + $message = "Success. partial_entry_id = {$partial_entry_id}. Entry ID = {$result}."; + } + $this->log_debug( __METHOD__ . '(): Saving new partial entry. ' . $message ); + } else { + $notification_event = 'partial_entry_updated'; + $saved_entry = $entries[0]; + $partial_entry['id'] = $saved_entry['id']; + $partial_entry = $this->filter_partial_entry_before_update( $partial_entry, $saved_entry, $form ); + $result = GFAPI::update_entry( $partial_entry ); + + if ( is_wp_error( $result ) ) { + $message = 'Error: ' . $result->get_error_message(); + } else { + $message = "Success. Entry ID = {$partial_entry['id']}."; + } + $this->log_debug( __METHOD__ . '(): Updating existing partial entry. ' . $message ); + } + } + + if ( ! is_wp_error( $result ) && $result ) { + GFAPI::send_notifications( $form, $partial_entry, $notification_event ); + + $event = str_replace( 'partial_', '', $notification_event ); + + /** + * Perform a custom action when the partial entry has been saved or updated. + * + * The dynamic part of the hook name contains the event which occurred; entry_saved or entry_updated. + * + * @param array $partial_entry The partial entry object. + * @param array $form The form object used to process this partial entry. + * + * @see https://www.gravityhelp.com/documentation/article/gform_partialentries_post_event/ + */ + do_action( "gform_partialentries_post_{$event}", $partial_entry, $form ); + } + + return $partial_entry_id; + } + + /** + * Allow overriding the partial entry values before updating. + * + * @since 1.6.1 + * + * @param array $partial_entry The partial entry array that contains new values. + * @param array $saved_entry The original entry. + * @param array $form The current form being processed. + * + * @return array The filtered partial entry array. + */ + public function filter_partial_entry_before_update( $partial_entry, $saved_entry, $form ) { + /** + * Filters partial entry values before update. + * + * @since 1.6.1 + * + * @param array $partial_entry The partial entry array that contains new values. + * @param array $saved_entry The original entry. + * @param array $form The current form being processed. + */ + return gf_apply_filters( array( 'gform_partialentries_pre_update', $form['id'] ), $partial_entry, $saved_entry, $form ); + } + + /** + * Creates a partial entry - fields that fail validation will return empty values. + * + * @param $form + * + * @return array|false The partial entry + */ + public function create_partial_entry( $form ) { + + $form_id = absint( $form['id'] ); + + $feed_settings = $this->get_feed_settings( $form_id ); + $enabled = rgar( $feed_settings, 'enable' ); + if ( ! $enabled ) { + $this->log_debug( __METHOD__ . '() Aborting; Not enabled.' ); + + return false; + } + + $fields_completed = 0; + $required_fields_completed = 0; + $total_fields = 0; + $required_fields = 0; + + $unset_keys = array(); + $ignore_types = array( + 'fileupload', + 'creditcard', + ); + + foreach ( $form['fields'] as $key => $field ) { + /* @var GF_Field $field */ + if ( $field->type === 'page' || $field->type === 'section' ) { + // Keeping the field so the logic can be evaluated but omitting it from the counts. + continue; + } elseif ( $field->displayOnly || in_array( $field->get_input_type(), $ignore_types ) ) { + $unset_keys[] = $key; + } else { + $total_fields ++; + if ( $field->isRequired ) { + $required_fields ++; + } + if ( $field->noDuplicates ) { + $field->noDuplicates = false; + } + } + } + + if ( $total_fields == 0 ) { + $this->log_debug( __METHOD__ . '() Aborting; No applicable fields to save.' ); + + return false; + } + + foreach ( $unset_keys as $key ) { + unset( $form['fields'][ $key ] ); + } + + $form['fields'] = array_values( $form['fields'] ); + + if ( rgpost( 'action' ) == 'heartbeat' ) { + $this->log_debug( __METHOD__ . '(): is heartbeat; running GFFormDisplay::validate().' ); + $page_number = GFFormDisplay::get_source_page( $form['id'] ); + GFFormDisplay::validate( $form, array(), $page_number, $page_number ); + } + + $entry = GFFormsModel::create_lead( $form ); + + foreach ( $form['fields'] as $field ) { + + if ( $field->displayOnly || $field->isRequired && $field->is_value_submission_empty( $form_id ) ) { + continue; + } + /* @var GF_Field $field */ + $inputs = in_array( $field->get_input_type(), array( + 'date', + 'time', + ) ) ? $field->inputs : $field->get_entry_inputs(); + if ( $field->failed_validation ) { + if ( is_array( $inputs ) ) { + foreach ( $inputs as $input ) { + if ( isset( $entry[ $input['id'] ] ) ) { + $entry[ $input['id'] ] = ''; + } + } + } else { + if ( isset( $entry[ (string) $field->id ] ) ) { + $entry[ (string) $field->id ] = ''; + } + } + } else { + if ( is_array( $inputs ) ) { + foreach ( $inputs as $input ) { + $value = GFFormsModel::get_prepared_input_value( $form, $field, $entry, $input['id'] ); + if ( ! empty( $value ) ) { + $fields_completed ++; + if ( $field->isRequired ) { + $required_fields_completed ++; + } + continue 2; + } + } + } else { + if ( isset( $entry[ (string) $field->id ] ) && ! empty( $entry[ (string) $field->id ] ) ) { + $fields_completed ++; + if ( $field->isRequired ) { + $required_fields_completed ++; + } + } + } + } + } + + $entry['required_fields_percent_complete'] = ( $required_fields > 0 ) ? (int) ( $required_fields_completed / $required_fields * 100 ) : 0; + $entry['partial_entry_percent'] = (int) ( $fields_completed / $total_fields * 100 ); + + $feed_id = $this->get_default_feed_id( $form_id ); + $feed = $this->get_feed( $feed_id ); + + if ( ! $this->is_feed_condition_met( $feed, $form, $entry ) ) { + $this->log_debug( __METHOD__ . '() Aborting; Condition not met.' ); + + return false; + } + + if ( rgars( $form, 'save/enabled' ) && $resume_token = rgpost( 'gform_resume_token' ) ) { + $entry['resume_token'] = $resume_token; + $saved_entry_details = GFFormsModel::get_draft_submission_values( $resume_token ); + $source_url = $saved_entry_details['source_url']; + $source_url = add_query_arg( array( 'gf_token' => $resume_token ), $source_url ); + $resume_url = esc_url_raw( $source_url ); + $entry['resume_url'] = $resume_url; + $entry['date_saved'] = $saved_entry_details['date_created']; + } + + $entry_meta = GFFormsModel::get_entry_meta( $form_id ); + if ( is_array( $entry_meta ) ) { + $keys_to_skip = array( 'required_fields_percent_complete', 'partial_entry_percent' ); + foreach ( $entry_meta as $key => $item ) { + if ( ! in_array( $key, $keys_to_skip ) && isset( $item['update_entry_meta_callback'] ) ) { + $entry[ $key ] = call_user_func_array( $item['update_entry_meta_callback'], array( + $key, + $entry, + $form + ) ); + } + } + } + + return $entry; + + } + + /** + * Target for the gform_entries_field_value filter. Returns the value for the entry list. + * + * @param $value + * @param $form_id + * @param $field_id + * @param $entry + * + * @return string + */ + public function filter_gform_entries_field_value( $value, $form_id, $field_id, $entry ) { + + switch ( $field_id ) { + case 'partial_entry_percent' : + case 'required_fields_percent_complete' : + $partial_entry_id = rgar( $entry, 'partial_entry_id' ); + if ( empty( $partial_entry_id ) ) { + $html = ''; + } else { + $html = sprintf( '
', $value, $entry['id'], $value ); + } + break; + case 'date_saved' : + if ( empty( $value ) ) { + $html = $value; + + } else { + $date = GFCommon::format_date( $value ); + $link = empty( $entry['resume_url'] ) ? '' : sprintf( '', rgar( $entry, 'resume_url' ), esc_html__( 'Open saved form', 'gravityforms' ) ); + $html = $link . ' ' . $date; + } + + break; + default : + $html = $value; + + } + + return $html; + } + + /** + * Target for the gform_entry_id_pre_save_lead filter. If a partial entry exists use its ID and delete partial entry meta. + * + * @param $entry_id + * @param $form + * + * @return int + */ + public function filter_gform_entry_id_pre_save_lead( $entry_id, $form ) { + $partial_entry_id = sanitize_key( rgpost( 'partial_entry_id' ) ); + + if ( ! empty( $partial_entry_id ) ) { + $search_criteria = array( + 'status' => 'active', + 'field_filters' => array( + array( 'key' => 'partial_entry_id', 'value' => $partial_entry_id ), + ), + ); + + $entries = GFAPI::get_entries( $form['id'], $search_criteria ); + if ( count( $entries ) > 0 ) { + $entry = $entries[0]; + $entry_id = absint( $entry['id'] ); + $result = GFAPI::update_entry( array( 'id' => $entry_id, 'currency' => $entry['currency'] ) ); + $this->log_debug( __METHOD__ . '() update result: ' . print_r( $result, true ) ); + gform_delete_meta( $entry_id, 'partial_entry_id' ); + gform_delete_meta( $entry_id, 'date_saved' ); + gform_delete_meta( $entry_id, 'resume_url' ); + gform_delete_meta( $entry_id, 'resume_token' ); + + // Flush the cached field display states so conditional logic is re-evaluated using the posted values. + GFCache::flush(); + add_filter( 'gform_use_post_value_for_conditional_logic_save_entry', '__return_true' ); + } + } + + return $entry_id; + } + + /** + * Target for the heartbeat_received filter. Handles the transport of the partial entry data. + * + * @param $response + * @param $data + * + * @return mixed + */ + public function filter_heartbeat_received( $response, $data ) { + if ( isset( $data['gf-partial_entries-heartbeat'] ) ) { + $orginal_post = $_POST; + $forms_values = json_decode( $data['gf-partial_entries-heartbeat'], true ); + $response['gf-partial-entries-ids'] = array(); + foreach ( $forms_values as $form_id => $form_values ) { + $post_values = array(); + foreach ( $form_values as $form_value ) { + $key = $form_value['name']; + if ( strpos( $key, '.' ) !== false ) { + $key = str_replace( '.', '_', $key ); + } + if ( strpos( $key, '[]' ) !== false ) { + $key = str_replace( '[]', '', $key ); + if ( ! isset( $post_values[ $key ] ) ) { + $post_values[ $key ] = array(); + } + $post_values[ $key ][] = $form_value['value']; + } else { + $post_values[ $key ] = $form_value['value']; + } + } + $_POST = array_merge_recursive( $orginal_post, $post_values ); + $partial_entry_id = $this->maybe_save_partial_entry( $form_id ); + if ( ! empty( $partial_entry_id ) ) { + $response['gf-partial-entries-ids'][ $form_id ] = $partial_entry_id; + } + } + } + + return $response; + } + + /** + * Target for the gform_entry_info action. Displays the progress information on the entry detail page. + * + * @param $form_id + * @param $entry + */ + public function action_gform_entry_info( $form_id, $entry ) { + if ( ! $this->is_enabled( $form_id ) ) { + return; + } + + $partial_entry_id = rgar( $entry, 'partial_entry_id' ); + if ( empty( $partial_entry_id ) ) { + $progress = ''; + } else { + $progress = sprintf( '
', $entry['partial_entry_percent'], $entry['id'], $entry['partial_entry_percent'] ); + } + printf( esc_html__( 'Progress: %s', 'gravityformspartialentries' ), $progress ); + + $date_saved = rgar( $entry, 'date_saved' ); + + if ( ! empty( $date_saved ) ) { + $date = GFCommon::format_date( $date_saved ); + $link = empty( $entry['resume_url'] ) ? '' : sprintf( '', rgar( $entry, 'resume_url' ), esc_html__( 'Open saved form', 'gravityforms' ) ); + echo '
' . sprintf( esc_html__( 'Last Saved: %s ', 'gravityformspartialentries' ), $date ) . $link . '
'; + } + } + + /** + * Target for the gform_form_tag filter. Adds the partial entry ID as a hidden field. + * + * @param $form_tag + * @param $form + * + * @return string + */ + public function filter_gform_form_tag( $form_tag, $form ) { + $feed_settings = $this->get_feed_settings( $form['id'] ); + if ( rgar( $feed_settings, 'enable' ) ) { + if ( $token = rgget( 'gf_token' ) ) { + $search_criteria = array( + 'field_filters' => array( + array( 'key' => 'resume_token', 'value' => $token ), + array( 'key' => 'partial_entry_id', 'operator' => 'isnot', 'value' => '' ) + ), + ); + $entries = GFAPI::get_entries( $form['id'], $search_criteria ); + + $partial_entry_id = isset( $entries[0] ) ? $entries[0]['partial_entry_id'] : 'pending'; + } else { + $partial_entry_id = ! empty( $_POST['partial_entry_id'] ) ? $_POST['partial_entry_id'] : 'pending'; + } + + $partial_entry_id = esc_attr( $partial_entry_id ); + $form_id = absint( $form['id'] ); + $form_tag .= sprintf( '', $form_id, esc_attr( $partial_entry_id ), $form_id ); + } + + return $form_tag; + } + + /** + * Target for the gform_filter_links_entry_list filter. Adds the config for the filters on the entry list. + * + * @param $filter_links + * @param $form + * @param $include_counts + * + * @return mixed + */ + public function filter_gform_filter_links_entry_list( $filter_links, $form, $include_counts ) { + if ( ! $this->is_enabled( $form['id'] ) ) { + return $filter_links; + } + + $form_id = absint( $form['id'] ); + + $complete_count = 0; + $partial_count = 0; + + if ( $include_counts ) { + global $wpdb; + $lead_table_name = self::get_entry_table_name(); + $lead_detail_meta_name = self::get_entry_meta_table_name(); + + $entry_id_column = version_compare( self::get_gravityforms_db_version(), '2.3-dev-1', '<' ) ? 'lead_id' : 'entry_id'; + + $sql = $wpdb->prepare( + "SELECT + (SELECT count(DISTINCT(l.id)) FROM $lead_table_name l LEFT JOIN $lead_detail_meta_name m ON l.id=m.{$entry_id_column} AND m.meta_key = 'partial_entry_percent' WHERE l.form_id=%d AND l.status='active' AND ( m.meta_value = '' OR m.meta_value IS NULL ) ) as complete, + (SELECT count(DISTINCT(l.id)) FROM $lead_table_name l INNER JOIN $lead_detail_meta_name m ON l.id=m.{$entry_id_column} WHERE l.form_id=%d AND l.status='active' AND m.meta_key = 'partial_entry_percent' AND m.meta_value > 1 ) as partial + ", + $form_id, $form_id + ); + $results = $wpdb->get_results( $sql, ARRAY_A ); + $complete_count = $results[0]['complete']; + $partial_count = $results[0]['partial']; + } + + $new_filter_links = array( + array( + 'id' => 'complete', + 'count' => $complete_count, + 'label' => esc_html_x( 'Complete', 'Entry List', 'gravityforms' ), + 'field_filters' => array( + array( + 'key' => 'partial_entry_percent', + 'value' => '', + ), + ), + ), + array( + 'id' => 'partial', + 'count' => $partial_count, + 'label' => esc_html_x( 'Partial', 'Entry List', 'gravityforms' ), + 'field_filters' => array( + array( + 'key' => 'partial_entry_percent', + 'operator' => '>', + 'value' => 1, + ), + ), + ), + ); + + array_splice( $filter_links, 1, 0, $new_filter_links ); + + return $filter_links; + } + + /** + * Is Partial Entries enabled for the given form? + * + * @param $form_id + * + * @return bool + */ + public function is_enabled( $form_id ) { + $feed_settings = $this->get_feed_settings( $form_id ); + + return (bool) rgar( $feed_settings, 'enable' ); + } + + /** + * Displays the screen options on the entry list for Gravity Forms < 2.0 + * + * @param $status + * @param $args + * + * @return string + */ + public function show_screen_options( $status, $args ) { + + $return = $status; + if ( $args->base == 'forms_page_gf_entries' ) { + + if ( ! $this->is_entry_list() ) { + return $return; + } + + $screen_options = $this->get_screen_options(); + + $per_page = $screen_options['per_page']; + + $filters = $this->get_filter_links(); + + $selected_filter = $this->get_default_filter(); + + $radios_arr = array(); + foreach ( $filters as $filter ) { + $id = esc_attr( $filter['id'] ); + $label = esc_attr( $filter['label'] ); + $checked = checked( $filter['id'], $selected_filter, false ); + $radios_arr[] = sprintf( '', $id, $id, $checked, $id, $label ); + } + + $radios_str = join( "\n", $radios_arr ); + + $filter_title = esc_html__( 'Default Filter', 'gravityformspartialentries' ); + $entries_label = esc_html__( 'Number of entries per page:', 'gravityformspartialentries' ); + + $button = get_submit_button( esc_html__( 'Apply', 'gravityformspartialentries' ), 'button', 'screen-options-apply', false ); + $return .= " +
{$filter_title}
+
+
+ {$radios_str} +
+
+
+ + + + +
+ +
+ $button"; + } + + return $return; + + } + + /** + * Sets the screen options for the entry list on Gravity Forms < 2.0 + * + * @param $status + * @param $option + * @param $value + * + * @return array + */ + function set_screen_options( $status, $option, $value ) { + $return = $value; + if ( 'gform_entries_screen_options' == $option ) { + $return = array(); + $return['default_filter'] = $_POST['gform_default_filter']; + $return['per_page'] = $_POST['gform_per_page']; + } + + return $return; + } + + /** + * Target for the gform_entry_page_size filter. + * Required for the entry list on Gravity Forms < 2.0 + * + * @param $per_page + * + * @return mixed + */ + public function filter_gform_entry_page_size( $per_page ) { + + $option_values = $this->get_screen_options(); + + return $option_values['per_page']; + } + + /** + * Returns the filter links for the entry list. Required for Gravity Forms < 2.0. + * + * @return array|mixed|string|void + */ + public function get_filter_links() { + $forms = RGFormsModel::get_forms( null, 'title' ); + $form_id = rgget( 'id' ); + + if ( sizeof( $forms ) == 0 ) { + return ''; + } else { + if ( empty( $form_id ) ) { + $form_id = $forms[0]->id; + } + } + + $form = GFAPI::get_form( $form_id ); + + require_once( GFCommon::get_base_path() . '/entry_list.php' ); + + $filters = GFEntryList::get_filter_links( $form, false ); + + return $filters; + } + + /** + * Returns the default filter. Required for Gravity Forms < 2.0. + * + * @return string + */ + public function get_default_filter() { + + $filters = $this->get_filter_links(); + + $option_values = $this->get_screen_options(); + + // If the filter is not available for the form then use 'all' + $selected_filter = 'all'; + foreach ( $filters as $filter ) { + if ( $option_values['default_filter'] == $filter['id'] ) { + $selected_filter = $option_values['default_filter']; + break; + } + } + + return $selected_filter; + } + + /** + * Returns the screen options for the entry list. Required for Gravity Forms < 2.0. + * + * @return array|mixed + */ + public function get_screen_options() { + $default_values = array( + 'per_page' => 20, + 'default_filter' => 'all', + ); + + $option_values = get_user_option( 'gform_entries_screen_options' ); + + if ( empty( $option_values ) ) { + $option_values = array(); + } + $option_values = array_merge( $default_values, $option_values ); + + return $option_values; + + } + + /** + * Returns the feed settings. + * + * @param $form_id + * + * @return array + */ + public function get_feed_settings( $form_id ) { + $feed_id = $this->get_default_feed_id( $form_id ); + if ( ! $feed_id ) { + return array(); + } + + $feed = $this->get_feed( $feed_id ); + + $feed_settings = $feed['meta']; + + return $feed_settings; + } + + /** + * A dedicated feed condition setting which includes some custom values as entry meta. + * + * @param $field + * @param bool $echo + * + * @return string + */ + public function settings_feed_condition( $field, $echo = true ) { + + $checkbox_label = isset( $field['checkbox_label'] ) ? $field['checkbox_label'] : esc_html__( 'Enable Condition', 'gravityforms' ); + + $checkbox_field = array( + 'name' => 'feed_condition_conditional_logic', + 'type' => 'checkbox', + 'choices' => array( + array( + 'label' => $checkbox_label, + 'name' => 'feed_condition_conditional_logic', + ), + ), + 'onclick' => 'ToggleConditionalLogic( false, "feed_condition" );', + ); + $conditional_logic_object = $this->get_setting( 'feed_condition_conditional_logic_object' ); + $form_id = rgget( 'id' ); + $form = GFFormsModel::get_form_meta( $form_id ); + if ( $conditional_logic_object ) { + $conditional_logic = json_encode( GFFormsModel::trim_conditional_logic_values_from_element( $conditional_logic_object, $form ) ); + } else { + $conditional_logic = '{}'; + } + + $hidden_field = array( + 'name' => 'feed_condition_conditional_logic_object', + 'value' => $conditional_logic, + ); + $instructions = isset( $field['instructions'] ) ? $field['instructions'] : esc_html__( 'Process this feed if', 'gravityforms' ); + $html = $this->settings_checkbox( $checkbox_field, false ); + $html .= $this->settings_hidden( $hidden_field, false ); + $html .= '
'; + + $percent_choices = array(); + for ( $i = 10; $i <= 100; $i += 10 ) { + $percent_choices[] = array( 'text' => $i . '%', 'value' => $i ); + } + + $entry_meta['partial_entry_percent'] = array( + 'label' => __( 'Progress: all fields', 'gravityflow' ), + 'filter' => array( + 'operators' => array( '>', 'is' ), + 'choices' => $percent_choices, + ), + ); + + $entry_meta['required_fields_percent_complete'] = array( + 'label' => esc_html__( 'Progress: required fields', 'gravityflow' ), + 'filter' => array( + 'operators' => array( '>', 'is' ), + 'choices' => $percent_choices, + ), + ); + + $html .= ''; + + if ( $echo ) { + echo $html; + } + + return $html; + } + + /** + * Target for the gform_post_process action. Saves a partial entry if conditions are met. + * + * @param $form + * @param $page_number + * @param $source_page_number + */ + function action_gform_post_process( $form, $page_number, $source_page_number ) { + $submission_info = GFFormDisplay::$submission[ $form['id'] ]; + + $saved_for_later = rgar( $submission_info, 'saved_for_later' ); + + // Save partial entry on validation failure, save (and continue) and page change - not on submission. + if ( ! rgar( $submission_info, 'is_valid' ) || $saved_for_later || $page_number > 0 ) { + + if ( $saved_for_later ) { + $_POST['gform_resume_token'] = rgar( $submission_info, 'resume_token' ); + } + + $partial_entry_id = $this->maybe_save_partial_entry( $form['id'] ); + + if ( ! empty( $partial_entry_id ) ) { + $_POST['partial_entry_id'] = $partial_entry_id; + } + } + } + + /** + * Returns the default warning message to display above the form. + * + * @return string + */ + function get_default_warning_message() { + return esc_html__( 'Please note that your information is saved on our server as you enter it.', 'gravityformspartialentries' ); + } + + /** + * Custom setting to display warning messages if certain field types exist on the form. + * + * @param $field + */ + function settings_fields_message( $field ) { + $form_id = rgget( 'id' ); + $form = GFAPI::get_form( $form_id ); + $fields = $form['fields']; + if ( empty( $fields ) || ! is_array( $fields ) ) { + return; + } + $display_fileupload_warning = $display_creditcard_warning = false; + /* @var GF_Field[] $fields */ + foreach ( $fields as $field ) { + $input_type = $field->get_input_type(); + if ( $input_type == 'fileupload' ) { + $display_fileupload_warning = true; + } + if ( $input_type == 'creditcard' ) { + $display_creditcard_warning = true; + } + } + + if ( $display_fileupload_warning ) { + ?> +
+ +
+ +
+ +
+ is_enabled( $form['id'] ) ) { + return false; + } + + return array( + 'partial_entry_saved' => esc_html__( 'Partial Entries: Saved', 'gravityformspartialentries' ), + 'partial_entry_updated' => esc_html__( 'Partial Entries: Updated', 'gravityformspartialentries' ), + ); + } + + /** + * Callback for the gform_is_duplicate filter. + * + * @param $is_duplicate + * @param $form_id + * @param $field + * @param $value + * + * @return bool + */ + public function filter_gform_is_duplicate( $is_duplicate, $form_id, $field, $value ) { + + if ( empty( $_POST['partial_entry_id'] ) ) { + return $is_duplicate; + } + + if ( ! $this->is_enabled( $form_id ) ) { + return $is_duplicate; + } + + + // Lookup entry id + + switch ( GFFormsModel::get_input_type( $field ) ) { + case 'time': + $value = sprintf( "%02d:%02d %s", $value[0], $value[1], $value[2] ); + break; + case 'date': + $value = GFFormsModel::prepare_date( $field->dateFormat, $value ); + break; + case 'number': + $value = GFCommon::clean_number( $value, $field->numberFormat ); + break; + case 'phone': + $value = str_replace( array( ')', '(', '-', ' ' ), '', $value ); + break; + case 'email': + $value = is_array( $value ) ? rgar( $value, 0 ) : $value; + break; + } + + $search_criteria = array( + 'status' => 'active', + 'field_filters' => array( + array( + 'key' => $field->id, + 'operator' => '=', + 'value' => $value, + ), + $search_criteria['field_filters'][] = array( + 'key' => 'partial_entry_percent', + 'operator' => '=', + 'value' => '', + ), + ), + ); + + $entries = GFAPI::get_entries( $form_id, $search_criteria ); + + if ( empty( $entries ) ) { + return false; + } + + return $is_duplicate; + } + + /** + * Returns the table name for the entry headers. + * + * @return string + * @since 1.0.8 + * + */ + public static function get_entry_table_name() { + return version_compare( self::get_gravityforms_db_version(), '2.3-dev-1', '<' ) ? GFFormsModel::get_lead_table_name() : GFFormsModel::get_entry_table_name(); + } + + /** + * Returns the table name for the entry meta. + * + * @return string + * @since 1.0.8 + * + */ + public static function get_entry_meta_table_name() { + return version_compare( self::get_gravityforms_db_version(), '2.3-dev-1', '<' ) ? GFFormsModel::get_lead_meta_table_name() : GFFormsModel::get_entry_meta_table_name(); + } + + /** + * Returns the current version of the Gravity Forms database. + * + * @return string + * @since 1.0.8 + * + */ + public static function get_gravityforms_db_version() { + + if ( method_exists( 'GFFormsModel', 'get_database_version' ) ) { + $db_version = GFFormsModel::get_database_version(); + } else { + $db_version = GFForms::$version; + } + + return $db_version; + } +} diff --git a/images/menu-icon.svg b/images/menu-icon.svg new file mode 100644 index 0000000..da4f89e --- /dev/null +++ b/images/menu-icon.svg @@ -0,0 +1 @@ +partial entries \ No newline at end of file diff --git a/js/admin.js b/js/admin.js new file mode 100644 index 0000000..9c3021a --- /dev/null +++ b/js/admin.js @@ -0,0 +1,38 @@ +(function (GF_Partial_Submissions_Admin, $) { + + "use strict"; + + GF_Partial_Submissions_Admin.drawCharts = function() { + + $('.gform_partial_entry_percent').each(function () { + var percent = $(this).data('percentage'); + var data = google.visualization.arrayToDataTable([ + ['Complete', 'Percentage'], + ['Complete', percent], + ['Incomplete', 100 - percent], + ]); + + var options = { + legend: 'none', + slices: { + 0: { color: '#888888' }, + 1: { color: 'transparent' } + }, + pieSliceText: 'none', + tooltip: { trigger: 'none' }, + backgroundColor: 'transparent', + pieSliceBorderColor: '#AAAAAA', + enableInteractivity: false + }; + + var chart = new google.visualization.PieChart( this ); + + chart.draw(data, options); + }) + } + +}(window.GF_Partial_Submissions_Admin = window.GF_Partial_Submissions_Admin || {}, jQuery)); + + +google.load("visualization", "1", {packages:["corechart"]}); +google.setOnLoadCallback(GF_Partial_Submissions_Admin.drawCharts); \ No newline at end of file diff --git a/js/admin.min.js b/js/admin.min.js new file mode 100644 index 0000000..a448f31 --- /dev/null +++ b/js/admin.min.js @@ -0,0 +1 @@ +!function(e,a){"use strict";e.drawCharts=function(){a(".gform_partial_entry_percent").each(function(){var e=a(this).data("percentage"),e=google.visualization.arrayToDataTable([["Complete","Percentage"],["Complete",e],["Incomplete",100-e]]);new google.visualization.PieChart(this).draw(e,{legend:"none",slices:{0:{color:"#888888"},1:{color:"transparent"}},pieSliceText:"none",tooltip:{trigger:"none"},backgroundColor:"transparent",pieSliceBorderColor:"#AAAAAA",enableInteractivity:!1})})}}(window.GF_Partial_Submissions_Admin=window.GF_Partial_Submissions_Admin||{},jQuery),google.load("visualization","1",{packages:["corechart"]}),google.setOnLoadCallback(GF_Partial_Submissions_Admin.drawCharts); \ No newline at end of file diff --git a/js/feed-settings.js b/js/feed-settings.js new file mode 100644 index 0000000..91e1fc7 --- /dev/null +++ b/js/feed-settings.js @@ -0,0 +1,36 @@ +(function (GF_Partial_Submissions_Feed_Settings, $) { + + "use strict"; + + var strings = gf_partial_entries_feed_settings_strings; + + $(document).ready(function(){ + gform.addFilter( 'gform_conditional_logic_fields', function( options, form, selectedFieldId ){ + var partialEntriesOptions = [], fieldOptions, newOptions = []; + fieldOptions = $.grep(options, function(option) { + return option.value != 'partial_entry_percent' && option.value != 'required_fields_percent_complete'; + }); + + partialEntriesOptions.push( { + label: strings.allFields, + value: 'partial_entry_percent', + isSelected: selectedFieldId == 'partial_entry_percent' ? "selected='selected'" : "" + } ); + partialEntriesOptions.push( { + label: strings.requiredFields, + value: 'required_fields_percent_complete', + isSelected: selectedFieldId == 'required_fields_percent_complete' ? "selected='selected'" : "" + } ); + newOptions.push({ + 'options' : fieldOptions, + 'label' : strings.fields + }); + newOptions.push({ + 'options' : partialEntriesOptions, + 'label' : strings.progress + }); + return newOptions; + }) + }); + +}(window.GF_Partial_Submissions_Feed_Settings = window.GF_Partial_Submissions_Feed_Settings || {}, jQuery)); diff --git a/js/feed-settings.min.js b/js/feed-settings.min.js new file mode 100644 index 0000000..592e4d6 --- /dev/null +++ b/js/feed-settings.min.js @@ -0,0 +1 @@ +!function(s){"use strict";var n=gf_partial_entries_feed_settings_strings;s(document).ready(function(){gform.addFilter("gform_conditional_logic_fields",function(e,t,i){var r=[],l=[],e=s.grep(e,function(e){return"partial_entry_percent"!=e.value&&"required_fields_percent_complete"!=e.value});return r.push({label:n.allFields,value:"partial_entry_percent",isSelected:"partial_entry_percent"==i?"selected='selected'":""}),r.push({label:n.requiredFields,value:"required_fields_percent_complete",isSelected:"required_fields_percent_complete"==i?"selected='selected'":""}),l.push({options:e,label:n.fields}),l.push({options:r,label:n.progress}),l})})}((window.GF_Partial_Submissions_Feed_Settings=window.GF_Partial_Submissions_Feed_Settings||{},jQuery)); \ No newline at end of file diff --git a/js/partial-entries.js b/js/partial-entries.js new file mode 100644 index 0000000..3474145 --- /dev/null +++ b/js/partial-entries.js @@ -0,0 +1,80 @@ +(function (GF_Partial_Entries, $) { + + "use strict"; + $(document).ready(function(){ + $('.partial_entry_id').each( function(el) { + var $this = $(this), + formId = $this.data('form_id'), + $heading = $('#gform_wrapper_' + formId + ' .gform_heading'), + strings = typeof window['gf_partial_entries_strings_' + formId] !== 'undefined' ? window['gf_partial_entries_strings_' + formId] : {}, + warning = typeof strings.warningMessage !== 'undefined' ? strings.warningMessage : '', + notice = warning ? '
' + warning + '
' : '', + $anchorElement = $heading.length > 0 ? $heading : $this; + if ( notice ) { + $anchorElement. after( notice ); + } + + }); + }); + + $(document).on('heartbeat-send', function(e, data) { + + var $forms = $('.gform_wrapper form'); + + if ( $forms.length == 0 ) { + return; + } + var formsData = {}; + $forms.each( function( i ) { + var formCopy = $(this).clone(), formData; + var $gformSubmit = formCopy.find('input[name=gform_submit]'); + var formId = $gformSubmit.val(); + $gformSubmit.remove(); + formData = formCopy.serializeArray(); + + formsData[formId] = formData; + }); + + var formsJson = JSON.stringify( formsData ); + + data['gf-partial_entries-heartbeat'] = formsJson; + }); + + + $(document).on( 'heartbeat-tick', function(e, data) { + + if ( ! data['gf-partial-entries-ids'] ) { + return; + } + + $.each( data['gf-partial-entries-ids'], function ( formId, entryId ) { + $('#partial_entry_id_' + formId ).val( entryId ); + }); + + }); + + // Textarea and select clone() bug workaround | Spencer Tipping + // Licensed under the terms of the MIT source code license + // Source: https://raw.githubusercontent.com/spencertipping/jquery.fix.clone/master/jquery.fix.clone.js + (function (original) { + jQuery.fn.clone = function () { + var result = original.apply(this, arguments), + my_textareas = this.find('textarea').add(this.filter('textarea')), + result_textareas = result.find('textarea').add(result.filter('textarea')), + my_selects = this.find('select').add(this.filter('select')), + result_selects = result.find('select').add(result.filter('select')); + + for (var i = 0, l = my_textareas.length; i < l; ++i) $(result_textareas[i]).val($(my_textareas[i]).val()); + for (var i = 0, l = my_selects.length; i < l; ++i) { + for (var j = 0, m = my_selects[i].options.length; j < m; ++j) { + if (my_selects[i].options[j].selected === true) { + result_selects[i].options[j].selected = true; + } + } + } + return result; + }; + }) (jQuery.fn.clone); + + +}(window.GF_Partial_Entries = window.GF_Partial_Entries || {}, jQuery)); diff --git a/js/partial-entries.min.js b/js/partial-entries.min.js new file mode 100644 index 0000000..0e21280 --- /dev/null +++ b/js/partial-entries.min.js @@ -0,0 +1 @@ +!function(d){"use strict";var f;d(document).ready(function(){d(".partial_entry_id").each(function(t){var e=d(this),i=e.data("form_id"),a=d("#gform_wrapper_"+i+" .gform_heading"),i=void 0!==window["gf_partial_entries_strings_"+i]?window["gf_partial_entries_strings_"+i]:{},i=void 0!==i.warningMessage?i.warningMessage:"",i=i?'
'+i+"
":"",e=0hT@%0>Kf_5f1SNH{YUt zh4_WM)_) zpdv@c&KwzX3ku!(8my9z9Hc2d(;#xu`wJKcz=;ea#aKEz~2p(*DrzfZVb@q`T zdRQ)y8ZTKDk`a#TC59TPF-)=B7Ij*e(&y{|rPA}#G}VN#_tMml2!-tdf`%Ntdf zm44=cvD#tOq2VrV?~$<0qUc?S<31xs1owt{X?aeY>NBE?X6m}_m@H{`LzA~%5^cub z6+M<(+GQ12wcGLc{5Gx(jl`DIL+)h3VakZ_2M=Upu4mj{J4V)G&y?8YijhmL02^`v zcxSg>|01iov8fv<2aF_1oX9v?uxP6-klqnp3vr^G`PL3D=|me-+{yQot!)uYClU@| zF<4EfFw0l8QRP})(nejKbnmd5uP=HbxfRnPrcFak{K0}Lt&qKC6B#LCf?d4AA87%p jnPG?N#quX*q{7TwgTH_9v2Y^l_amD3(>f5KZg}fI4sa&z literal 0 HcmV?d00001 diff --git a/languages/gravityformspartialentries-da_DK.mo b/languages/gravityformspartialentries-da_DK.mo new file mode 100644 index 0000000000000000000000000000000000000000..e90fbfeb7ce378297cba21691310a48121c136a6 GIT binary patch literal 3008 zcmb7FO>AVv5pKdJfXNU4;Lm}7 z1pWs29+36@3-~zjD)3R@e}Qc0H6Y*r3iw;#GK75oA~3^S1K?9w{}syb2mTw#etg)h zKL)aYp8)Rxe$nh-2fmE;H^Ae-7w-c%KndjYUjqLC{0_+WJPGpH-$mdPzz&e*W8g*L zpMk9Zb0FJs4an!e20jFQ6hc0K68JFiB_Lc9TfhszH-Ioj{1eFjy$|FNvW{Qi$K3E> z?s)%TFz3uSk4Nxh9sC~ahKlqiK!B>m3(uK%jtvja8}=7rPW-~$G?;rHY%Avp`vxk6 zSW&87S4u`rmGsfblC+I}LZ&ZP%97TNE>#dKRaH*JssY)kRpgjX^)O(D+2{((N)|d7x43ibI-lyfUA&XRq;A@n(8ikBEVT@pozSohhEH7W!7Ups**=1c zfsG@~c@GqfVUG-alug9K#Fp6ft^ilzaKhf)u<-ir+IZ;cPt#)SVBJKV8A!Y%Q=+l8 zrj8qGD{hzXY@JI|B|4yX6*^6%Rp}emG?rxz)_zphbOx|};7SGmBAzdfAruG!TzErz z3jv*na|i;v^cE5#+fdo-NC$9~)14$8&9|PP+0vEsF*_d$qp~w~5wecUX%J%-@^-tJ z<}Q;TT0J*vhq~hPGn^w?hcmR4(fOQKU1h*!cS3JUE2E?rdr6XYC)qnm^70f~&KED? zzAb7PpLMhx<+LWP2?IL0rtL`j;VC+KV8PzrUMqbGrmrt06TIsUbu6?sb!bOi5AA2> zURXTcR&sak)%K~G^Bd>S9x*ZBnw#MWA~CYgq|!M>eJQr9QX0ENy@B)~czLI@o-H1o zW5@d1XJ>8CDR7$pdv5!DrL|$Er%8d}@Y(e-tqy+tWX4I{#qWEpPRWE8ky?R@DcJ;l9%7GKfLBf2DhX$@NB-vzDRXzl>&em`aq7e;HLO wwV7L_#7!nqz$rF{Gs=~5+?$Oh$E)PTnx*=an(wE4<^BVv;Y7A literal 0 HcmV?d00001 diff --git a/languages/gravityformspartialentries-de_DE.mo b/languages/gravityformspartialentries-de_DE.mo new file mode 100644 index 0000000000000000000000000000000000000000..69160a87dfee3ef01b0f086c3bc753e87b87cf3e GIT binary patch literal 3178 zcmbtWNo*WN6m3Y@90(BhMevalNQ8T4oB(m!7~wTC*uk>LB+A9qbl1!j(^J){svc+N z0>?Tta)<{-3}{f&2Cb!F|Akzy;uoz&nBO0PhA~06qr12;}oV z2R;J)3iuH4dm!`t9mxCt06q?!#2~Lf0}OFj8#s>shd6v6@CuOc`2jDs>lYxe{{zVO z@42hAb3bqi{fB@Dfidt?-~}M_s9^EazYSFY*9L&n5$WxDHsh2jG z)1r~eV7Li`lGd`+h{Vx`!fD!gYDiK#PV+=2XpnDTeY29T5}dT6r+lC0jqN&G9uk`a zF-#LUhi~X$q1>!AZkEoIX_B`>JJ=z0+$W(EGI=qM`Ao*1n#NM@q;=0xW>rsMm2WG@ zyi_bpjv~vXuq1pz&6}(^7%Z{53-+nmC}tArVyiNiqY^9%5LgvGnM8ERQE)2nHl*d9 ztR|Dr(9J|kM!_}aT-%SzbFLp=E8LV^HPNSq*Utr6Bf*mV4`Dk zgf`DXf*$w?$4i+aI?7jqWouf{%568;nq3_$&*jSEoMssoD_i}c$60|wcSMR;I+s*8 z9jSxs$JbUCi_s*gL)safsXWaHJ3N|tBF&-Ne~OxV8{FF_O<q98_|r(6tryg>8#Mg3wk*&M#4rv926t3mQXRa^$Pau zRu1xEU5YN~N_bb(;S&2t=apEbkJV0$zgSJ= z`s`fw$jH*d(%g;`W0l$ndk}#U*87=^$jkG-nx#VN35wgoLg2}@`g}OPvyKgGN*m7U z*d)+8qVb0EBg=1uE3&6p;&2veCZeOYu@hl!JUmvT+Oep1{P`N5ZR;W`IE^f)xfN+R z&oV}&v!d%Jk4{k8oII@QnMoQuGBI){JSnxbrKm$GU#PrPIf|KO!;uwE;{(ZrZ%0(A zRCf&<)9DJ^!3{fuGbZN@7}1(m9{ySNpzU_%Uzhz2V-vTioR4UIePUfste=m@Mvum5 zZZ$LyOswmoKl%cu^tv`C+Zx{Pj2??8kV@8t_ZEk7v+cX-uvv`Cozb#cj1F(w-Z1R% z5M4zci`&aEJFLO*T@%Owv79OW7*Xzg|e<7v4TAn-)&J@2DmLDkvm%9s?zb)lDKE{GADdqy3CcuyR5x=$(Ea^ zrOy2TQyU6)CMXAzPNC!s$Y2xLHL%eY)h~`RwYE3JT7-&fNuOmk(5zc`z41@AJl}qGJsDXMVyI@~btzFg@f{v0i4S}qm z!WoY7JZhjNDG;aNJ|w7Xqsv}-PcsJIcnOIgINfStS1;ObClj=7|)Zv2mdM?xHM zp}T4tMQ-=xZUtvS^fdfc2FUA0{p;ZW9I2bd=YsF&|Aje9Uk}wD(Qrzz1pKuOQ2MzN IB94N;0p07qK>z>% literal 0 HcmV?d00001 diff --git a/languages/gravityformspartialentries-de_DE_formal.mo b/languages/gravityformspartialentries-de_DE_formal.mo new file mode 100644 index 0000000000000000000000000000000000000000..0fbebb0f9144723dadfc98484f8f880f87a8d073 GIT binary patch literal 830 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 literal 0 HcmV?d00001 diff --git a/languages/gravityformspartialentries-fr_FR.mo b/languages/gravityformspartialentries-fr_FR.mo new file mode 100644 index 0000000000000000000000000000000000000000..d476013498cb2f779c16f098e937f85e8dda4750 GIT binary patch literal 3251 zcmbVOON=DN5lzg3VE|+PAAb}AcD*aMx_f5%?AEZz!+cg_W?(ei1Bgp2rz*QUcdIjt z%&hKhxNzrSfrKSE!Nu}kELlDyghqT$TsT^?EDlH<5H}78B)`b6o}O7)BuGv5%gC&Z zjCc_hG5@{&$eRM=k9dBM=jLmKI05_)$m5+~3GqANyTCc%d%){}p8#(IehT~z@PEMT zfnV474e$))?}4`fAHPNc-Us{)$ojql-UIv|co*<47|ZrPj2}Kf54<1v3XtFb8<6k$JMb86c^}w> z`~@!I{kv}z0)OHde%P)S@OI!i;IDx%0@?3f;5twP+24Ny9|C>={44MaAp1E4iOlsI zz&n8d0zL@*u*NTeY|kw>fg|8CAp83|koCR|giF-iKHt<0p zt`*9O)3@C{@m{(9F?!?1fa9r3Y9&F2WKy|c% zd19`4u+5w=JeW7m8McE5QY88o1^j@5I5>V{DbJf#naiN4lrCslkTSuINq5CkQP7%J zMefB?Sr%ilY)!6%wgzu)*q+YBit5U$2zads%86AYcMB!zLs(R_Qx;a{o<RLyYSI+;5N~ebsZ-u`0Y^BcB#9~>NN;;Au8X9aF6~ks6 z9>ABvj^a+PeCBk?S~E+Lk(r&4i+J6Fo-M9&YG zcVf`;LmOjSUlCh{l3uYB3YNj}v8^1KW`iZ$M{v=zafCVVfr26Ik)B^=6Cvo(5L?dn zz?I*hur~(;r(UYGgP!grNw%kQ9e-vZ@{YU-+Ej|#_M$T4hwD4r8*w!kZBV-mojTC6 zbhT<4%Ax{mKP!jYec0Z&MGpT0uJ0W|C=dd;@Vs;e3$zL65CqW=q4a69*_)&tGvynZ z9k8z#kSWP}o;-&<0eLAM%1yc=e04U`eB<%CZB<$ql1<;!dGb`%^GVyLbhW#hzAE|s zIZmFW&8b_+U|mY1vJ*2IA`I~K#Hb8^9+k<##YXAulQ?R66&y<1N)A$50+k3&}ZUl$)^mqBJr{dZh7KV*xu`mUAt63ZG0VLqDZPqj_N2+KU6}F5-gO z;53t(L_McYck#)J>&_1Np=`42CV&_sie{vzibUHD-u-GwCKKTf? zOvhMTPM7zElc%yp6qk++!9f{M`(aSj)3~Z;tCMM5t#8^tP|Gtz&!e(s{VlkiwBY!G z4OE3pElXj5irQQLA9XPxymK3FoiPdQ{N&iYB;!3#*lcztag$}#s8zUi0s9uxuc zw#qc$9X~86#J8tN@x_j&9S7Tmq@ji@XgJIZ`Ud#bh4x6I3KnPJFjjAFLFhix$-RF< zC%mIR9H+dBy{E#r21;-(S98`g>pv?B^$kT_8K-*4LZ=3Bm16mR#twFIE}z9Fx(_pZ zic)m&7_Wnr{MZ_FtxlE04S$t)HnoR;bv}som4nyLNEgqk3jZCbmt)g%#0fC89IpMI z1s^$x`wsD)+WcDs+ z+#W!(uaoTX+Fw*a|J=8T=?TMSqLIM}A~TMMb-)*fG98mcVfR br&IXogqx#Y)EzGJ4kD1{-p6FPde8emRO!u_ literal 0 HcmV?d00001 diff --git a/languages/gravityformspartialentries-he_IL.mo b/languages/gravityformspartialentries-he_IL.mo new file mode 100644 index 0000000000000000000000000000000000000000..cda1945d16e680a23b35f37756b832a19aea6233 GIT binary patch literal 2893 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 T!KF$uXmGP%4cRVoy)Ey&W8UHyTJ{6HkzdN zPWw^{Ao)#US)kGbNY`kcq-dFpc>iBlzqpc?4|QrXrE>xOFGVb#^M%~0dUpPT&LE5N?CJpvU5(vRLrLR|(MNVe6va0Gc*xYq*S{+hMIgkhXqV literal 0 HcmV?d00001 diff --git a/languages/gravityformspartialentries-it_IT.mo b/languages/gravityformspartialentries-it_IT.mo new file mode 100644 index 0000000000000000000000000000000000000000..559fea54a52595b3c10319d832aacacfdaea525f GIT binary patch literal 3063 zcmb7FOKcoP5bb~=!~qh*p8zVPL`j5acby-}7z2)DM^=6&UPFYCtlpX4-6p-=lb)V+ z)>6a?gg_i4aYEcU!jW4Jkq{RyTo4FxKwRMhM-IH28OP2CiHx-Sx~scuUcIX6`gPaX zHG${(`u7iTFMhvl!5^ONJ|T7kF9XMcp8_8Meg=FD_&M-V;Mc$hf!AyN7I+-94}n*K9LE^M;`i+aJ^?%md;xeGNI(r_%|8ad3;Yte9XN(R=C>30 zB9MTr^XowN`zDb6^?}UuDsTz-0T2|$Pxbn5Ku#fR{0M%m4L__M`(J<9C+p1*`(+;- zFXSruu$KHlsxrSkX5BeA{BR7c8`50Hi!jOuvX&2P$o!crq!D5!O`AoQ%AhEdK4@u3 z+2BW{yJE%|n%BytxtPf^GZM4TraEY6vFnWM=|r4VU0IlbZ6+u$=B(@(CF(;kDq7Br zlW9(?8s4l6)KR2uVv1Cy7*Ocn`ev>2hB)QrP=^uCJ3q*2u_iVLVpt|{j?P=LP#xA5 zZ&rCA*QMa4%@K#P$%v#)$rYs?>zPUdb)BaoS3ciInb$*!U7@dY=A{!=agWYZK0^`E-5SSv@c&cQ(9Be zhP1QrsRPYQU%RHEGzC<bf9!M*QHiueLv-y1_JkOGA8w)7STI*Z_t1PA7a8v# z=NgIHT%kq==VO|6nTD2~5xpa=43b_=N=wukMQf$yjS3ph_g=$!+bcjmYAZR2X(33X zrKSBm7#*Pf>jYL;R~zMrpu6fy$-ukhk_tU#b&)p%PUp=-t>edzG<8^6Xg3dxpItaR zw;{x2qczS6L|sJfQKn)F>Pl#4Mru1nNnd*CJH6bVkB)8Zh#=X5}=W zJ~&0yaC*O`m!@g*z|{D~=#;X`SDMyJ9&Q|M9K_0^6pLFdmnb;vs#cO~vg*G$HMqy2U(jv$F2UU`pFB4d z>jsKaWsKxrQnivbNqDC+QlR~y%|c)p>s}?%k63YsszdjII~JfUW5=Bd5kvc`Q@MMtL3ARVMklH| N5R%GsX^etn@h^>ZgIoXr literal 0 HcmV?d00001 diff --git a/languages/gravityformspartialentries-ja.mo b/languages/gravityformspartialentries-ja.mo new file mode 100644 index 0000000000000000000000000000000000000000..89e32cbc0bb2745b45509ec7ddee9d27c3583de7 GIT binary patch literal 2989 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 literal 0 HcmV?d00001 diff --git a/languages/gravityformspartialentries-nl_BE.mo b/languages/gravityformspartialentries-nl_BE.mo new file mode 100644 index 0000000000000000000000000000000000000000..55846ee4cdf2639d52950468e96b6aee9ed321af GIT binary patch literal 416 zcmYL_UrWO<6vZd{lt-U^xPlKGnAQ!^nb8O5x-oF9j4I-b#P+U>v`I;Bhx;IYJwJ=- z2JXO*{5zbS zb}%)za*#+%N|q37vI-qM<~d(#=m+;~g|)H77p_n_A2)^LsS&Uts+|bK;t;cLQ-aJ? zp=;{a&z_|to?5A0Dalv?en%ILERd%tD*=;+vJG^{SQe_OdTcRY#K(I5;D$XJO{jg! zu0{cb-xSuRRC)y2Mp}myy`)nYt7Y#;mm_}U_IY`8>Pj|i~N@I<8$KE-wXEw95 z_W2qlM1h1-MuH|C5*<{C4$&YephGkW8X5{J5ETXA8}Hfq1v1k5o8LF@eecbC{`ts( zs~+01;`?@Pn4#>v_)uF9XMbSAh2eKLkDwyas#(_!;mZ@XG?f2A;wAJK%BP zq5C}V1Kw1h@t~3Oo*sfgb~}0O|f|ko5wv3M4&a;5hKz0zUzgKHmekfxiK9)O!<3JPfP? zY5f9_cs4+i^B$1wyavP(?{nZK;8#F8e+=Y23494iE+mdg9-W~^+>t%|jcg>osnI!@ z=^eobx*Q*53pG&X?4~~HLB63zaYO!xo1MO-6E)E4+7tLC{SVCB*H5W zVN$T|G|@cDSWiNl(v~#@lPXN|NJQu`8{c@d6j{kTXLwiIK3mqNld+9Lu_Y*mWd@!7 zb>&qDho!>JBC||4nKfKxutTV@&$x=1&fPrLQxRI$)Q07mFxks2HL}aG%f>Pzy<{i` zI`T9X+%RsPYF4LCFto(s4zy2n&#gov3yn;Pj*_>+p}-dJiYQ>?8S~ciPD23KMJ< z#=!|>(hdT`uSWiPgabGV*#8MSHd%UYY*VD#SbsHZ$;dyOw=%z`0~TAGW z%$zQ(q`tONJ~_5pU0oWHFoyInj z-0;L-CMGeLx?wgqHOmIwxf6=LIL9VW&W>I5&j}^WfY*Y}Q>9l+Q&`#16kPsUypfdK zIAEnx`MPPX+Z}G&xM4K8pmR!s0ozv6!bMvaYG2Ma=q|U815JuhDc9Np&X2ocB&~>xLO~V7l zc%?OF8VJS0z>fjTZ4-{Xh|t=#k?z3B{?;ID1J;M--Z>EofgGR6 zoi-)#J%Lhy2W7SBYJspSH{?!F7`%4I6BLXDxxb6rf!ANtAU?~`JFm6ralGPf^{~m0I zLaHcN!tqr2Y;+}U)d;)&+O?-6!|5OqCRG>zLP{>aYzy8R^&A@%cWyo)?gU=#FgTI^ z(}dhHoLNu7BU}T@BAjcX?^{>KZnr{(Y<YKHV1#(59HkQLG7{R8VbqJIDY literal 0 HcmV?d00001 diff --git a/languages/gravityformspartialentries-pt_BR.mo b/languages/gravityformspartialentries-pt_BR.mo new file mode 100644 index 0000000000000000000000000000000000000000..0701e48b331405480058fd8e1faa21810c74c959 GIT binary patch literal 3077 zcmb7_J!~9B6vqb$0UQVrzQTvZL*l~`-n(-`Lb4_iiDL(Yjg51L2n5#L?)YxV-ppoZ z_UubTi-agB5UCIm1tJm(QiOzP5IPCbP*5QxL`6eE1C$i}-|n5AgcPun{hQA>@9Wpy$}zAXTUM=UGPTm9C#=AA-EI#9NZ3mJ><9GAzAe`5~0dE3-0Pg~S0@=Rb!CSz; zLAG-{CP%^Bz&+q1cn<7?{QfRD>k)7q+yy=l^82rY4}%*Z+j|at1H1_0s@S;$GQr&- zpL-m<8=M8%U$22|_d3Y-y$8MoegHx>@f*nR{Q+_a`3}ot-|)x2V>?G5>%jizkKbV# zd>`%-5K-C!2)L@Wo5%b<$A&-78}<*vT-paS%8zy9kA28_!aimD#L+aZ7kMg!qFlP5 zWk$*b*C)Luj%FFnYL%s49L@8rFQ%yOZvd99CWkESHV`M8+Vt5IQ zidOQ>%GA@khBRXXwG?TSWJRh{3@CK2d|4}B6DOSP>CmTH>$;wnhKkKVF>DibjuwrW zt1fGUmz57>*C;q?JnT>=>60`m*`i#>ey);0Yt~WWmGe(f?sQM$ROo2Wdg(+}I?6m( z(vb|MYQg5EU}%ZUU1*=#^>QawK5;r{chtnZgaRjJPo*(E=1II*bX&^tP4*@$onxBI zwu;47=3G6GD>?sOUMa(rUa`@qx#MCfQ_?FoLP0Yq-nWH=Z&_i<`Vma@tQ=v?dvHMy z`bf`5Sw&2AsEH+K+whg&9I!Q)O`JMYXa_lKRaz{M=EI1y2TH#qQ-U^yqNY8qjQIEZ z%JO`wy5@Z%_I7!j?F(=RJY!8i(lvrP1ua!Rry{0xwEu2kGtI$?P7rq`K!PoaS4jtTI z&%^4m<@%nng}H^9EoV&D8e<$p?~Q%t>_qjBDoBm zm}<59Wy{u{?n-wW4{R+?+k$IgOe;nQMCfFI)_!aM1=U}-F7@-;hnQAZr&iU}>P9>{ zu{XhRG%&m{wQ9=w#8cQZtH#=Vw7fZ)*q2P9n5@bW9E9<(6S~=OSgxwA)oNI-4qw{5 zFr05GdJ>f^=`6wQr~$(-+dvga)N~cbn2NB5IG#dgZG_Zz5##7&m9;VTVL94_P7prn z>M|5E(`Qj3ood5=XSs1C+&GYlqG?l$ADyQ}c5};(Bh!g=Vt#N5{X$a0Eu<8cp-p7f z_0$bMw&FS2(wPo8JYC&gL}lt}ODSrKL*eN`tieexUyQPi3_jxrRd-Vzl-g1sjXuJi z0ws?dhfGoJPql)0vntKVww!LuFu24q80ODai)ANbhp|VI zzUVqtlafxL8}SZ1qufE3a{Izg>Hd+skv9r2x=@kE|#l==>ncEa5LiPWUndnTo Sk4P>k!yT#1Q<*6jr1%G`>zbzk literal 0 HcmV?d00001 diff --git a/languages/gravityformspartialentries-pt_PT.mo b/languages/gravityformspartialentries-pt_PT.mo new file mode 100644 index 0000000000000000000000000000000000000000..f2b13be5af4c348773c2e9d4b6abb6b581a91a00 GIT binary patch literal 3115 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 literal 0 HcmV?d00001 diff --git a/languages/gravityformspartialentries-sv_SE.mo b/languages/gravityformspartialentries-sv_SE.mo new file mode 100644 index 0000000000000000000000000000000000000000..9d296928d41bc513edee491f0537de9a42800b67 GIT binary patch literal 2931 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 H^LOn(yX76f literal 0 HcmV?d00001 diff --git a/languages/gravityformspartialentries.pot b/languages/gravityformspartialentries.pot new file mode 100644 index 0000000..51c06a1 --- /dev/null +++ b/languages/gravityformspartialentries.pot @@ -0,0 +1,140 @@ +# Copyright (C) 2022 Gravity Forms +# This file is distributed under the GPL-2.0+. +msgid "" +msgstr "" +"Project-Id-Version: Gravity Forms Partial Entries Add-On 1.7\n" +"Report-Msgid-Bugs-To: https://gravityforms.com/support\n" +"Last-Translator: Gravity Forms \n" +"Language-Team: Gravity Forms \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"POT-Creation-Date: 2022-04-07T16:02:28+00:00\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"X-Generator: WP-CLI 2.5.0\n" +"X-Domain: gravityformspartialentries\n" + +#. Plugin Name of the plugin +msgid "Gravity Forms Partial Entries Add-On" +msgstr "" + +#. Plugin URI of the plugin +#. Author URI of the plugin +msgid "https://gravityforms.com" +msgstr "" + +#. Description of the plugin +msgid "Adds support for partial submissions." +msgstr "" + +#. Author of the plugin +msgid "Gravity Forms" +msgstr "" + +#: class-gf-partial-entries.php:198 +msgid "Fields" +msgstr "" + +#: class-gf-partial-entries.php:199 +msgid "All Fields" +msgstr "" + +#: class-gf-partial-entries.php:200 +msgid "Required Fields" +msgstr "" + +#: class-gf-partial-entries.php:201 +#: class-gf-partial-entries.php:339 +msgid "Progress" +msgstr "" + +#: class-gf-partial-entries.php:233 +#: class-gf-partial-entries.php:237 +msgid "Partial Entries" +msgstr "" + +#: class-gf-partial-entries.php:242 +#: class-gf-partial-entries.php:267 +msgid "Enable" +msgstr "" + +#: class-gf-partial-entries.php:255 +msgid "Warning Message" +msgstr "" + +#: class-gf-partial-entries.php:259 +msgid "In the interest of transparency and out of respect for users' privacy this notice will appear at the top of the form below the description." +msgstr "" + +#: class-gf-partial-entries.php:265 +msgid "Conditional Logic" +msgstr "" + +#: class-gf-partial-entries.php:268 +msgid "Add/update partial entry if" +msgstr "" + +#: class-gf-partial-entries.php:309 +msgctxt "Threshold setting" +msgid "%s of %s" +msgstr "" + +#: class-gf-partial-entries.php:333 +msgid "Partial Entry ID" +msgstr "" + +#: class-gf-partial-entries.php:354 +msgid "Progress: required fields" +msgstr "" + +#: class-gf-partial-entries.php:363 +msgid "Save and Continue Token" +msgstr "" + +#: class-gf-partial-entries.php:368 +msgid "Save and Continue URL" +msgstr "" + +#: class-gf-partial-entries.php:373 +msgid "Saved" +msgstr "" + +#: class-gf-partial-entries.php:845 +msgid "Progress: %s" +msgstr "" + +#: class-gf-partial-entries.php:852 +msgid "Last Saved: %s " +msgstr "" + +#: class-gf-partial-entries.php:1006 +msgid "Default Filter" +msgstr "" + +#: class-gf-partial-entries.php:1007 +msgid "Number of entries per page:" +msgstr "" + +#: class-gf-partial-entries.php:1009 +msgid "Apply" +msgstr "" + +#: class-gf-partial-entries.php:1268 +msgid "Please note that your information is saved on our server as you enter it." +msgstr "" + +#: class-gf-partial-entries.php:1298 +msgid "File Uploads will not be included in the partial entries." +msgstr "" + +#: class-gf-partial-entries.php:1306 +msgid "The Credit Card field values will not be included in the partial entries." +msgstr "" + +#: class-gf-partial-entries.php:1326 +msgid "Partial Entries: Saved" +msgstr "" + +#: class-gf-partial-entries.php:1327 +msgid "Partial Entries: Updated" +msgstr "" diff --git a/partialentries.php b/partialentries.php new file mode 100644 index 0000000..65382e8 --- /dev/null +++ b/partialentries.php @@ -0,0 +1,46 @@ +