initial
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
if ( ! class_exists( 'GFForms' ) ) {
|
||||
die();
|
||||
}
|
||||
|
||||
class GFAddonLocking extends GFLocking {
|
||||
protected $_strings;
|
||||
/* @var GFAddOn $_addon */
|
||||
protected $_addon;
|
||||
|
||||
/**
|
||||
* e.g.
|
||||
*
|
||||
* array(
|
||||
* "object_type" => 'contact',
|
||||
* "capabilities" => array("gravityforms_contacts_edit_contacts"),
|
||||
* "redirect_url" => admin_url("admin.php?page=gf_contacts"),
|
||||
* "edit_url" => admin_url(sprintf("admin.php?page=gf_contacts&id=%d", $contact_id)),
|
||||
* "strings" => $strings
|
||||
* );
|
||||
*
|
||||
* @param array $config
|
||||
* @param GFAddOn $addon
|
||||
*/
|
||||
public function __construct( $config, $addon ) {
|
||||
$this->_addon = $addon;
|
||||
$capabilities = isset( $config['capabilities'] ) ? $config['capabilities'] : array();
|
||||
$redirect_url = isset( $config['redirect_url'] ) ? $config['redirect_url'] : '';
|
||||
$edit_url = isset( $config['edit_url'] ) ? $config['edit_url'] : '';
|
||||
$object_type = isset( $config['object_type'] ) ? $config['object_type'] : '';
|
||||
$this->_strings = isset( $config['strings'] ) ? $config['strings'] : array();
|
||||
parent::__construct( $object_type, $redirect_url, $edit_url, $capabilities );
|
||||
}
|
||||
|
||||
public function get_strings() {
|
||||
return array_merge( parent::get_strings(), $this->_strings );
|
||||
}
|
||||
|
||||
protected function is_edit_page() {
|
||||
return $this->_addon->is_locking_edit_page();
|
||||
}
|
||||
|
||||
protected function is_list_page() {
|
||||
return $this->_addon->is_locking_list_page();
|
||||
}
|
||||
|
||||
protected function is_view_page() {
|
||||
return $this->_addon->is_locking_view_page();
|
||||
}
|
||||
|
||||
protected function get_object_id() {
|
||||
return $this->_addon->get_locking_object_id();
|
||||
}
|
||||
|
||||
protected function is_object_locked( $object_id ) {
|
||||
return $this->is_object_locked( $object_id );
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,361 @@
|
||||
<?php
|
||||
|
||||
if ( ! class_exists( 'GFForms' ) ) {
|
||||
die();
|
||||
}
|
||||
|
||||
class GFAutoUpgrade {
|
||||
protected $_version;
|
||||
protected $_min_gravityforms_version;
|
||||
protected $_slug;
|
||||
protected $_title;
|
||||
protected $_full_path;
|
||||
protected $_path;
|
||||
protected $_url;
|
||||
protected $_is_gravityforms_supported;
|
||||
|
||||
|
||||
public function __construct( $slug, $version, $min_gravityforms_version, $title, $full_path, $path, $url, $is_gravityforms_supported ) {
|
||||
$this->_slug = $slug;
|
||||
$this->_version = $version;
|
||||
$this->_min_gravityforms_version = $min_gravityforms_version;
|
||||
$this->_title = $title;
|
||||
$this->_full_path = $full_path;
|
||||
$this->_path = $path;
|
||||
$this->_url = $url;
|
||||
$this->_is_gravityforms_supported = $is_gravityforms_supported;
|
||||
add_action( 'init', array( $this, 'init' ) );
|
||||
}
|
||||
|
||||
public function init() {
|
||||
if ( is_admin() ) {
|
||||
GFCommon::load_gf_text_domain();
|
||||
add_action( 'install_plugins_pre_plugin-information', array( $this, 'display_changelog' ), 9 );
|
||||
add_action( 'gform_after_check_update', array( $this, 'flush_version_info' ) );
|
||||
add_action( 'gform_updates', array( $this, 'display_updates' ) );
|
||||
|
||||
add_filter( 'gform_updates_list', array( $this, 'get_update_info' ) );
|
||||
|
||||
if ( RG_CURRENT_PAGE == 'plugins.php' ) {
|
||||
add_action( 'after_plugin_row_' . $this->_path, array( $this, 'rg_plugin_row' ) );
|
||||
} elseif ( in_array( RG_CURRENT_PAGE, array( 'admin-ajax.php' ) ) ) {
|
||||
add_action( 'wp_ajax_gf_get_changelog', array( $this, 'ajax_display_changelog' ) );
|
||||
}
|
||||
}
|
||||
|
||||
// Check for updates. The check might not run the admin context. E.g. from WP-CLI.
|
||||
add_filter( 'transient_update_plugins', array( $this, 'check_update' ) );
|
||||
add_filter( 'site_transient_update_plugins', array( $this, 'check_update' ) );
|
||||
add_filter( 'auto_update_plugin', function ( $update, $item ) {
|
||||
return GFForms::maybe_auto_update( $update, $item, $this->_slug, $this->_version );
|
||||
}, 10, 2 );
|
||||
|
||||
// ManageWP premium update filters
|
||||
add_filter( 'mwp_premium_update_notification', array( $this, 'premium_update_push' ) );
|
||||
add_filter( 'mwp_premium_perform_update', array( $this, 'premium_update' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays messages for the Gravity Forms listing on the Plugins page.
|
||||
*
|
||||
* Displays if Gravity Forms isn't supported.
|
||||
*
|
||||
* @since Unknown
|
||||
* @since 2.4.15 Update to improve multisite updates.
|
||||
*/
|
||||
public function rg_plugin_row() {
|
||||
|
||||
if ( ! $this->_is_gravityforms_supported ) {
|
||||
$message = sprintf( esc_html__( 'Gravity Forms %s is required. Activate it now or %spurchase it today!%s', 'gravityforms' ), $this->_min_gravityforms_version, "<a href='https://www.gravityforms.com'>", '</a>' );
|
||||
GFAddOn::display_plugin_message( $message, 'error' );
|
||||
}
|
||||
}
|
||||
|
||||
//Integration with ManageWP
|
||||
public function premium_update_push( $premium_update ) {
|
||||
|
||||
if ( ! function_exists( 'get_plugin_data' ) ) {
|
||||
include_once( ABSPATH . 'wp-admin/includes/plugin.php' );
|
||||
}
|
||||
|
||||
$update = $this->get_version_info( $this->_slug );
|
||||
if ( rgar( $update, 'is_valid_key' ) == true && version_compare( $this->_version, $update['version'], '<' ) ) {
|
||||
$plugin_data = get_plugin_data( $this->_full_path );
|
||||
$plugin_data['type'] = 'plugin';
|
||||
$plugin_data['slug'] = $this->_path;
|
||||
$plugin_data['new_version'] = isset( $update['version'] ) ? $update['version'] : false;
|
||||
$premium_update[] = $plugin_data;
|
||||
}
|
||||
|
||||
return $premium_update;
|
||||
}
|
||||
|
||||
//Integration with ManageWP
|
||||
public function premium_update( $premium_update ) {
|
||||
|
||||
if ( ! function_exists( 'get_plugin_data' ) ) {
|
||||
include_once( ABSPATH . 'wp-admin/includes/plugin.php' );
|
||||
}
|
||||
|
||||
$update = $this->get_version_info( $this->_slug );
|
||||
if ( rgar( $update, 'is_valid_key' ) == true && version_compare( $this->_version, $update['version'], '<' ) ) {
|
||||
$plugin_data = get_plugin_data( $this->_full_path );
|
||||
$plugin_data['slug'] = $this->_path;
|
||||
$plugin_data['type'] = 'plugin';
|
||||
$plugin_data['url'] = isset( $update['url'] ) ? $update['url'] : false; // OR provide your own callback function for managing the update
|
||||
|
||||
array_push( $premium_update, $plugin_data );
|
||||
}
|
||||
|
||||
return $premium_update;
|
||||
}
|
||||
|
||||
public function flush_version_info() {
|
||||
$this->set_version_info( $this->_slug, false );
|
||||
}
|
||||
|
||||
private function set_version_info( $plugin_slug, $version_info ) {
|
||||
if ( function_exists( 'set_site_transient' ) ) {
|
||||
set_site_transient( $plugin_slug . '_version', $version_info, 60 * 60 * 12 );
|
||||
} else {
|
||||
set_transient( $plugin_slug . '_version', $version_info, 60 * 60 * 12 );
|
||||
}
|
||||
}
|
||||
|
||||
public function check_update( $option ) {
|
||||
|
||||
if ( empty( $option ) ) {
|
||||
return $option;
|
||||
}
|
||||
|
||||
$key = $this->get_key();
|
||||
|
||||
$version_info = $this->get_version_info( $this->_slug );
|
||||
|
||||
if ( rgar( $version_info, 'is_error' ) == '1' ) {
|
||||
return $option;
|
||||
}
|
||||
|
||||
if ( empty( $option->response[ $this->_path ] ) ) {
|
||||
$option->response[ $this->_path ] = new stdClass();
|
||||
}
|
||||
|
||||
$new_version = rgar( $version_info, 'version', '0' );
|
||||
|
||||
$plugin = array(
|
||||
'plugin' => $this->_path,
|
||||
'url' => $this->_url,
|
||||
'slug' => $this->_slug,
|
||||
'package' => $version_info['url'] ? str_replace( '{KEY}', $key, $version_info['url'] ) : '',
|
||||
'new_version' => $new_version,
|
||||
'id' => '0',
|
||||
'minimum_requirements' => $version_info['minimum_requirements'],
|
||||
'version_latest' => $version_info['version_latest']
|
||||
);
|
||||
|
||||
//Empty response means that the key is invalid. Do not queue for upgrade
|
||||
if ( ! rgar( $version_info, 'is_valid_key' ) || version_compare( $this->_version, $new_version, '>=' ) ) {
|
||||
unset( $option->response[ $this->_path ] );
|
||||
$option->no_update[ $this->_path ] = (object) $plugin;
|
||||
} else {
|
||||
$option->response[ $this->_path ] = (object) $plugin;
|
||||
}
|
||||
|
||||
// Check minimum requirements. If not met, remove from response and add to no_update.
|
||||
$minimum_requirements_evaluation_result = GFCommon::evaluate_minimum_requirements(
|
||||
is_array( $version_info['minimum_requirements'] ?? null )
|
||||
? $version_info['minimum_requirements']
|
||||
: []
|
||||
);
|
||||
|
||||
if ( $minimum_requirements_evaluation_result['block'] == true ) {
|
||||
if ( isset( $option->response[ $this->_path ] ) ) {
|
||||
unset( $option->response[ $this->_path ] );
|
||||
}
|
||||
$option->no_update[ $this->_path ] = (object) $plugin;
|
||||
}
|
||||
|
||||
return $option;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Displays current version details on plugins page and updates page
|
||||
public function display_changelog() {
|
||||
if ( isset( $_REQUEST['plugin'] ) && $_REQUEST['plugin'] != $this->_slug ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
return;
|
||||
}
|
||||
$change_log = $this->get_changelog();
|
||||
echo $change_log; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get changelog with admin-ajax.php in GFForms::maybe_display_update_notification().
|
||||
*
|
||||
* @since 2.4.15
|
||||
*/
|
||||
public function ajax_display_changelog() {
|
||||
check_admin_referer();
|
||||
|
||||
$this->display_changelog();
|
||||
}
|
||||
|
||||
private function get_changelog() {
|
||||
$key = $this->get_key();
|
||||
$body = "key={$key}";
|
||||
$options = array( 'method' => 'POST', 'timeout' => 3, 'body' => $body );
|
||||
$options['headers'] = array(
|
||||
'Content-Type' => 'application/x-www-form-urlencoded; charset=' . get_option( 'blog_charset' ),
|
||||
'Content-Length' => strlen( $body ),
|
||||
'User-Agent' => 'WordPress/' . get_bloginfo( 'version' ),
|
||||
);
|
||||
|
||||
$raw_response = GFCommon::post_to_manager( 'changelog.php', $this->get_remote_request_params( $this->_slug, $key, $this->_version ), $options );
|
||||
|
||||
if ( is_wp_error( $raw_response ) || 200 != $raw_response['response']['code'] ) {
|
||||
$text = sprintf( esc_html__( 'Oops!! Something went wrong.%sPlease try again or %scontact us%s.', 'gravityforms' ), '<br/>', "<a href='" . esc_attr( GFCommon::get_support_url() ). "'>", '</a>' );
|
||||
} else {
|
||||
$text = $raw_response['body'];
|
||||
if ( substr( $text, 0, 10 ) != '<!--GFM-->' ) {
|
||||
$text = '';
|
||||
}
|
||||
}
|
||||
|
||||
return stripslashes( $text );
|
||||
}
|
||||
|
||||
private function get_version_info( $offering, $use_cache = true ) {
|
||||
|
||||
$version_info = GFCommon::get_version_info( $use_cache );
|
||||
|
||||
$info = array(
|
||||
'is_valid_key' => rgar( $version_info, 'is_valid_key' ),
|
||||
'version' => rgars( $version_info, "offerings/{$offering}/version" ),
|
||||
'url' => rgars( $version_info, "offerings/{$offering}/url" ),
|
||||
'is_available' => rgars( $version_info, "offerings/{$offering}/is_available" ),
|
||||
'minimum_requirements' => rgars( $version_info, "offerings/{$offering}/minimum_requirements" ),
|
||||
'version_latest' => rgars( $version_info, "offerings/{$offering}/version_latest" ),
|
||||
);
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
private function get_remote_request_params( $offering, $key, $version ) {
|
||||
global $wpdb;
|
||||
|
||||
return sprintf( 'of=%s&key=%s&v=%s&wp=%s&php=%s&mysql=%s', urlencode( $offering ), urlencode( $key ), urlencode( $version ), urlencode( get_bloginfo( 'version' ) ), urlencode( phpversion() ), urlencode( GFCommon::get_db_version() ) );
|
||||
}
|
||||
|
||||
private function get_key() {
|
||||
if ( $this->_is_gravityforms_supported ) {
|
||||
return GFCommon::get_key();
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
public function get_update_info( $updates ) {
|
||||
|
||||
$force_check = rgget( 'force-check' ) == 1;
|
||||
$version_info = $this->get_version_info( $this->_slug, ! $force_check );
|
||||
|
||||
$plugin_file = $this->_path;
|
||||
$upgrade_url = wp_nonce_url( 'update.php?action=upgrade-plugin&plugin=' . urlencode( $plugin_file ), 'upgrade-plugin_' . $plugin_file );
|
||||
|
||||
if ( ! rgar( $version_info, 'is_valid_key' ) ) {
|
||||
|
||||
$version_icon = 'dashicons-no';
|
||||
$version_message = sprintf(
|
||||
'<p>%s</p>',
|
||||
sprintf(
|
||||
esc_html( '%sRegister%s your copy of Gravity Forms to receive access to automatic updates and support. Need a license key? %sPurchase one now%s.', 'gravityforms' ),
|
||||
'<a href="admin.php?page=gf_settings">',
|
||||
'</a>',
|
||||
'<a href="https://www.gravityforms.com">',
|
||||
'</a>'
|
||||
)
|
||||
);
|
||||
|
||||
} elseif ( version_compare( $this->_version, $version_info['version'], '<' ) ) {
|
||||
|
||||
$details_url = self_admin_url( 'plugin-install.php?tab=plugin-information&plugin=' . urlencode( $this->_slug ) . '§ion=changelog&TB_iframe=true&width=600&height=800' );
|
||||
$message_link_text = sprintf( esc_html__( 'View version %s details', 'gravityforms' ), $version_info['version'] );
|
||||
$message_link = sprintf( '<a href="%s" class="thickbox" title="%s">%s</a>', esc_url( $details_url ), esc_attr( $this->_title ), $message_link_text );
|
||||
$message = sprintf( esc_html__( 'There is a new version of %1$s available. %s.', 'gravityforms' ), $this->_title, $message_link );
|
||||
|
||||
$version_icon = 'dashicons-no';
|
||||
$version_message = $message;
|
||||
|
||||
} else {
|
||||
|
||||
$version_icon = 'dashicons-yes';
|
||||
$version_message = sprintf( esc_html__( 'Your version of %s is up to date.', 'gravityforms' ), $this->_title );
|
||||
}
|
||||
|
||||
$updates[] = array(
|
||||
'name' => esc_html( $this->_title ),
|
||||
'is_valid_key' => rgar( $version_info, 'is_valid_key' ),
|
||||
'path' => $this->_path,
|
||||
'slug' => $this->_slug,
|
||||
'latest_version' => $version_info['version'],
|
||||
'installed_version' => $this->_version,
|
||||
'upgrade_url' => $upgrade_url,
|
||||
'download_url' => $version_info['url'],
|
||||
'version_icon' => $version_icon,
|
||||
'version_message' => $version_message,
|
||||
);
|
||||
|
||||
return $updates;
|
||||
|
||||
}
|
||||
|
||||
public function display_updates() {
|
||||
|
||||
?>
|
||||
<div class="wrap <?php echo esc_attr( GFCommon::get_browser_class() ); ?>">
|
||||
<h2><?php esc_html_e( $this->_title ); ?></h2>
|
||||
<?php
|
||||
$force_check = rgget( 'force-check' ) == 1;
|
||||
$version_info = $this->get_version_info( $this->_slug, ! $force_check );
|
||||
|
||||
if ( ! rgar( $version_info, 'is_valid_key' ) ) {
|
||||
?>
|
||||
<div class="gf_update_expired alert_red">
|
||||
<?php printf( esc_html__( '%sRegister%s your copy of Gravity Forms to receive access to automatic updates and support. Need a license key? %sPurchase one now%s.', 'gravityforms' ), '<a href="admin.php?page=gf_settings">','</a>','<a href="https://www.gravityforms.com">', '</a>' ); ?>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
} elseif ( version_compare( $this->_version, $version_info['version'], '<' ) ) {
|
||||
|
||||
if ( rgar( $version_info, 'is_valid_key' ) ) {
|
||||
$plugin_file = $this->_path;
|
||||
$upgrade_url = wp_nonce_url( 'update.php?action=upgrade-plugin&plugin=' . urlencode( $plugin_file ), 'upgrade-plugin_' . $plugin_file );
|
||||
$details_url = self_admin_url( 'plugin-install.php?tab=plugin-information&plugin=' . urlencode( $this->_slug ) . '§ion=changelog&TB_iframe=true&width=600&height=800' );
|
||||
$message_link_text = sprintf( esc_html__( 'View version %s details', 'gravityforms' ), $version_info['version'] );
|
||||
$message_link = sprintf( '<a href="%s" class="thickbox" title="%s">%s</a>', esc_url( $details_url ), esc_attr( $this->_title ), $message_link_text );
|
||||
$message = sprintf( esc_html__( 'There is a new version of %1$s available. %s.', 'gravityforms' ), $this->_title, $message_link );
|
||||
|
||||
?>
|
||||
<div class="gf_update_outdated alert_yellow">
|
||||
<?php echo $message . ' <p>' . sprintf( esc_html__( 'You can update to the latest version automatically or download the update and install it manually. %sUpdate Automatically%s %sDownload Update%s', 'gravityforms' ), "</p><a class='button-primary' href='{$upgrade_url}'>", '</a>', " <a class='button' href='{$version_info['url']}'>", '</a>' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped?>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
} else {
|
||||
|
||||
?>
|
||||
<div class="gf_update_current alert_green">
|
||||
<?php printf( esc_html__( 'Your version of %s is up to date.', 'gravityforms' ), esc_html( $this->_title ) ); ?>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,429 @@
|
||||
<?php
|
||||
|
||||
use Gravity_Forms\Gravity_Forms\Async\GF_Background_Process;
|
||||
|
||||
if ( ! class_exists( 'GFForms' ) ) {
|
||||
die();
|
||||
}
|
||||
|
||||
if ( ! class_exists( 'Gravity_Forms\Gravity_Forms\Async\GF_Background_Process' ) ) {
|
||||
require_once GF_PLUGIN_DIR_PATH . 'includes/async/class-gf-background-process.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* GF_Feed_Processor Class.
|
||||
*
|
||||
* @since 2.2
|
||||
*/
|
||||
class GF_Feed_Processor extends GF_Background_Process {
|
||||
|
||||
/**
|
||||
* Contains instances of this class, if available.
|
||||
*
|
||||
* @since 2.2
|
||||
* @since 2.9.25 Changed to an array.
|
||||
*
|
||||
* @var self[] $_instances If available, contains instances of this class.
|
||||
*/
|
||||
private static $_instances = array();
|
||||
|
||||
/**
|
||||
* The action name.
|
||||
*
|
||||
* @since 2.2
|
||||
* @access protected
|
||||
* @var string
|
||||
*/
|
||||
protected $action = 'gf_feed_processor';
|
||||
|
||||
/**
|
||||
* Indicates if the task uses an array that supports the attempts key.
|
||||
*
|
||||
* @since 2.9.9
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $supports_attempts = true;
|
||||
|
||||
/**
|
||||
* Null or the add-on instance.
|
||||
*
|
||||
* @since 2.9.25
|
||||
*
|
||||
* @var null|GFFeedAddOn
|
||||
*/
|
||||
protected $add_on = null;
|
||||
|
||||
/**
|
||||
* Instantiates the class.
|
||||
*
|
||||
* @since 2.9.25
|
||||
*
|
||||
* @param bool|array $allowed_batch_data_classes Optional. Array of class names that can be unserialized. Default true (any class).
|
||||
* @param null|GFFeedAddOn $add_on Optional. The add-on instance.
|
||||
*/
|
||||
public function __construct( $allowed_batch_data_classes = true, $add_on = null ) {
|
||||
if ( $add_on instanceof GFFeedAddOn ) {
|
||||
$this->action = str_replace( 'gf', 'gf_' . $add_on->get_short_slug(), $this->action );
|
||||
$this->add_on = $add_on;
|
||||
}
|
||||
parent::__construct( $allowed_batch_data_classes );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get instance of this class.
|
||||
*
|
||||
* @since 2.2
|
||||
* @since 2.9.25 Added the $add_on param.
|
||||
*
|
||||
* @param null|GFFeedAddOn $add_on Optional. The add-on instance.
|
||||
*
|
||||
* @return GF_Feed_Processor
|
||||
*/
|
||||
public static function get_instance( $add_on = null ) {
|
||||
$key = $add_on instanceof GFFeedAddOn ? $add_on->get_slug() : 0;
|
||||
|
||||
if ( empty( self::$_instances[ $key ] ) ) {
|
||||
self::$_instances[ $key ] = new self( true, $add_on );
|
||||
}
|
||||
|
||||
return self::$_instances[ $key ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Push to queue
|
||||
*
|
||||
* @since 2.9.25
|
||||
* @remove-in 3.0
|
||||
*
|
||||
* @param mixed $data Data.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function push_to_queue( $data ) {
|
||||
if ( ! is_string( rgar( $data, 'addon' ) ) ) {
|
||||
_doing_it_wrong( __METHOD__, "Support for passing an add-on instance to `\$data['addon']` is deprecated since v2.7.15 and will be removed in v3.0. Please pass the add-on’s fully qualified class name (including its namespace) instead.", '' );
|
||||
$data['addon'] = get_class( $data['addon'] );
|
||||
}
|
||||
|
||||
return parent::push_to_queue( $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the task.
|
||||
*
|
||||
* @since 2.2
|
||||
* @since 2.9.4 Updated to use the add-on save_entry_feed_status(), post_process_feed(), and fullfill_entry() methods.
|
||||
*
|
||||
* @access protected
|
||||
*
|
||||
* @param array $item {
|
||||
* The task arguments.
|
||||
*
|
||||
* @type string $addon The add-on class name.
|
||||
* @type array $feed The feed.
|
||||
* @type int $entry_id The entry ID.
|
||||
* @type int $form_id The form ID.
|
||||
* @type int $attempts The number of attempts. Only included if the task has been processed before.
|
||||
* }
|
||||
*
|
||||
* @return bool|array
|
||||
*/
|
||||
protected function task( $item ) {
|
||||
$feed = $item['feed'];
|
||||
$feed_name = GFAPI::get_feed_name( $feed );
|
||||
$feed_id = (int) rgar( $feed, 'id' );
|
||||
$entry_id = (int) rgar( $item, 'entry_id' );
|
||||
|
||||
if ( $this->add_on instanceof GFFeedAddOn ) {
|
||||
$addon = $this->add_on;
|
||||
$feed_slug = rgar( $feed, 'addon_slug' );
|
||||
if ( $feed_slug !== $addon->get_slug() ) {
|
||||
$this->log_error( __METHOD__ . "(): Aborting. Feed (#{$feed_id} - {$feed_name}) for entry #{$entry_id} belongs to a different add-on ({$feed_slug})." );
|
||||
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
/**
|
||||
* Passing an instance of the add-on.
|
||||
* @deprecated 2.7.15
|
||||
* @remove-in 3.0
|
||||
*/
|
||||
$addon = $item['addon'];
|
||||
$callable = array( is_string( $addon ) ? $addon : get_class( $addon ), 'get_instance' );
|
||||
if ( is_callable( $callable ) ) {
|
||||
$addon = call_user_func( $callable );
|
||||
}
|
||||
|
||||
if ( ! $addon instanceof GFFeedAddOn ) {
|
||||
GFCommon::log_error( __METHOD__ . "(): Aborting. Add-on ({$feed['addon_slug']}) not found for feed (#{$feed_id} - {$feed_name}) and entry #{$entry_id}." );
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$addon->log_debug( __METHOD__ . "(): Preparing to process feed (#{$feed_id} - {$feed_name}) for entry #{$entry_id}." );
|
||||
|
||||
$entry = GFAPI::get_entry( $entry_id );
|
||||
$addon_slug = $addon->get_slug();
|
||||
|
||||
// Remove task if entry cannot be found.
|
||||
if ( is_wp_error( $entry ) ) {
|
||||
$addon->log_error( __METHOD__ . "(): Aborting. Entry #{$entry_id} not found for feed (#{$feed_id} - {$feed_name})." );
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
$form_id = (int) rgar( $item, 'form_id' );
|
||||
$form = $this->filter_form( GFAPI::get_form( $form_id ), $entry );
|
||||
|
||||
if ( ! $this->can_process_feed( $feed, $entry, $form, $addon ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$max_attempts = 1;
|
||||
|
||||
/**
|
||||
* Allow the number of retries to be modified before the feed is abandoned.
|
||||
*
|
||||
* if $max_attempts > 1 and if GFFeedAddOn::process_feed() throws an error or returns a WP_Error then the feed
|
||||
* will be attempted again. Once the maximum number of attempts has been reached then the feed will be abandoned.
|
||||
*
|
||||
* @since 2.4
|
||||
*
|
||||
* @param int $max_attempts The maximum number of retries allowed. Default: 1.
|
||||
* @param array $form The form array
|
||||
* @param array $entry The entry array
|
||||
* @param string $addon_slug The add-on slug
|
||||
* @param array $feed The feed array
|
||||
*/
|
||||
$max_attempts = apply_filters( 'gform_max_async_feed_attempts', $max_attempts, $form, $entry, $addon_slug, $feed );
|
||||
|
||||
// Remove task if it was attempted too many times but failed to complete.
|
||||
if ( $item['attempts'] > $max_attempts ) {
|
||||
$addon->log_error( __METHOD__ . "(): Aborting. Feed (#{$feed_id} - {$feed_name}) attempted too many times for entry #{$entry_id}. Attempt number: {$item['attempts']}. Limit: {$max_attempts}." );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$addon->log_debug( __METHOD__ . "(): Starting to process feed (#{$feed_id} - {$feed_name}) for entry #{$entry_id}. Attempt number: {$item['attempts']}." );
|
||||
|
||||
// Keeping the try catch in case any third-party add-ons still throw exceptions.
|
||||
try {
|
||||
|
||||
$result = $addon->process_feed( $feed, $entry, $form );
|
||||
|
||||
} catch ( Exception $e ) {
|
||||
|
||||
$addon->save_entry_feed_status( $e, $entry_id, $feed_id, $form_id );
|
||||
$addon->log_error( __METHOD__ . "(): Aborting. Error occurred during processing of feed (#{$feed_id} - {$feed_name}) for entry #{$entry_id}: {$e->getMessage()}" );
|
||||
|
||||
$error = new WP_Error( $e->getCode(), $e->getMessage() );
|
||||
|
||||
return $addon->is_feed_error_retryable( true, $error, $feed, $entry, $form ) ? $item : false;
|
||||
}
|
||||
|
||||
$addon->save_entry_feed_status( $result, $entry_id, $feed_id, $form_id );
|
||||
|
||||
if ( is_wp_error( $result ) ) {
|
||||
/** @var WP_Error $result */
|
||||
$addon->log_error( __METHOD__ . "(): Aborting. Error occurred during processing of feed (#{$feed_id} - {$feed_name}) for entry #{$entry_id}: {$result->get_error_message()}" );
|
||||
|
||||
return $addon->is_feed_error_retryable( true, $result, $feed, $entry, $form ) ? $item : false;
|
||||
}
|
||||
|
||||
|
||||
// If returned value from the process feed call is an array containing an ID, update entry and set the entry to its value.
|
||||
if ( (int) rgar( $result, 'id' ) === $entry_id ) {
|
||||
|
||||
// Save updated entry.
|
||||
if ( $entry !== $result ) {
|
||||
GFAPI::update_entry( $result );
|
||||
}
|
||||
|
||||
// Set entry to returned entry.
|
||||
$entry = $result;
|
||||
|
||||
}
|
||||
|
||||
$addon->post_process_feed( $feed, $entry, $form );
|
||||
$addon->fulfill_entry( $entry_id, $form_id );
|
||||
|
||||
// Update the entry meta.
|
||||
GFAPI::update_processed_feeds_meta( $entry_id, $addon_slug, $feed_id, $form_id );
|
||||
$addon->log_debug( __METHOD__ . "(): Completed processing of feed (#{$feed_id} - {$feed_name}) for entry #{$entry_id}." );
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the task can be processed based on its attempts property.
|
||||
*
|
||||
* Overridden, returning true, so the existing feed-specific filters are used instead during task().
|
||||
*
|
||||
* @since 2.9.9
|
||||
*
|
||||
* @param mixed $task The task about to be processed.
|
||||
* @param object $batch The batch currently being processed.
|
||||
* @param int $task_num The number that identifies the task in the logs.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function can_process_task( $task, $batch, $task_num ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the feed can be processed based on the contents of the processed feeds entry meta.
|
||||
*
|
||||
* @since 2.9.2
|
||||
*
|
||||
* @param array $entry The entry being processed.
|
||||
* @param array $feed The feed queued for processing.
|
||||
* @param array $form The form the entry belongs to.
|
||||
* @param GFFeedAddOn $addon The current instance of the add-on the feed belongs to.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function can_process_feed( $feed, $entry, $form, $addon ) {
|
||||
return $addon->can_process_feed( $feed, $entry, $form );
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs the error that occurred during feed processing.
|
||||
*
|
||||
* @since 2.9.8
|
||||
*
|
||||
* @param array $error The error returned by error_get_last().
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function handle_error( $error ) {
|
||||
parent::handle_error( $error );
|
||||
|
||||
$task = $this->get_current_task();
|
||||
if ( empty( $task ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( $this->add_on ) {
|
||||
$addon = $this->add_on;
|
||||
} else {
|
||||
$callable = array( $task['addon'], 'get_instance' );
|
||||
if ( ! is_callable( $callable ) ) {
|
||||
return;
|
||||
}
|
||||
$addon = call_user_func( $callable );
|
||||
}
|
||||
|
||||
$feed = $task['feed'];
|
||||
$feed_id = (int) rgar( $feed, 'id' );
|
||||
$entry_id = (int) rgar( $task, 'entry_id' );
|
||||
$addon->log_error( __METHOD__ . "(): Aborting. Error occurred during processing of feed (#{$feed_id} - {$addon->get_feed_name( $feed )}) for entry #{$entry_id}: {$error['message']}" );
|
||||
$addon->save_entry_feed_status( new WP_Error( $error['type'], $error['message'] ), $entry_id, $feed_id, (int) rgar( $task, 'form_id' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments the item attempts property and updates the batch in the database.
|
||||
*
|
||||
* @depecated 2.9.9
|
||||
* @remove-in 4.0
|
||||
*
|
||||
* @since 2.4
|
||||
* @since 2.9.4 Updated to use get_current_branch() instead of making a db request to get the batch.
|
||||
*
|
||||
* @param array $item {
|
||||
* The task arguments.
|
||||
*
|
||||
* @type string $addon The add-on class name.
|
||||
* @type array $feed The feed.
|
||||
* @type int $entry_id The entry ID.
|
||||
* @type int $form_id The form ID.
|
||||
* @type int $attempts The number of processing attempts. Only included if the task has been processed before.
|
||||
* }
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function increment_attempts( $item ) {
|
||||
$batch = $this->get_current_batch();
|
||||
|
||||
$item_feed = rgar( $item, 'feed' );
|
||||
$item_entry_id = rgar( $item, 'entry_id' );
|
||||
|
||||
foreach ( $batch->data as $key => $task ) {
|
||||
$task_feed = rgar( $task, 'feed' );
|
||||
$task_entry_id = rgar( $task, 'entry_id' );
|
||||
if ( $item_feed['id'] === $task_feed['id'] && $item_entry_id === $task_entry_id ) {
|
||||
$batch->data[ $key ]['attempts'] = isset( $batch->data[ $key ]['attempts'] ) ? $batch->data[ $key ]['attempts'] + 1 : 1;
|
||||
$item['attempts'] = $batch->data[ $key ]['attempts'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$this->update( $batch->key, $batch->data );
|
||||
|
||||
return $item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a message to the add-on or core log.
|
||||
*
|
||||
* @since 2.9.25
|
||||
*
|
||||
* @param string $message The message to be logged.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function log_debug( $message ) {
|
||||
if ( $this->add_on ) {
|
||||
$this->add_on->log_debug( $message );
|
||||
} else {
|
||||
parent::log_debug( $message );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes an error message to the add-on or core log.
|
||||
*
|
||||
* @since 2.9.25
|
||||
*
|
||||
* @param string $message The message to be logged.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function log_error( $message ) {
|
||||
if ( $this->add_on ) {
|
||||
$this->add_on->log_error( $message );
|
||||
} else {
|
||||
parent::log_error( $message );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the action portion of logging statements.
|
||||
*
|
||||
* @since 2.9.25
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_action_for_log() {
|
||||
return $this->add_on ? '' : parent::get_action_for_log();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an instance of the GF_Feed_Processor class
|
||||
*
|
||||
* @since 2.2
|
||||
* @since 2.9.25 Added the $add_on param.
|
||||
*
|
||||
* @param null|GFFeedAddOn $add_on Optional. The add-on instance.
|
||||
*
|
||||
* @return GF_Feed_Processor
|
||||
*/
|
||||
function gf_feed_processor( $add_on = null ) {
|
||||
return GF_Feed_Processor::get_instance( $add_on );
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,958 @@
|
||||
<?php
|
||||
|
||||
if ( ! class_exists( 'GFForms' ) ) {
|
||||
die();
|
||||
}
|
||||
|
||||
if ( ! class_exists( 'GFResults' ) ) {
|
||||
|
||||
class GFResults {
|
||||
protected $_slug;
|
||||
protected $_title;
|
||||
protected $_icon;
|
||||
protected $_callbacks;
|
||||
protected $_capabilities;
|
||||
protected $_search_title;
|
||||
|
||||
public function __construct( $slug, $config ) {
|
||||
$this->_slug = $slug;
|
||||
$this->_title = rgar( $config, 'title' );
|
||||
$this->_icon = rgar( $config, 'icon' );
|
||||
$this->_search_title = rgempty( 'search_title', $config ) ? esc_html__( 'Results Filters', 'gravityforms' ) : rgar( $config, 'search_title' );
|
||||
$this->_callbacks = isset( $config['callbacks'] ) ? $config['callbacks'] : array();
|
||||
$this->_capabilities = isset( $config['capabilities'] ) ? $config['capabilities'] : array();
|
||||
}
|
||||
|
||||
public function init() {
|
||||
|
||||
if ( ! GFCommon::current_user_can_any( $this->_capabilities ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// is any GF page
|
||||
if ( GFForms::is_gravity_page() ) {
|
||||
|
||||
// add top toolbar menu item
|
||||
add_filter( 'gform_toolbar_menu', array( $this, 'add_toolbar_menu_item' ), 10, 2 );
|
||||
|
||||
// add custom form action
|
||||
add_filter( 'gform_form_actions', array( $this, 'add_form_action' ), 10, 2 );
|
||||
|
||||
}
|
||||
|
||||
// is results page
|
||||
if ( rgget( 'view' ) == "gf_results_{$this->_slug}" ) {
|
||||
|
||||
// add the results view
|
||||
add_action( 'gform_entries_view', array( $this, 'add_view' ), 10, 2 );
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_scripts' ) );
|
||||
|
||||
// tooltips
|
||||
require_once( GFCommon::get_base_path() . '/tooltips.php' );
|
||||
add_filter( 'gform_tooltips', array( $this, 'add_tooltips' ) );
|
||||
|
||||
add_filter( 'admin_title', array( $this, 'set_unique_page_title' ), 100, 2 );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function enqueue_admin_scripts() {
|
||||
wp_enqueue_script( 'jquery-ui-resizable' );
|
||||
wp_enqueue_script( 'jquery-ui-datepicker' );
|
||||
|
||||
wp_enqueue_script( 'google_charts' );
|
||||
wp_enqueue_style( 'gaddon_results_css' );
|
||||
wp_enqueue_script( 'gaddon_results_js' );
|
||||
$this->localize_results_scripts();
|
||||
}
|
||||
|
||||
public static function localize_results_scripts() {
|
||||
|
||||
// Get current page protocol
|
||||
$protocol = isset( $_SERVER['HTTPS'] ) ? 'https://' : 'http://';
|
||||
// Output admin-ajax.php URL with same protocol as current page
|
||||
|
||||
$vars = array(
|
||||
'ajaxurl' => admin_url( 'admin-ajax.php', $protocol ),
|
||||
'imagesUrl' => GFCommon::get_base_url() . '/images'
|
||||
);
|
||||
|
||||
wp_localize_script( 'gaddon_results_js', 'gresultsVars', $vars );
|
||||
|
||||
$strings = array(
|
||||
'ajaxError' => wp_strip_all_tags( __( 'Error retrieving results. If the problem persists, please contact support.', 'gravityforms' ) )
|
||||
);
|
||||
|
||||
wp_localize_script( 'gaddon_results_js', 'gresultsStrings', $strings );
|
||||
|
||||
}
|
||||
|
||||
private function get_fields( $form ) {
|
||||
return isset( $this->_callbacks['fields'] ) ? call_user_func( $this->_callbacks['fields'], $form ) : $form['fields'];
|
||||
}
|
||||
|
||||
public function add_form_action( $actions, $form_id ) {
|
||||
return $this->filter_menu_items( $actions, $form_id, true );
|
||||
}
|
||||
|
||||
public function add_toolbar_menu_item( $menu_items, $form_id ) {
|
||||
return $this->filter_menu_items( $menu_items, $form_id, false );
|
||||
}
|
||||
|
||||
public function filter_menu_items( $menu_items, $form_id, $compact ) {
|
||||
$form_meta = GFFormsModel::get_form_meta( $form_id );
|
||||
$results_fields = $this->get_fields( $form_meta );
|
||||
if ( false === empty( $results_fields ) ) {
|
||||
$form_id = $form_meta['id'];
|
||||
$link_class = '';
|
||||
$page = GFForms::get_page_query_arg();
|
||||
if ( $page == 'gf_new_form' ) {
|
||||
$link_class = 'gf_toolbar_disabled';
|
||||
} elseif ( $page == 'gf_entries' && rgget( 'view' ) == 'gf_results_' . $this->_slug ) {
|
||||
$link_class = 'gf_toolbar_active';
|
||||
}
|
||||
|
||||
$id = rgget( 'id' );
|
||||
if ( empty( $id ) ) {
|
||||
//on the form list page, do not use icons.
|
||||
$icon = '';
|
||||
} else {
|
||||
$icon = $this->_icon;
|
||||
if ( empty( $icon ) ) {
|
||||
$icon = '<i class="fa fa-bar-chart-o fa-lg"></i>';
|
||||
}
|
||||
}
|
||||
|
||||
$sub_menu_items = array();
|
||||
$sub_menu_items[] = array(
|
||||
'label' => $this->_title,
|
||||
'icon' => $icon,
|
||||
'title' => esc_html__( 'View results generated by this form', 'gravityforms' ),
|
||||
'link_class' => $link_class,
|
||||
'url' => admin_url( "admin.php?page=gf_entries&view=gf_results_{$this->_slug}&id={$form_id}" ),
|
||||
'capabilities' => $this->_capabilities,
|
||||
);
|
||||
|
||||
$duplicate_submenus = wp_filter_object_list( rgars( $menu_items, 'results/sub_menu_items' ), array( 'label' => $sub_menu_items[0]['label'] ) );
|
||||
if ( count( $duplicate_submenus ) > 0 ) {
|
||||
return $menu_items;
|
||||
}
|
||||
|
||||
// If there's already a menu item with the key "results" then merge the two.
|
||||
if ( isset( $menu_items['results'] ) ) {
|
||||
$existing_link_class = $menu_items['results']['link_class'];
|
||||
$link_class == empty( $existing_link_class ) ? $link_class : $existing_link_class;
|
||||
$existing_capabilities = $menu_items['results']['capabilities'];
|
||||
$merged_capabilities = array_merge( $existing_capabilities, $this->_capabilities );
|
||||
$existing_sub_menu_items = isset( $menu_items['results']['sub_menu_items'] ) ? $menu_items['results']['sub_menu_items'] : $menu_items['results']['single_menu_item'];
|
||||
$merged_sub_menu_items = array_merge( $existing_sub_menu_items, $sub_menu_items );
|
||||
$menu_items['results']['link_class'] = $link_class;
|
||||
$menu_items['results']['capabilities'] = $merged_capabilities;
|
||||
$menu_items['results']['sub_menu_items'] = $merged_sub_menu_items;
|
||||
$menu_items['results']['label'] = esc_html__( 'Results', 'gravityforms' );
|
||||
$menu_items['results']['icon'] = '<i class="fa fa-bar-chart-o fa-lg"></i>';
|
||||
|
||||
} else {
|
||||
// so far during the page cycle this is the only menu item for this key.
|
||||
$menu_items['results'] = array(
|
||||
'label' => $compact ? esc_html__( 'Results', 'gravityforms' ) : $this->_title,
|
||||
'icon' => $icon,
|
||||
'title' => esc_attr__( 'View results generated by this form', 'gravityforms' ),
|
||||
'url' => admin_url( "admin.php?page=gf_entries&view=gf_results_{$this->_slug}&id={$form_id}" ),
|
||||
'menu_class' => 'gf_form_toolbar_results',
|
||||
'link_class' => $link_class,
|
||||
'capabilities' => $this->_capabilities,
|
||||
'single_menu_item' => $sub_menu_items,
|
||||
'priority' => 750,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $menu_items;
|
||||
}
|
||||
|
||||
|
||||
public function add_view( $view, $form_id ) {
|
||||
if ( $view == 'gf_results_' . $this->_slug ) {
|
||||
$form_id = absint( $form_id );
|
||||
GFResults::results_page( $form_id, $this->_title, 'gf_entries', $view );
|
||||
}
|
||||
}
|
||||
|
||||
public function results_page($form_id, $page_title, $gf_page, $gf_view ) {
|
||||
$form_id = absint( $form_id );
|
||||
if ( empty( $form_id ) ) {
|
||||
$forms = RGFormsModel::get_forms();
|
||||
if ( ! empty( $forms ) ) {
|
||||
$form_id = $forms[0]->id;
|
||||
}
|
||||
}
|
||||
$form = GFFormsModel::get_form_meta( $form_id );
|
||||
$form = gf_apply_filters( array( 'gform_form_pre_results', $form_id ), $form );
|
||||
|
||||
// Set up filter vars
|
||||
$start_date = preg_replace( '/[^0-9-]/', '', rgget( 'start' ) );
|
||||
$end_date = preg_replace( '/[^0-9-]/', '', rgget( 'end' ) );
|
||||
|
||||
$all_fields = $form['fields'];
|
||||
|
||||
$filter_settings = GFCommon::get_field_filter_settings( $form );
|
||||
$filter_settings = apply_filters( 'gform_filters_pre_results', $filter_settings, $form );
|
||||
$filter_settings = array_values( $filter_settings ); // reset the numeric keys in case some filters have been unset
|
||||
|
||||
$filter_fields = rgget( 'f' );
|
||||
$filter_operators = rgget( 'o' );
|
||||
$filter_values = rgget( 'v' );
|
||||
$filters = array();
|
||||
|
||||
$init_vars = array();
|
||||
if ( ! empty( $filter_fields ) ) {
|
||||
$init_vars['mode'] = rgget( 'mode' );
|
||||
foreach ( $filter_fields as $i => $filter_field ) {
|
||||
$filters[ $i ]['field'] = $filter_field;
|
||||
$filters[ $i ]['operator'] = $filter_operators[ $i ];
|
||||
$filters[ $i ]['value'] = $filter_values[ $i ];
|
||||
}
|
||||
$init_vars['filters'] = $filters;
|
||||
}
|
||||
?>
|
||||
<script type="text/javascript">
|
||||
var gresultsFields = <?php echo json_encode( $all_fields ); ?>;
|
||||
var gresultsFilterSettings = <?php echo json_encode( $filter_settings ); ?>;
|
||||
var gresultsInitVars = <?php echo json_encode( $init_vars ); // nosemgrep scanner.php.lang.security.xss.direct-reflected ?>;
|
||||
|
||||
<?php GFCommon::gf_global() ?>
|
||||
<?php GFCommon::gf_vars() ?>
|
||||
</script>
|
||||
|
||||
<div class="wrap gforms_edit_form <?php echo esc_attr( GFCommon::get_browser_class() ); ?>">
|
||||
|
||||
<?php //GFCommon::form_page_title( $form ); ?>
|
||||
<?php //GFCommon::display_dismissible_message(); ?>
|
||||
<?php //GFForms::top_toolbar(); ?>
|
||||
<?php GFForms::admin_header(array(), true ) ;?>
|
||||
<?php if ( false === empty( $all_fields ) ) : ?>
|
||||
|
||||
<div id="poststuff" class="metabox-holder has-right-sidebar">
|
||||
<div id="side-info-column" class="inner-sidebar">
|
||||
<div id="gresults-results-filter" class="gform-settings-panel__content postbox">
|
||||
<h2><?php echo esc_html( $this->_search_title ); ?></h2>
|
||||
|
||||
<div id="gresults-results-filter-content">
|
||||
<form id="gresults-results-filter-form" action="" method="GET">
|
||||
<?php wp_nonce_field( 'gf_results', '_gf_results_nonce' ); ?>
|
||||
<input type="hidden" id="gresults-page-slug" name="page"
|
||||
value="<?php echo esc_attr( $gf_page ); ?>">
|
||||
<input type="hidden" id="gresults-view-slug" name="view"
|
||||
value="<?php echo esc_attr( $gf_view ); ?>">
|
||||
<input type="hidden" id="gresults-form-id" name="id"
|
||||
value="<?php echo esc_attr( $form_id ); ?>">
|
||||
|
||||
<?php
|
||||
$filter_ui = array(
|
||||
'fields' => array(
|
||||
'label' => esc_attr__( 'Include results if', 'gravityforms' ),
|
||||
'tooltip' => 'gresults_filters',
|
||||
'markup' => '<div class="gform-settings-field__conditional-logic"><div id="gresults-results-field-filters-container">
|
||||
<!-- placeholder populated by js -->
|
||||
</div></div>',
|
||||
),
|
||||
'start_date' => array(
|
||||
'label' => esc_attr__( 'Start date', 'gravityforms' ),
|
||||
'markup' => '<div class="gform-settings-field gform-settings-field__date_time">
|
||||
<span class="gform-settings-input__container"><input type="text" id="gresults-results-filter-date-start" name="start" value="' . esc_attr( $start_date ) . '"/><button type="button" class="ui-datepicker-trigger"><span class="screen-reader-text">'.esc_html__( 'Open Date Picker', 'gravityforms' ).'</span><svg width="18" height="18" role="presentation" focusable="false" fill="#9092B2" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M13.0909 1.6364V1.231C13.0909.5513 13.6357 0 14.3182 0c.6778 0 1.2273.5468 1.2273 1.2311v.4053h.8254c.8997 0 1.6291.7349 1.6291 1.6288v13.106C18 17.2707 17.2721 18 16.3709 18H1.6291C.7294 18 0 17.2651 0 16.3712V3.2652c0-.8996.728-1.6288 1.6291-1.6288h.8254V1.231C2.4545.5513 2.9993 0 3.6818 0c.6778 0 1.2273.5468 1.2273 1.2311v.4053h2.4545V1.231C7.3636.5513 7.9084 0 8.591 0c.6778 0 1.2273.5468 1.2273 1.2311v.4053h3.2727zM1.6364 7.3636v9h14.7272v-9H1.6364z"></path></svg></button></span>
|
||||
</div>',
|
||||
),
|
||||
'end_date' => array(
|
||||
'label' => esc_attr__( 'End date', 'gravityforms' ),
|
||||
'markup' => '<div class="gform-settings-field gform-settings-field__date_time" >
|
||||
<span class="gform-settings-input__container"><input type="text" id="gresults-results-filter-date-end" name="end" value="' . esc_attr( $end_date ) . '"/><button type="button" class="ui-datepicker-trigger"><span class="screen-reader-text">'.esc_html__( 'Open Date Picker', 'gravityforms' ).'</span><svg width="18" height="18" role="presentation" focusable="false" fill="#9092B2" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M13.0909 1.6364V1.231C13.0909.5513 13.6357 0 14.3182 0c.6778 0 1.2273.5468 1.2273 1.2311v.4053h.8254c.8997 0 1.6291.7349 1.6291 1.6288v13.106C18 17.2707 17.2721 18 16.3709 18H1.6291C.7294 18 0 17.2651 0 16.3712V3.2652c0-.8996.728-1.6288 1.6291-1.6288h.8254V1.231C2.4545.5513 2.9993 0 3.6818 0c.6778 0 1.2273.5468 1.2273 1.2311v.4053h2.4545V1.231C7.3636.5513 7.9084 0 8.591 0c.6778 0 1.2273.5468 1.2273 1.2311v.4053h3.2727zM1.6364 7.3636v9h14.7272v-9H1.6364z"></path></svg></button></span>
|
||||
</div>',
|
||||
),
|
||||
);
|
||||
$filter_ui = apply_filters( 'gform_filter_ui', $filter_ui, $form_id, $page_title, $gf_page, $gf_view );
|
||||
|
||||
foreach ( $filter_ui as $name => $filter ) {
|
||||
?>
|
||||
<div class="gform-settings-field__header"><label class='gform-settings-label'><?php echo esc_html( $filter['label'] ); ?><?php gform_tooltip( rgar( $filter, 'tooltip' ), 'tooltip_bottomleft' ) ?></label></div>
|
||||
<?php
|
||||
echo $filter['markup']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
|
||||
<div id="gresults-results-filter-buttons">
|
||||
<input type="submit" id="gresults-results-filter-submit-button"
|
||||
class="button primary large"
|
||||
value="<?php esc_attr_e( 'Apply filters', 'gravityforms' ); ?>">
|
||||
<input type="button" id="gresults-results-filter-clear-button"
|
||||
class="button large"
|
||||
value="<?php esc_attr_e( 'Clear', 'gravityforms' ); ?>"
|
||||
onclick="gresults.clearFilterForm();"
|
||||
onkeypress="gresults.clearFilterForm();">
|
||||
|
||||
<div class="gresults-filter-loading"
|
||||
style="display:none; float:right; margin-top:5px;">
|
||||
<i class='gform-spinner'></i>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="gresults-filter-loading" style="display:none;margin:0 5px 10px 0;">
|
||||
<i class='gform-spinner'></i>
|
||||
<a href="javascript:void(0);" onclick="javascript:gresultsAjaxRequest.abort()" onkeypress="javascript:gresultsAjaxRequest.abort()"><?php esc_html_e( 'Cancel', 'gravityforms' ); ?></a>
|
||||
</div>
|
||||
|
||||
<div id="gresults-results-wrapper">
|
||||
<div id="gresults-results">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
else :
|
||||
esc_html_e( 'This form does not have any fields that can be used for results', 'gravityforms' );
|
||||
endif ?>
|
||||
</div>
|
||||
|
||||
<?php GFForms::admin_footer() ?>
|
||||
|
||||
|
||||
<?php
|
||||
}
|
||||
|
||||
public static function add_tooltips( $tooltips ) {
|
||||
$tooltips['gresults_total_score'] = '<h6>' . esc_html__( 'Total Score', 'gravityforms' ) . '</h6>' . esc_html__( 'Scores are weighted calculations. Items ranked higher are given a greater score than items that are ranked lower. The total score for each item is the sum of the weighted scores.', 'gravityforms' );
|
||||
$tooltips['gresults_agg_rank'] = '<h6>' . esc_html__( 'Aggregate Rank', 'gravityforms' ) . '</h6>' . esc_html__( 'The aggregate rank is the overall rank for all entries based on the weighted scores for each item.', 'gravityforms' );
|
||||
$tooltips['gresults_date_range'] = '<h6>' . esc_html__( 'Date Range', 'gravityforms' ) . '</h6>' . esc_html__( 'Date Range is optional, if no date range is specified it will be ignored.', 'gravityforms' );
|
||||
$tooltips['gresults_filters'] = '<h6>' . esc_html__( 'Filters', 'gravityforms' ) . '</h6>' . esc_html__( 'Narrow the results by adding filters. Note that some field types support more options than others.', 'gravityforms' );
|
||||
$tooltips['gresults_average_row_score'] = '<h6>' . esc_html__( 'Average Row Score', 'gravityforms' ) . '</h6>' . esc_html__( 'The average (mean) score for each row: the sum of all the scores for each row divided by the total number of entries.', 'gravityforms' );
|
||||
$tooltips['gresults_average_global_score'] = '<h6>' . esc_html__( 'Average Global Score', 'gravityforms' ) . '</h6>' . esc_html__( 'The average (mean) score for the whole field. The sum of the total scores divided by the number of entries.', 'gravityforms' );
|
||||
$tooltips['gresults_average_score'] = '<h6>' . esc_html__( 'Average Score', 'gravityforms' ) . '</h6>' . esc_html__( 'The average (mean) score: The sum of the scores divided by the number of entries.', 'gravityforms' );
|
||||
|
||||
return $tooltips;
|
||||
}
|
||||
|
||||
|
||||
public function ajax_get_results() {
|
||||
|
||||
check_ajax_referer( 'gf_results', '_gf_results_nonce' );
|
||||
|
||||
if ( ! GFAPI::current_user_can_any( $this->_capabilities ) ) {
|
||||
wp_die( 'Not allowed' );
|
||||
}
|
||||
|
||||
// tooltips
|
||||
require_once( GFCommon::get_base_path() . '/tooltips.php' );
|
||||
add_filter( 'gform_tooltips', array( $this, 'add_tooltips' ) );
|
||||
|
||||
$output = array();
|
||||
$html = '';
|
||||
$form_id = rgpost( 'id' );
|
||||
$form_id = absint( $form_id );
|
||||
$form = GFFormsModel::get_form_meta( $form_id );
|
||||
$form = gf_apply_filters( array( 'gform_form_pre_results', $form_id ), $form );
|
||||
$search_criteria['status'] = 'active';
|
||||
$fields = $this->get_fields( $form );
|
||||
$total_entries = GFAPI::count_entries( $form_id, $search_criteria );
|
||||
if ( $total_entries == 0 ) {
|
||||
$html = esc_html__( 'No results.', 'gravityforms' );
|
||||
} else {
|
||||
$search_criteria = array();
|
||||
$search_criteria['field_filters'] = GFCommon::get_field_filters_from_post( $form );
|
||||
|
||||
$start_date = preg_replace( '/[^0-9-]/', '', rgpost( 'start' ) );
|
||||
$end_date = preg_replace( '/[^0-9-]/', '', rgpost( 'end' ) );
|
||||
|
||||
if ( $start_date ) {
|
||||
$search_criteria['start_date'] = $start_date;
|
||||
}
|
||||
if ( $end_date ) {
|
||||
$search_criteria['end_date'] = $end_date;
|
||||
}
|
||||
|
||||
$search_criteria['status'] = 'active';
|
||||
$output['s'] = http_build_query( $search_criteria );
|
||||
$state_array = null;
|
||||
if ( isset( $_POST['state'] ) ) {
|
||||
$state = rgpost( 'state' );
|
||||
$posted_check_sum = rgpost( 'checkSum' );
|
||||
$generated_check_sum = self::generate_checksum( $state );
|
||||
$state_array = json_decode( base64_decode( $state ), true );
|
||||
if ( $generated_check_sum !== $posted_check_sum ) {
|
||||
$output['status'] = 'complete';
|
||||
$output['html'] = esc_html__( 'There was an error while processing the entries. Please contact support.', 'gravityforms' );
|
||||
echo json_encode( $output );
|
||||
die();
|
||||
}
|
||||
}
|
||||
|
||||
$data = isset( $this->_callbacks['data'] ) ? call_user_func( $this->_callbacks['data'], $form, $fields, $search_criteria, $state_array ) : $this->get_results_data( $form, $fields, $search_criteria, $state_array );
|
||||
$entry_count = $data['entry_count'];
|
||||
|
||||
if ( 'incomplete' === rgar( $data, 'status' ) ) {
|
||||
$state = base64_encode( json_encode( $data ) );
|
||||
$output['status'] = 'incomplete';
|
||||
$output['stateObject'] = $state;
|
||||
$output['checkSum'] = self::generate_checksum( $state );
|
||||
$output['html'] = sprintf( esc_html__( 'Entries processed: %1$d of %2$d', 'gravityforms' ), rgar( $data, 'offset' ), $entry_count );
|
||||
echo json_encode( $output );
|
||||
die();
|
||||
}
|
||||
|
||||
if ( $total_entries > 0 ) {
|
||||
$html = isset( $this->_callbacks['markup'] ) ? call_user_func( $this->_callbacks['markup'], $html, $data, $form, $fields ) : '';
|
||||
if ( empty( $html ) ) {
|
||||
foreach ( $fields as $field ) {
|
||||
$field_id = $field->id;
|
||||
$html .= "<div class='gresults-results-field gform-settings-panel' id='gresults-results-field-{$field_id}'>";
|
||||
$html .= "<header class='gform-settings-panel__header'><legend class='gform-settings-panel__title'>" . esc_html( GFCommon::get_label( $field ) ) . '</legend></header>';
|
||||
$html .= '<div class="gform-settings-panel__content">' . self::get_field_results( $form_id, $data, $field, $search_criteria ) . '</div>';
|
||||
$html .= '</div>';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$html .= esc_html__( 'No results', 'gravityforms' );
|
||||
}
|
||||
}
|
||||
|
||||
$output['html'] = $html;
|
||||
$output['status'] = 'complete';
|
||||
$output['searchCriteria'] = $search_criteria;
|
||||
echo json_encode( $output );
|
||||
die();
|
||||
}
|
||||
|
||||
|
||||
public function ajax_get_more_results() {
|
||||
|
||||
check_ajax_referer( 'gf_results', '_gf_results_nonce' );
|
||||
|
||||
if ( ! GFAPI::current_user_can_any( $this->_capabilities ) ) {
|
||||
wp_die( 'Not allowed' );
|
||||
}
|
||||
|
||||
$form_id = rgpost( 'form_id' );
|
||||
$field_id = rgpost( 'field_id' );
|
||||
$offset = rgpost( 'offset' );
|
||||
$search_criteria = rgpost( 'search_criteria' );
|
||||
|
||||
if ( empty( $search_criteria ) ) {
|
||||
$search_criteria = array();
|
||||
}
|
||||
$page_size = 10;
|
||||
|
||||
$form = RGFormsModel::get_form_meta( $form_id );
|
||||
$form_id = $form['id'];
|
||||
$field = RGFormsModel::get_field( $form, $field_id );
|
||||
$more_remaining = false;
|
||||
$html = self::get_default_field_results( $form_id, $field, $search_criteria, $offset, $page_size, $more_remaining );
|
||||
|
||||
$response = array();
|
||||
$response['more_remaining'] = $more_remaining;
|
||||
$response['html'] = $html;
|
||||
$response['offset'] = $offset;
|
||||
|
||||
echo json_encode( $response ); // nosemgrep scanner.php.lang.security.xss.direct-reflected
|
||||
die();
|
||||
}
|
||||
|
||||
private static function generate_checksum( $data ) {
|
||||
return wp_hash( crc32( ( $data ) ) );
|
||||
}
|
||||
|
||||
|
||||
public static function get_total_entries( $form ) {
|
||||
$totals = RGFormsModel::get_form_counts( $form['id'] );
|
||||
|
||||
return $totals['total'];
|
||||
}
|
||||
|
||||
public static function get_field_results( $form_id, $data, $field, $search_criteria ) {
|
||||
|
||||
if ( empty( $data['entry_count'] ) || empty ( $data['field_data'] ) ) {
|
||||
return esc_html__( 'No entries for this field', 'gravityforms' );
|
||||
}
|
||||
|
||||
$field_data = $data['field_data'];
|
||||
$entry_count = $data['entry_count'];
|
||||
|
||||
if ( empty( $field_data[ $field->id ] ) ) {
|
||||
return esc_html__( 'No entries for this field', 'gravityforms' );
|
||||
}
|
||||
|
||||
$field_results = '';
|
||||
|
||||
$field_type = GFFormsModel::get_input_type( $field );
|
||||
switch ( $field_type ) {
|
||||
case 'radio' :
|
||||
case 'checkbox' :
|
||||
case 'select' :
|
||||
case 'rating' :
|
||||
case 'multiselect' :
|
||||
$results = $field_data[ $field->id ];
|
||||
$non_zero_results = is_array( $results ) ? array_filter( $results ) : $results;
|
||||
if ( empty( $non_zero_results ) ) {
|
||||
$field_results .= esc_html__( 'No entries for this field', 'gravityforms' );
|
||||
|
||||
return $field_results;
|
||||
}
|
||||
$choices = $field->choices;
|
||||
|
||||
$data_table = array();
|
||||
$data_table [] = array( esc_html__( 'Choice', 'gravityforms' ), esc_html__( 'Frequency', 'gravityforms' ) );
|
||||
|
||||
foreach ( $choices as $choice ) {
|
||||
$text = $choice['text'];
|
||||
$val = $results[ $choice['value'] ];
|
||||
$data_table [] = array( $text, $val );
|
||||
}
|
||||
|
||||
$bar_height = 40;
|
||||
$chart_area_height = ( count( $choices ) * ( $bar_height + 20 ) );
|
||||
|
||||
$chart_options = array(
|
||||
'isStacked' => true,
|
||||
'height' => ( $chart_area_height + $bar_height ),
|
||||
'fontSize' => 14,
|
||||
'chartArea' => array(
|
||||
'top' => 0,
|
||||
'left' => 200,
|
||||
'height' => $chart_area_height,
|
||||
'width' => '100%',
|
||||
),
|
||||
'series' => array(
|
||||
'0' => array(
|
||||
'color' => 'silver',
|
||||
'visibleInLegend' => 'false',
|
||||
),
|
||||
),
|
||||
'hAxis' => array(
|
||||
'viewWindowMode' => 'explicit',
|
||||
'viewWindow' => array( 'min' => 0 ),
|
||||
'title' => esc_html__( 'Frequency', 'gravityforms' ),
|
||||
),
|
||||
'tooltip' => array(
|
||||
'isHtml' => true,
|
||||
),
|
||||
);
|
||||
|
||||
$data_table_json = htmlentities( json_encode( $data_table ), ENT_QUOTES, 'UTF-8', true );
|
||||
$options_json = htmlentities( json_encode( $chart_options ), ENT_QUOTES, 'UTF-8', true );
|
||||
$div_id = 'gresults-results-chart-field-' . $field->id;
|
||||
$height = ''; // = sprintf("height:%dpx", (count($choices) * $bar_height));
|
||||
|
||||
$field_results .= sprintf( '<div class="gresults-chart-wrapper" style="width: 100%%;%s" id=%s data-datatable=\'%s\' data-options=\'%s\' data-charttype="bar" ></div>', $height, $div_id, $data_table_json, $options_json );
|
||||
|
||||
|
||||
break;
|
||||
case 'likert' :
|
||||
$results = $field_data[ $field->id ];
|
||||
$multiple_rows = $field->gsurveyLikertEnableMultipleRows ? true : false;
|
||||
$scoring_enabled = $field->gsurveyLikertEnableScoring && class_exists( 'GFSurvey' ) ? true : false;
|
||||
|
||||
$n = 100;
|
||||
|
||||
$xr = 255;
|
||||
$xg = 255;
|
||||
$xb = 255;
|
||||
|
||||
$yr = 100;
|
||||
$yg = 250;
|
||||
$yb = 100;
|
||||
|
||||
$field_results .= "<div class='gsurvey-likert-field-results'>";
|
||||
$field_results .= "<table class='gsurvey-likert'>";
|
||||
$field_results .= '<tr>';
|
||||
if ( $multiple_rows ) {
|
||||
$field_results .= '<td></td>';
|
||||
}
|
||||
|
||||
foreach ( $field->choices as $choice ) {
|
||||
$field_results .= "<td class='gsurvey-likert-choice-label'>" . $choice['text'] . '</td>';
|
||||
}
|
||||
|
||||
if ( $multiple_rows && $scoring_enabled ) {
|
||||
$field_results .= sprintf( '<td>%s %s</td>', esc_html__( 'Average Score', 'gravityforms' ), gform_tooltip( 'gresults_average_row_score', null, true ) );
|
||||
}
|
||||
|
||||
$field_results .= '</tr>';
|
||||
|
||||
foreach ( $field->gsurveyLikertRows as $row ) {
|
||||
$row_text = $row['text'];
|
||||
$row_value = $row['value'];
|
||||
$max = 0;
|
||||
foreach ( $field->choices as $choice ) {
|
||||
if ( $multiple_rows ) {
|
||||
$choice_value = rgar( $choice, 'value' );
|
||||
$results_row = rgar( $results, $row_value );
|
||||
$results_for_choice = rgar( $results_row, $choice_value );
|
||||
$max = max( array( $max, $results_for_choice ) );
|
||||
} else {
|
||||
$max = max( array( $max, $results[ $choice['value'] ] ) );
|
||||
}
|
||||
}
|
||||
|
||||
$field_results .= '<tr>';
|
||||
|
||||
if ( $multiple_rows ) {
|
||||
$field_results .= "<td class='gsurvey-likert-row-label'>" . $row_text . '</td>';
|
||||
}
|
||||
|
||||
foreach ( $field->choices as $choice ) {
|
||||
$val = $multiple_rows ? $results[ $row_value ][ $choice['value'] ] : $results[ $choice['value'] ];
|
||||
$percent = $max > 0 ? round( $val / $max * 100, 0 ) : 0;
|
||||
$red = (int) ( ( $xr + ( ( $percent * ( $yr - $xr ) ) / ( $n - 1 ) ) ) );
|
||||
$green = (int) ( ( $xg + ( ( $percent * ( $yg - $xg ) ) / ( $n - 1 ) ) ) );
|
||||
$blue = (int) ( ( $xb + ( ( $percent * ( $yb - $xb ) ) / ( $n - 1 ) ) ) );
|
||||
$clr = 'rgb(' . $red . ',' . $green . ',' . $blue . ')';
|
||||
$field_results .= "<td class='gsurvey-likert-results' style='background-color:{$clr}'>" . $val . '</td>';
|
||||
}
|
||||
|
||||
if ( $multiple_rows && $scoring_enabled ) {
|
||||
$row_sum = $results[ $row_value ]['row_score_sum'];
|
||||
$average_row_score = $row_sum == 0 ? 0 : round( $row_sum / $entry_count, 3 );
|
||||
$field_results .= "<td class='gsurvey-likert-results'>" . $average_row_score . '</td>';
|
||||
}
|
||||
|
||||
$field_results .= '</tr>';
|
||||
|
||||
if ( false === $multiple_rows ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
$field_results .= '</table>';
|
||||
$field_results .= '</div>';
|
||||
|
||||
if ( $scoring_enabled ) {
|
||||
$sum = $results['sum_of_scores'];
|
||||
$average_score = $sum == 0 ? 0 : round( $sum / $entry_count, 3 );
|
||||
if ( $multiple_rows ) {
|
||||
$average_global_score_tooltip = gform_tooltip( 'gresults_average_global_score', null, true );
|
||||
$field_results .= sprintf( "<div class='gsurvey-likert-score'>%s %s: %s</div>", esc_html__( 'Average global score', 'gravityforms' ), $average_global_score_tooltip, $average_score );
|
||||
} else {
|
||||
$field_results .= sprintf( "<div class='gsurvey-likert-score'>%s %s: %s</div>", esc_html__( 'Average score', 'gravityforms' ), gform_tooltip( 'gresults_average_score', null, true ), $average_score );
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case 'rank' :
|
||||
$results = $field_data[ $field->id ];
|
||||
arsort( $results );
|
||||
$field_results .= "<div class='gsurvey-rank-field-results'>";
|
||||
$field_results .= ' <table>';
|
||||
$field_results .= " <tr class='gresults-results-field-table-header'>";
|
||||
$field_results .= " <td class='gresults-rank-field-label'>";
|
||||
$field_results .= esc_html__( 'Item', 'gravityforms' );
|
||||
$field_results .= ' </td>';
|
||||
$field_results .= " <td class='gresults-rank-field-score'>";
|
||||
$field_results .= esc_html__( 'Total Score', 'gravityforms' ) . ' ' . gform_tooltip( 'gresults_total_score', null, true );
|
||||
$field_results .= ' </td>';
|
||||
$field_results .= " <td class='gresults-rank-field-rank'>";
|
||||
$field_results .= esc_html__( 'Aggregate Rank', 'gravityforms' ) . ' ' . gform_tooltip( 'gresults_agg_rank', null, true );
|
||||
$field_results .= ' </td>';
|
||||
$field_results .= ' </tr>';
|
||||
|
||||
$agg_rank = 1;
|
||||
foreach ( $results as $choice_val => $score ) {
|
||||
$field_results .= '<tr>';
|
||||
$field_results .= " <td class='gresults-rank-field-label' style='text-align:left;'>";
|
||||
$field_results .= RGFormsModel::get_choice_text( $field, $choice_val );
|
||||
$field_results .= ' </td>';
|
||||
$field_results .= " <td class='gresults-rank-field-score'>";
|
||||
$field_results .= $score;
|
||||
$field_results .= ' </td>';
|
||||
$field_results .= " <td class='gresults-rank-field-rank'>";
|
||||
$field_results .= $agg_rank;
|
||||
$field_results .= ' </td>';
|
||||
$field_results .= '</tr>';
|
||||
$agg_rank ++;
|
||||
}
|
||||
$field_results .= '</table>';
|
||||
$field_results .= '</div>';
|
||||
|
||||
break;
|
||||
default :
|
||||
$page_size = 5;
|
||||
$offset = 0;
|
||||
$field_id = $field->id;
|
||||
$more_remaining = false;
|
||||
$default_field_results = self::get_default_field_results( $form_id, $field, $search_criteria, $offset, $page_size, $more_remaining );
|
||||
|
||||
$field_results .= "<div class='gresults-results-field-sub-label'>" . esc_html__( 'Latest values:', 'gravityforms' ) . '</div>';
|
||||
|
||||
$field_results .= "<ul id='gresults-results-field-content-{$field_id}' class='gresults-results-field-content' data-offset='{$offset}'>";
|
||||
|
||||
$field_results .= $default_field_results;
|
||||
$field_results .= '</ul>';
|
||||
|
||||
if ( $more_remaining ) {
|
||||
$field_results .= "<a id='gresults-results-field-more-link-{$field_id}' class='gresults-results-field-more-link' href='javascript:void(0)' onclick='gresults.getMoreResults({$form_id},{$field_id})' onkeypress='gresults.getMoreResults({$form_id},{$field_id})'>" . esc_html__( 'Show more', 'gravityforms' ) . '</a>';
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return $field_results;
|
||||
|
||||
}
|
||||
|
||||
public function get_results_data( $form, $fields, $search_criteria = array(), $state_array = array(), $max_execution_time = 15 /* seconds */ ) {
|
||||
// todo: add hooks to modify $max_execution_time and $page_size?
|
||||
|
||||
$page_size = 150;
|
||||
|
||||
$time_start = microtime( true );
|
||||
|
||||
$form_id = $form['id'];
|
||||
$data = array();
|
||||
$offset = 0;
|
||||
$entry_count = 0;
|
||||
$field_data = array();
|
||||
|
||||
|
||||
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 ) {
|
||||
$field_type = GFFormsModel::get_input_type( $field );
|
||||
if ( false === isset( $field->choices ) ) {
|
||||
$field_data[ $field->id ] = 0;
|
||||
continue;
|
||||
}
|
||||
$choices = $field->choices;
|
||||
|
||||
if ( $field_type == 'likert' && rgar( $field, 'gsurveyLikertEnableMultipleRows' ) ) {
|
||||
foreach ( $field->gsurveyLikertRows as $row ) {
|
||||
foreach ( $choices as $choice ) {
|
||||
$field_data[ $field->id ][ $row['value'] ][ $choice['value'] ] = 0;
|
||||
}
|
||||
if ( rgar( $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_leads = GFAPI::count_entries( $form_id, $search_criteria );
|
||||
$data['entry_count'] = $count_search_leads;
|
||||
|
||||
$entries_left = $count_search_leads - $offset;
|
||||
|
||||
while ( $entries_left > 0 ) {
|
||||
|
||||
$paging = array(
|
||||
'offset' => $offset,
|
||||
'page_size' => $page_size,
|
||||
);
|
||||
|
||||
$search_leads_time_start = microtime( true );
|
||||
$leads = GFFormsModel::search_leads( $form_id, $search_criteria, null, $paging );
|
||||
$search_leads_time_end = microtime( true );
|
||||
$search_leads_time = $search_leads_time_end - $search_leads_time_start;
|
||||
|
||||
$leads_in_search = count( $leads );
|
||||
|
||||
$entry_count += $leads_in_search;
|
||||
$leads_processed = 0;
|
||||
foreach ( $leads as $lead ) {
|
||||
|
||||
$lead_time_start = microtime( true );
|
||||
foreach ( $fields as $field ) {
|
||||
$field_type = GFFormsModel::get_input_type( $field );
|
||||
$field_id = $field->id;
|
||||
$value = RGFormsModel::get_lead_field_value( $lead, $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, $lead );
|
||||
}
|
||||
}
|
||||
}
|
||||
} 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 ( false === empty( $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 ( RGFormsModel::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, $lead );
|
||||
}
|
||||
}
|
||||
$leads_processed ++;
|
||||
$lead_time_end = microtime( true );
|
||||
$total_execution_time = $lead_time_end - $search_leads_time_start;
|
||||
$lead_execution_time = $lead_time_end - $lead_time_start;
|
||||
if ( $total_execution_time + $lead_execution_time > $max_execution_time ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
$data['field_data'] = $field_data;
|
||||
if ( isset( $this->_callbacks['calculation'] ) ) {
|
||||
$data = call_user_func( $this->_callbacks['calculation'], $data, $form, $fields, $leads );
|
||||
$field_data = $data['field_data'];
|
||||
}
|
||||
$offset += $leads_processed;
|
||||
$entries_left -= $leads_processed;
|
||||
|
||||
$time_end = microtime( true );
|
||||
$execution_time = ( $time_end - $time_start );
|
||||
|
||||
if ( $entries_left > 0 && $execution_time + $search_leads_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;
|
||||
}
|
||||
|
||||
public 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;
|
||||
}
|
||||
|
||||
public function get_likert_score( $field, $entry ) {
|
||||
return is_callable( array(
|
||||
'GFSurvey',
|
||||
'get_field_score',
|
||||
) ) ? GFSurvey::get_field_score( $field, $entry ) : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a unique page title to the results page based on the title
|
||||
* and the form the user is viewing.
|
||||
*
|
||||
* @since 2.8.16
|
||||
*
|
||||
* @filter admin_title
|
||||
*
|
||||
* @param string $admin_title The page title with extra context added.
|
||||
* @param string $title The original page title.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function set_unique_page_title( $admin_title, $title ) {
|
||||
$form_id = rgget( 'id' );
|
||||
if ( ! $form_id ) {
|
||||
$forms = RGFormsModel::get_forms( null, 'title' );
|
||||
$form_id = ( ! empty( $forms ) ) ? $forms[0]->id : '';
|
||||
}
|
||||
|
||||
$form = GFAPI::get_form( $form_id );
|
||||
$form_title = rgar( $form, 'title', esc_html__( 'Form Not Found', 'gravityforms' ) );
|
||||
$admin_title = sprintf( '%1$s ‹ %2$s ‹ %3$s', esc_html( $this->_title ), esc_html( $form_title ), esc_html( $admin_title ) );
|
||||
|
||||
return $admin_title;
|
||||
}
|
||||
|
||||
public static function get_default_field_results( $form_id, $field, $search_criteria, &$offset, $page_size, &$more_remaining = false ) {
|
||||
$field_results = '';
|
||||
|
||||
$sorting = array( 'key' => 'date_created', 'direction' => 'DESC' );
|
||||
|
||||
$c = 0;
|
||||
|
||||
do {
|
||||
$paging = array( 'offset' => $offset, 'page_size' => $page_size );
|
||||
$leads = GFFormsModel::search_leads( $form_id, $search_criteria, $sorting, $paging );
|
||||
foreach ( $leads as $lead ) {
|
||||
|
||||
$value = RGFormsModel::get_lead_field_value( $lead, $field );
|
||||
|
||||
$content = apply_filters( 'gform_entries_field_value', $value, $form_id, $field->id, $lead );
|
||||
|
||||
if ( is_array( $content ) ) {
|
||||
$content = join( ' ', $content );
|
||||
}
|
||||
|
||||
if ( ! empty( $content ) ) {
|
||||
$field_results .= "<li>" . wp_kses_post( $content ) . "</li>";
|
||||
$c ++;
|
||||
}
|
||||
}
|
||||
$offset += $page_size;
|
||||
|
||||
} while ( $c < $page_size && ! empty( $leads ) );
|
||||
|
||||
if ( ! empty( $leads ) ) {
|
||||
$more_remaining = true;
|
||||
}
|
||||
|
||||
return $field_results;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
.settings-field-map-table { }
|
||||
.settings-field-map-table thead th { font-weight: bold; border-bottom: 1px solid #ccc; }
|
||||
.settings-field-map-table tbody td { border-bottom: 1px dotted #eee; }
|
||||
.settings-field-map-table select { max-width: 90%; }
|
||||
|
||||
.settings-field-map-table .custom-key-reset,
|
||||
.settings-field-map-table .custom-value-reset,
|
||||
.gaddon-setting-select-custom-container .select-custom-reset {
|
||||
background: url( ../../../images/xit.gif ) no-repeat scroll 0 0 transparent;
|
||||
cursor:pointer;
|
||||
display:none;
|
||||
position:absolute;
|
||||
text-indent:-9999px;
|
||||
width:10px;
|
||||
height: 10px;
|
||||
-moz-transition: none;
|
||||
-webkit-transition: none;
|
||||
-o-transition: color 0 ease-in;
|
||||
transition: none;
|
||||
}
|
||||
.settings-field-map-table .custom-key-reset {
|
||||
margin-top: 10px;
|
||||
margin-left: 165px;
|
||||
}
|
||||
.settings-field-map-table .repeater th { padding-left: 0px; }
|
||||
|
||||
.settings-field-map-table .custom-key-reset:hover,
|
||||
.settings-field-map-table .custom-value-reset:hover,
|
||||
.gaddon-setting-select-custom-container .select-custom-reset:hover {
|
||||
background-position-x: -10px;
|
||||
}
|
||||
|
||||
.settings-field-map-table .custom-key-container:hover .custom-key-reset,
|
||||
.settings-field-map-table .custom-key-container:hover .custom-value-reset,
|
||||
.gaddon-setting-select-custom-container:hover .select-custom-reset {
|
||||
display:block;
|
||||
}
|
||||
|
||||
.gaddon-setting-select-custom-container { display:inline-block;position:relative; }
|
||||
.gaddon-setting-select-custom-container .select-custom-reset {
|
||||
left: 171px;
|
||||
top: 10px;
|
||||
}
|
||||
|
||||
.gaddon-section .required { color: #f00; }
|
||||
.gaddon-setting-inline{
|
||||
display:inline;
|
||||
margin-right:6px;
|
||||
}
|
||||
|
||||
.mt-gaddon-editor {
|
||||
float: right;
|
||||
position: relative;
|
||||
right: 21px;
|
||||
top: 70px;
|
||||
}
|
||||
|
||||
.mt-gaddon-editor ~ .wp-editor-wrap {
|
||||
margin-right: 23px;
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
.settings-field-map-table thead th{font-weight:700;border-bottom:1px solid #ccc}.settings-field-map-table tbody td{border-bottom:1px dotted #eee}.settings-field-map-table select{max-width:90%}.gaddon-setting-select-custom-container .select-custom-reset,.settings-field-map-table .custom-key-reset,.settings-field-map-table .custom-value-reset{background:url(../../../images/xit.gif) no-repeat scroll 0 0 transparent;cursor:pointer;display:none;position:absolute;text-indent:-9999px;width:10px;height:10px;-moz-transition:none;-webkit-transition:none;-o-transition:color 0 ease-in;transition:none}.settings-field-map-table .custom-key-reset{margin-top:10px;margin-left:165px}.settings-field-map-table .repeater th{padding-left:0}.gaddon-setting-select-custom-container .select-custom-reset:hover,.settings-field-map-table .custom-key-reset:hover,.settings-field-map-table .custom-value-reset:hover{background-position-x:-10px}.gaddon-setting-select-custom-container:hover .select-custom-reset,.settings-field-map-table .custom-key-container:hover .custom-key-reset,.settings-field-map-table .custom-key-container:hover .custom-value-reset{display:block}.gaddon-setting-select-custom-container{display:inline-block;position:relative}.gaddon-setting-select-custom-container .select-custom-reset{left:171px;top:10px}.gaddon-section .required{color:red}.gaddon-setting-inline{display:inline;margin-right:6px}.mt-gaddon-editor{float:right;position:relative;right:21px;top:70px}.mt-gaddon-editor~.wp-editor-wrap{margin-right:23px}
|
||||
@@ -0,0 +1,293 @@
|
||||
/* --------- TODO: remove quiz specific styles ------------------*/
|
||||
|
||||
table#gquiz-results-summary {
|
||||
table-layout: fixed;
|
||||
}
|
||||
|
||||
table#gquiz-results-summary td {
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
table#gquiz-results-summary td.gquiz-results-summary-label {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
table#gquiz-results-summary td.gquiz-results-summary-data {
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
.gquiz-results-summary-data-box {
|
||||
width: 75%;
|
||||
padding: 15px 0;
|
||||
min-width: 0;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.gaddon-results-summary-secondary{
|
||||
padding-top: 4px;
|
||||
font-weight: normal;
|
||||
font-size:13px;
|
||||
}
|
||||
|
||||
.gaddon-results-summary-primary{
|
||||
padding:5px;
|
||||
}
|
||||
|
||||
.gquiz-field-precentages-correct {
|
||||
float: inline-start;
|
||||
margin-block-start: 8px;
|
||||
margin-inline-start: 64px;
|
||||
}
|
||||
|
||||
/* -------------------------------------*/
|
||||
|
||||
table#gaddon-results-summary {
|
||||
table-layout: fixed;
|
||||
}
|
||||
|
||||
table#gaddon-results-summary td {
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
table#gaddon-results-summary td.gaddon-results-summary-label {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
table#gaddon-results-summary td.gaddon-results-summary-data {
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
.gaddon-results-summary-data-box {
|
||||
border: 1px solid silver;
|
||||
padding: 10px;
|
||||
width: 75%;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.gaddon-field-precentages-correct {
|
||||
float: left;
|
||||
margin: 15px 0 0 50px;
|
||||
}
|
||||
|
||||
|
||||
/*-------------*/
|
||||
#gresults-results {
|
||||
margin-right: 300px;
|
||||
}
|
||||
|
||||
.gform-admin .gresults-filter-loading .gform-spinner {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.gf-results .gf-result-box {
|
||||
background: #fff;
|
||||
border-radius: 3px;
|
||||
border: 1px solid #e3e6ef;
|
||||
box-shadow: 0 1px 4px rgb(18 25 97 / 8%);
|
||||
padding: 1.562rem 1rem;
|
||||
}
|
||||
|
||||
.gf-results .gf-result-box {
|
||||
margin-right: 0;
|
||||
margin-bottom: 1.25rem;
|
||||
}
|
||||
|
||||
.gf-results .gf-result-box__primary {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
@media screen and ( min-width: 782px ) {
|
||||
.gf-results {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 1.25rem;
|
||||
}
|
||||
|
||||
.gf-results .gf-result-box {
|
||||
flex: 1 1 0px;
|
||||
margin-inline-end: 1.25rem;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.gf-results .gf-result-box:last-child {
|
||||
margin-inline-end: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.gresults-results-field {
|
||||
margin-bottom:20px;
|
||||
}
|
||||
|
||||
.gresults-results-field table{
|
||||
border-bottom: 1px solid silver;
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
.gresults-results-field table td{
|
||||
padding:5px;
|
||||
border-right: 1px solid silver;
|
||||
border-top: 1px solid silver;
|
||||
text-align:center;
|
||||
}
|
||||
|
||||
.gresults-results-field-table-header{
|
||||
background-color: #eeeeee;
|
||||
}
|
||||
|
||||
.gresults-results-field table td:first-child {
|
||||
border-left: 1px solid silver;
|
||||
}
|
||||
|
||||
|
||||
ul.gresults-results-field-content,
|
||||
ul.gresults-results-field-content li{
|
||||
list-style:disc outside none;
|
||||
}
|
||||
ul.gresults-results-field-content{
|
||||
margin:10px;
|
||||
}
|
||||
|
||||
td.gresults-rank-field-score,
|
||||
td.gresults-rank-field-rank{
|
||||
width:110px;
|
||||
}
|
||||
|
||||
.gsurvey-rank-field-results table{
|
||||
width:100%;
|
||||
}
|
||||
|
||||
|
||||
/* filter box */
|
||||
|
||||
#gresults-results-filter-content {
|
||||
padding:12px;
|
||||
border-top: 1px solid #EBEBF2
|
||||
}
|
||||
|
||||
|
||||
.gresults-results-filter-section-label {
|
||||
font-size:1.2em;
|
||||
font-weight:bold;
|
||||
margin-top:10px;
|
||||
margin-bottom:10px;
|
||||
}
|
||||
|
||||
#gresults-results-filter label {
|
||||
margin-top:10px;
|
||||
display:block;
|
||||
}
|
||||
|
||||
#gresults-results-filter-content #gform-no-filters {
|
||||
color: #242748;
|
||||
}
|
||||
|
||||
.gresults-remove,
|
||||
.gresults-add {
|
||||
margin-top:2px;
|
||||
vertical-align:middle;
|
||||
cursor:pointer;
|
||||
}
|
||||
|
||||
.gresults-add {
|
||||
margin-left:5px;
|
||||
}
|
||||
|
||||
#gresults-no-filters{
|
||||
color:silver;
|
||||
}
|
||||
|
||||
.gresults-datepicker,
|
||||
.gresults-filter-value,
|
||||
.gresults-filter-field {
|
||||
width:150px;
|
||||
box-sizing:border-box;
|
||||
-ms-box-sizing:border-box;
|
||||
-moz-box-sizing:border-box;
|
||||
-webkit-box-sizing:border-box;
|
||||
height: 2em;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.gresults-filter-operator {
|
||||
width:70px;
|
||||
}
|
||||
|
||||
#gresults-results-filter-buttons {
|
||||
clear:both;
|
||||
margin-top:20px;
|
||||
width:180px;
|
||||
}
|
||||
|
||||
.gresults-results-filter-field-label {
|
||||
font-size:1.1em;
|
||||
font-weight:bold;
|
||||
margin-bottom:10px;
|
||||
}
|
||||
|
||||
.gresults-results-filter-field {
|
||||
margin-bottom:20px;
|
||||
}
|
||||
|
||||
.gresults-results-filter-field ul li label {
|
||||
margin-left:5px;
|
||||
}
|
||||
|
||||
.gresults-results-filter-title {
|
||||
font-size:1.5em;
|
||||
font-weight:bold;
|
||||
}
|
||||
|
||||
/*
|
||||
#gresults-results-filter {
|
||||
visibility: hidden;
|
||||
}
|
||||
*/
|
||||
|
||||
#gresults-results-field-filters-container.resizable {
|
||||
border-bottom:5px double #ddd;
|
||||
min-height:120px;
|
||||
}
|
||||
|
||||
#gresults-results-field-filters {
|
||||
height:100%;
|
||||
overflow-y:auto;
|
||||
}
|
||||
|
||||
.ui-resizable {
|
||||
position:relative;
|
||||
}
|
||||
|
||||
.ui-resizable-handle {
|
||||
position:absolute;
|
||||
font-size:0.1px;
|
||||
z-index:99999;
|
||||
display:block;
|
||||
}
|
||||
|
||||
.ui-resizable-s {
|
||||
cursor:s-resize;
|
||||
height:7px;
|
||||
width:100%;
|
||||
bottom:-5px;
|
||||
left:0px;
|
||||
}
|
||||
|
||||
.gsurvey-likert-score{
|
||||
margin-top:5px;
|
||||
}
|
||||
|
||||
/* NEW */
|
||||
|
||||
#gquiz-results-summary { margin: 60px 0; }
|
||||
|
||||
.gresults-chart-wrapper { border-top: 1px solid #dfdfdf; margin: 0 0 28px; }
|
||||
.gquiz-field-precentages-correct + .gresults-chart-wrapper { margin: 0 0 14px; }
|
||||
|
||||
.gresults-label-group { display: block; clear: right; }
|
||||
.gresults-label-group .gresults-label { display: inline-block; width: 65px; }
|
||||
.gresults-group-correct .gresults-value { color: green; }
|
||||
.gresults-group-incorrect .gresults-value { color: red; }
|
||||
@@ -0,0 +1 @@
|
||||
table#gquiz-results-summary{table-layout:fixed}table#gquiz-results-summary td{font-weight:700;text-align:center;padding:10px}table#gquiz-results-summary td.gquiz-results-summary-label{font-size:1.2em}table#gquiz-results-summary td.gquiz-results-summary-data{font-size:2em}.gquiz-results-summary-data-box{width:75%;padding:15px 0;min-width:0;margin:auto}.gaddon-results-summary-secondary{padding-top:4px;font-weight:400;font-size:13px}.gaddon-results-summary-primary{padding:5px}.gquiz-field-precentages-correct{float:inline-start;margin-block-start:8px;margin-inline-start:64px}table#gaddon-results-summary{table-layout:fixed}table#gaddon-results-summary td{font-weight:700;text-align:center;padding:10px}table#gaddon-results-summary td.gaddon-results-summary-label{font-size:1.2em}table#gaddon-results-summary td.gaddon-results-summary-data{font-size:2em}.gaddon-results-summary-data-box{border:1px solid silver;padding:10px;width:75%;margin:auto}.gaddon-field-precentages-correct{float:left;margin:15px 0 0 50px}#gresults-results{margin-right:300px}.gform-admin .gresults-filter-loading .gform-spinner{display:inline-block}.gf-results .gf-result-box{background:#fff;border-radius:3px;border:1px solid #e3e6ef;box-shadow:0 1px 4px rgb(18 25 97 / 8%);padding:1.562rem 1rem}.gf-results .gf-result-box{margin-right:0;margin-bottom:1.25rem}.gf-results .gf-result-box__primary{display:flex}@media screen and (min-width:782px){.gf-results{display:flex;justify-content:space-between;margin-bottom:1.25rem}.gf-results .gf-result-box{flex:1 1 0px;margin-inline-end:1.25rem;margin-bottom:0}.gf-results .gf-result-box:last-child{margin-inline-end:0}}.gresults-results-field{margin-bottom:20px}.gresults-results-field table{border-bottom:1px solid silver;border-spacing:0}.gresults-results-field table td{padding:5px;border-right:1px solid silver;border-top:1px solid silver;text-align:center}.gresults-results-field-table-header{background-color:#eee}.gresults-results-field table td:first-child{border-left:1px solid silver}ul.gresults-results-field-content,ul.gresults-results-field-content li{list-style:disc outside none}ul.gresults-results-field-content{margin:10px}td.gresults-rank-field-rank,td.gresults-rank-field-score{width:110px}.gsurvey-rank-field-results table{width:100%}#gresults-results-filter-content{padding:12px;border-top:1px solid #ebebf2}.gresults-results-filter-section-label{font-size:1.2em;font-weight:700;margin-top:10px;margin-bottom:10px}#gresults-results-filter label{margin-top:10px;display:block}#gresults-results-filter-content #gform-no-filters{color:#242748}.gresults-add,.gresults-remove{margin-top:2px;vertical-align:middle;cursor:pointer}.gresults-add{margin-left:5px}#gresults-no-filters{color:silver}.gresults-datepicker,.gresults-filter-field,.gresults-filter-value{width:150px;box-sizing:border-box;-ms-box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;height:2em;padding:2px}.gresults-filter-operator{width:70px}#gresults-results-filter-buttons{clear:both;margin-top:20px;width:180px}.gresults-results-filter-field-label{font-size:1.1em;font-weight:700;margin-bottom:10px}.gresults-results-filter-field{margin-bottom:20px}.gresults-results-filter-field ul li label{margin-left:5px}.gresults-results-filter-title{font-size:1.5em;font-weight:700}#gresults-results-field-filters-container.resizable{border-bottom:5px double #ddd;min-height:120px}#gresults-results-field-filters{height:100%;overflow-y:auto}.ui-resizable{position:relative}.ui-resizable-handle{position:absolute;font-size:.1px;z-index:99999;display:block}.ui-resizable-s{cursor:s-resize;height:7px;width:100%;bottom:-5px;left:0}.gsurvey-likert-score{margin-top:5px}#gquiz-results-summary{margin:60px 0}.gresults-chart-wrapper{border-top:1px solid #dfdfdf;margin:0 0 28px}.gquiz-field-precentages-correct+.gresults-chart-wrapper{margin:0 0 14px}.gresults-label-group{display:block;clear:right}.gresults-label-group .gresults-label{display:inline-block;width:65px}.gresults-group-correct .gresults-value{color:green}.gresults-group-incorrect .gresults-value{color:red}
|
||||
@@ -0,0 +1,308 @@
|
||||
/* ------------------ Field Map ------------------ */
|
||||
|
||||
.gforms_form_settings.form-table .settings-field-map-table thead th {
|
||||
font-weight: bold;
|
||||
text-align: left;
|
||||
padding-left:0px;
|
||||
}
|
||||
|
||||
table.settings-field-map-table tbody td {
|
||||
padding: 0px 0px 8px 0px;
|
||||
}
|
||||
|
||||
.settings-field-map-table td:first-child {
|
||||
width: 220px;
|
||||
}
|
||||
|
||||
.settings-field-map-table td:last-child {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.settings-field-map-table .repeater th, .settings-field-map-table .repeater td:nth-child(2) {
|
||||
padding-left: 0px;
|
||||
padding-top: 0px;
|
||||
width: 220px;
|
||||
}
|
||||
|
||||
.settings-field-map-table select {
|
||||
font-family: inherit;
|
||||
height: 25px;
|
||||
width: 210px;
|
||||
}
|
||||
|
||||
.settings-field-map-table .chosen-container,
|
||||
.settings-field-map-table .select2-container {
|
||||
width: 210px !important;
|
||||
}
|
||||
|
||||
.settings-field-map-table .custom-key-container,
|
||||
.settings-field-map-table .custom-value-container {
|
||||
position: relative;
|
||||
width: 210px;
|
||||
}
|
||||
|
||||
.settings-field-map-table .custom-key-container input,
|
||||
.settings-field-map-table .custom-value-container input {
|
||||
width: 210px;
|
||||
}
|
||||
|
||||
.settings-field-map-table .custom-key-container input:not(:only-child),
|
||||
.settings-field-map-table .custom-value-container input:not(:only-child) {
|
||||
padding-right: 30px;
|
||||
}
|
||||
|
||||
.settings-field-map-table .custom-key-container.supports-merge-tags input:not(:only-child),
|
||||
.settings-field-map-table .custom-value-container.supports-merge-tags input:not(:only-child) {
|
||||
padding-right: 50px;
|
||||
}
|
||||
|
||||
.settings-field-map-table .custom-value-container.supports-merge-tags .all-merge-tags {
|
||||
height: 25px;
|
||||
position: absolute;
|
||||
right: 36px;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.settings-field-map-table .custom-value-container.supports-merge-tags .all-merge-tags .tooltip-merge-tag {
|
||||
background-position: center;
|
||||
height: 25px;
|
||||
margin: 0;
|
||||
width: 25px;
|
||||
}
|
||||
|
||||
.settings-field-map-table .custom-key-reset,
|
||||
.settings-field-map-table .custom-value-reset {
|
||||
background: url( '../images/field-map-reset.png' ) no-repeat center #ddd;
|
||||
background-size: 10px 10px;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
height: 25px;
|
||||
opacity: .3;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
right: 11px;
|
||||
text-indent: -9999px;
|
||||
top: 0;
|
||||
transition: opacity .25s ease-in-out;
|
||||
width: 25px;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.settings-field-map-table .custom-key-reset:hover,
|
||||
.settings-field-map-table .custom-value-reset:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.settings-field-map-table .add-item span,
|
||||
.settings-field-map-table .remove-item span {
|
||||
background: url( '../images/field-map-buttons.png' ) no-repeat center top transparent;
|
||||
background-size: 20px 100px;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
height: 25px;
|
||||
overflow: hidden;
|
||||
text-indent: -9999px;
|
||||
width: 20px;
|
||||
}
|
||||
|
||||
.settings-field-map-table .add-item span:hover {
|
||||
background-position: 0 -25px;
|
||||
}
|
||||
|
||||
.settings-field-map-table .remove-item span {
|
||||
background-position: 0 -50px;
|
||||
}
|
||||
|
||||
.settings-field-map-table .remove-item span:hover {
|
||||
background-position: 0 -75px;
|
||||
}
|
||||
|
||||
@media screen and ( max-width: 782px ) {
|
||||
|
||||
.settings-field-map-table .custom-key-container input:not(:only-child),
|
||||
.settings-field-map-table .custom-value-container input:not(:only-child) {
|
||||
padding-right: 45px;
|
||||
}
|
||||
|
||||
.settings-field-map-table .custom-key-reset,
|
||||
.settings-field-map-table .custom-value-reset {
|
||||
height: 40px;
|
||||
right: 0;
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* ---------------- Select Custom ---------------- */
|
||||
|
||||
.gaddon-setting-select-custom-container .select-custom-reset {
|
||||
background: url( ../../../images/xit.gif ) no-repeat scroll 0 0 transparent;
|
||||
cursor:pointer;
|
||||
display:none;
|
||||
position:absolute;
|
||||
text-indent:-9999px;
|
||||
width:10px;
|
||||
height: 10px;
|
||||
-moz-transition: none;
|
||||
-webkit-transition: none;
|
||||
-o-transition: color 0 ease-in;
|
||||
transition: none;
|
||||
z-index: 99;
|
||||
}
|
||||
|
||||
.gaddon-setting-select-custom-container .select-custom-reset:hover { background-position-x: -10px; }
|
||||
|
||||
.gaddon-setting-select-custom-container:hover .select-custom-reset {
|
||||
display:block;
|
||||
}
|
||||
|
||||
.gaddon-setting-select-custom-container {
|
||||
display:inline-block;
|
||||
position:relative;
|
||||
width: 210px;
|
||||
}
|
||||
.gaddon-setting-select-custom-container .select-custom-reset {
|
||||
left: 171px;
|
||||
top: 10px;
|
||||
}
|
||||
.gaddon-section .required { color: #f00; }
|
||||
.gaddon-setting-inline{
|
||||
display:inline;
|
||||
margin-right:6px;
|
||||
}
|
||||
|
||||
.gaddon-section-description ol { }
|
||||
.gaddon-section-description ol li {
|
||||
list-style: decimal;
|
||||
}
|
||||
|
||||
.repeater-buttons .add-item {
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
.add-item, .remove-item {
|
||||
color: #444;
|
||||
}
|
||||
|
||||
.mt-gaddon-editor {
|
||||
float: right;
|
||||
position: relative;
|
||||
right: 0.625rem;
|
||||
top: 2.8125rem;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
div#tab_notification .mt-gaddon-editor {
|
||||
top: 3.3125rem;
|
||||
}
|
||||
|
||||
html[dir="rtl"] .mt-gaddon-editor {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.mt-gaddon-editor ~ .wp-editor-wrap {
|
||||
margin-right: 23px;
|
||||
}
|
||||
|
||||
/* Visual Radio Buttons */
|
||||
.gaddon-setting-choice-visual {
|
||||
display: inline-block;
|
||||
margin-bottom: 5px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.gaddon-setting-choice-visual label {
|
||||
background: #F9F9F9;
|
||||
border: 1px solid #eee;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.gaddon-setting-choice-visual label > span {
|
||||
display: inline-block;
|
||||
-webkit-filter: brightness( 1.8 ) grayscale( 1 ) opacity( .5 );
|
||||
-moz-filter: brightness( 1.8 ) grayscale( 1 ) opacity( .5 );
|
||||
filter: brightness( 1.8 ) grayscale( 1 ) opacity( .5 );
|
||||
height: 65px;
|
||||
min-width: 110px;
|
||||
padding: 5px 10px 0;
|
||||
-webkit-transition: all 100ms ease-in;
|
||||
-moz-transition: all 100ms ease-in;
|
||||
transition: all 100ms ease-in;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.gaddon-setting-choice-visual label > span > i {
|
||||
color: #0074a2;
|
||||
display: inline-block;
|
||||
font-size: 2.5em;
|
||||
height: 32px;
|
||||
margin: 5px;
|
||||
width: 32px;
|
||||
}
|
||||
|
||||
.gaddon-setting-choice-visual label > span > img{
|
||||
height: 32px;
|
||||
margin: 5px;
|
||||
vertical-align: middle;
|
||||
width: 32px;
|
||||
}
|
||||
|
||||
.gaddon-setting-choice-visual input {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.gaddon-setting-choice-visual input:checked + label {
|
||||
background-color: #fff;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
.gaddon-setting-choice-visual input:checked + label > span {
|
||||
-webkit-filter: none;
|
||||
-moz-filter: none;
|
||||
filter: none;
|
||||
}
|
||||
|
||||
.gaddon-setting-choice-visual input:not([disabled]):not([checked]) + label > span:hover{
|
||||
-webkit-filter: brightness(1.2) grayscale(.5) opacity(.9);
|
||||
-moz-filter: brightness(1.2) grayscale(.5) opacity(.9);
|
||||
filter: brightness(1.2) grayscale(.5) opacity(.9);
|
||||
}
|
||||
|
||||
/* Feed Ordering */
|
||||
.ui-sortable-helper {
|
||||
background-color: #fff !important;
|
||||
-webkit-box-shadow: 6px 6px 28px -9px rgba(0,0,0,0.75);
|
||||
-moz-box-shadow: 6px 6px 28px -9px rgba(0,0,0,0.75);
|
||||
box-shadow: 6px 6px 28px -9px rgba(0,0,0,0.75);
|
||||
transform: rotate(1deg);
|
||||
-moz-transform: rotate(1deg);
|
||||
-webkit-transform: rotate(1deg);
|
||||
}
|
||||
|
||||
.wp-list-table.feed-list-sortable .sort-column {
|
||||
vertical-align: top;
|
||||
width: 2.2em;
|
||||
}
|
||||
|
||||
.wp-list-table.feed-list-sortable .feed-sort-handle {
|
||||
cursor: move;
|
||||
font-size: 1.25rem;
|
||||
width: 2.2em;
|
||||
}
|
||||
|
||||
/* ------------------ Feed List ------------------ */
|
||||
@media screen and ( max-width: 782px ) {
|
||||
.wp-list-table tbody tr:not(.inline-edit-row):not(.no-items) td:not(.column-primary)::before {
|
||||
content: attr(data-colname) ":";
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.wp-list-table.feeds .manage-column {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.wp-list-table.feeds .manage-column img {
|
||||
margin-top: 16px;
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
//Nothing to see here
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.8 KiB |
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
//Nothing to see here
|
||||
@@ -0,0 +1,111 @@
|
||||
var GFFeedOrder = function( args ) {
|
||||
|
||||
var self = this,
|
||||
$ = jQuery;
|
||||
|
||||
/**
|
||||
* Initialize Feed Ordering
|
||||
*/
|
||||
self.init = function() {
|
||||
|
||||
// Assign options to instance.
|
||||
self.options = args;
|
||||
|
||||
// Prepare sorting handle.
|
||||
var sortHandleMarkup = '<td class="sort-column"><i class="gform-icon gform-icon--drag-indicator feed-sort-handle"></i></td>';
|
||||
|
||||
// Add sorting handle to table.
|
||||
$( '.wp-list-table thead tr, .wp-list-table tfoot tr' ).append( '<th class="sort-column"></th>' );
|
||||
$( '.wp-list-table tbody tr' ).append( sortHandleMarkup );
|
||||
|
||||
// Initialize sorting.
|
||||
self.initSorting();
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize jQuery UI Sortable.
|
||||
*/
|
||||
self.initSorting = function() {
|
||||
|
||||
$( '.wp-list-table tbody' ).sortable(
|
||||
{
|
||||
cursor: 'move',
|
||||
handle: '.feed-sort-handle',
|
||||
placeholder: 'feed-placeholder',
|
||||
tolerance: 'pointer',
|
||||
create: function() { $( '.wp-list-table' ).addClass( 'feed-list-sortable' ); },
|
||||
helper: self.fixSortableColumnWidths,
|
||||
start: self.setPlaceholderHeight,
|
||||
update: self.updateFeedOrder,
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Fix table column widths.
|
||||
*/
|
||||
self.fixSortableColumnWidths = function( event, tr ) {
|
||||
|
||||
var $originals = tr.children(),
|
||||
$helper = tr.clone();
|
||||
|
||||
$helper.children().each( function( index ) {
|
||||
$( this ).width( $originals.eq( index ).width() );
|
||||
} );
|
||||
|
||||
return $helper;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get order of feeds.
|
||||
*/
|
||||
self.getFeedOrder = function() {
|
||||
|
||||
// Get all the checkboxes from the feed list table.
|
||||
var feed_checkboxes = $( '.wp-list-table tbody .check-column input[type="checkbox"]' );
|
||||
|
||||
// Map a function to the feed checkboxes array that returns the checkbox value.
|
||||
return feed_checkboxes.map( function() {
|
||||
return $( this ).val();
|
||||
} ).get();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Set height of the placeholder draggable feed.
|
||||
*/
|
||||
self.setPlaceholderHeight = function( event, ui ) {
|
||||
|
||||
// Set the height of the placeholder to the height of the feed being moved.
|
||||
$( '.wp-list-table .feed-placeholder' ).height( ui.item.height() );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the feed ordering to the database.
|
||||
*/
|
||||
self.updateFeedOrder = function( event, ui ) {
|
||||
|
||||
$.ajax(
|
||||
ajaxurl,
|
||||
{
|
||||
method: 'POST',
|
||||
dataType: 'JSON',
|
||||
data: {
|
||||
action: 'gf_save_feed_order',
|
||||
addon: self.options.addon,
|
||||
form_id: self.options.formId,
|
||||
feed_order: self.getFeedOrder(),
|
||||
nonce: self.options.nonce,
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
this.init();
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
var GFFeedOrder=function(e){var o=this,i=jQuery;o.init=function(){o.options=e;i(".wp-list-table thead tr, .wp-list-table tfoot tr").append('<th class="sort-column"></th>'),i(".wp-list-table tbody tr").append('<td class="sort-column"><i class="gform-icon gform-icon--drag-indicator feed-sort-handle"></i></td>'),o.initSorting()},o.initSorting=function(){i(".wp-list-table tbody").sortable({cursor:"move",handle:".feed-sort-handle",placeholder:"feed-placeholder",tolerance:"pointer",create:function(){i(".wp-list-table").addClass("feed-list-sortable")},helper:o.fixSortableColumnWidths,start:o.setPlaceholderHeight,update:o.updateFeedOrder})},o.fixSortableColumnWidths=function(e,t){var o=t.children(),t=t.clone();return t.children().each(function(e){i(this).width(o.eq(e).width())}),t},o.getFeedOrder=function(){return i('.wp-list-table tbody .check-column input[type="checkbox"]').map(function(){return i(this).val()}).get()},o.setPlaceholderHeight=function(e,t){i(".wp-list-table .feed-placeholder").height(t.item.height())},o.updateFeedOrder=function(e,t){i.ajax(ajaxurl,{method:"POST",dataType:"JSON",data:{action:"gf_save_feed_order",addon:o.options.addon,form_id:o.options.formId,feed_order:o.getFeedOrder(),nonce:o.options.nonce}})},this.init()};
|
||||
@@ -0,0 +1,136 @@
|
||||
var gfieldmap = function( options ) {
|
||||
|
||||
var self = this;
|
||||
|
||||
self.options = options;
|
||||
self.UI = jQuery( '#gaddon-setting-row-'+ self.options.fieldName );
|
||||
|
||||
self.init = function() {
|
||||
|
||||
self.bindEvents();
|
||||
|
||||
self.setupData();
|
||||
|
||||
self.setupRepeater();
|
||||
|
||||
};
|
||||
|
||||
self.bindEvents = function() {
|
||||
|
||||
self.UI.on( 'change', 'select[name="_gaddon_setting_'+ self.options.keyFieldName +'"]', function() {
|
||||
|
||||
var $select = jQuery( this ),
|
||||
$selectElm = $select.data( 'chosen' ) ? $select.siblings( '.chosen-container' ) : ( $select.data( 'select2' ) ? $select.siblings( '.select2-container' ) : $select ),
|
||||
$input = $select.siblings( '.custom-key-container' );
|
||||
|
||||
if( $select.val() != 'gf_custom' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$selectElm.fadeOut( function() {
|
||||
$input.fadeIn().focus();
|
||||
} );
|
||||
|
||||
} );
|
||||
|
||||
self.UI.on( 'click', 'a.custom-key-reset', function( event ) {
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
var $reset = jQuery( this ),
|
||||
$input = $reset.parents( '.custom-key-container' ),
|
||||
$select = $input.siblings( 'select.key' ),
|
||||
$selectElm = $select.data( 'chosen' ) ? $select.siblings( '.chosen-container' ) : ( $select.data( 'select2' ) ? $select.siblings( '.select2-container' ) : $select );
|
||||
|
||||
$input.fadeOut( function() {
|
||||
$input.find( 'input' ).val( '' ).change();
|
||||
$select.val( '' ).trigger( 'change' );
|
||||
$selectElm.fadeIn().focus();
|
||||
} );
|
||||
|
||||
} );
|
||||
|
||||
self.UI.closest( 'form' ).on( 'submit', function( event ) {
|
||||
|
||||
jQuery( '[name^="_gaddon_setting_'+ self.options.fieldName +'_"]' ).each( function( i ) {
|
||||
|
||||
jQuery( this ).removeAttr( 'name' );
|
||||
|
||||
} );
|
||||
|
||||
} );
|
||||
|
||||
};
|
||||
|
||||
self.setupData = function() {
|
||||
|
||||
var data = jQuery( '#' + self.options.fieldId ).val();
|
||||
self.data = data ? jQuery.parseJSON( data ) : null;
|
||||
|
||||
if ( ! self.data ) {
|
||||
self.data = [ {
|
||||
key: '',
|
||||
value: '',
|
||||
custom_key: ''
|
||||
} ];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
self.setupRepeater = function() {
|
||||
|
||||
var limit;
|
||||
if (self.options.limit > 0){
|
||||
limit = self.options.limit;
|
||||
}
|
||||
else{
|
||||
limit = 0;
|
||||
}
|
||||
|
||||
self.UI.find( 'tbody.repeater' ).repeater( {
|
||||
|
||||
limit: limit,
|
||||
items: self.data,
|
||||
addButtonMarkup: '<i class="gficon-add"></i>',
|
||||
removeButtonMarkup: '<i class="gficon-subtract"></i>',
|
||||
callbacks: {
|
||||
add: function( obj, $elem, item ) {
|
||||
|
||||
var key_select = $elem.find( 'select[name="_gaddon_setting_'+ self.options.keyFieldName +'"]' );
|
||||
|
||||
if ( ! item.custom_key && key_select.length > 0 ) {
|
||||
$elem.find( '.custom-key-container' ).hide();
|
||||
} else {
|
||||
$elem.find( '.key' ).hide();
|
||||
}
|
||||
|
||||
gform.doAction( 'gform_fieldmap_add_row', obj, $elem, item );
|
||||
|
||||
},
|
||||
save: function( obj, data ) {
|
||||
|
||||
data = jQuery.extend( {}, data );
|
||||
|
||||
for ( var i = 0; i < data.length; i++ ) {
|
||||
|
||||
if ( data[i].custom_key != '' ) {
|
||||
data[i].custom = 1;
|
||||
data[i].key = data[i].custom_key;
|
||||
}
|
||||
|
||||
delete data[i].custom_key;
|
||||
|
||||
}
|
||||
|
||||
jQuery( '#'+ self.options.fieldId ).val( jQuery.toJSON( data ) );
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
return self.init();
|
||||
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
var gfieldmap=function(e){var a=this;return a.options=e,a.UI=jQuery("#gaddon-setting-row-"+a.options.fieldName),a.init=function(){a.bindEvents(),a.setupData(),a.setupRepeater()},a.bindEvents=function(){a.UI.on("change",'select[name="_gaddon_setting_'+a.options.keyFieldName+'"]',function(){var e=jQuery(this),t=e.data("chosen")?e.siblings(".chosen-container"):e.data("select2")?e.siblings(".select2-container"):e,n=e.siblings(".custom-key-container");"gf_custom"==e.val()&&t.fadeOut(function(){n.fadeIn().focus()})}),a.UI.on("click","a.custom-key-reset",function(e){e.preventDefault();var t=jQuery(this).parents(".custom-key-container"),n=t.siblings("select.key"),i=n.data("chosen")?n.siblings(".chosen-container"):n.data("select2")?n.siblings(".select2-container"):n;t.fadeOut(function(){t.find("input").val("").change(),n.val("").trigger("change"),i.fadeIn().focus()})}),a.UI.closest("form").on("submit",function(e){jQuery('[name^="_gaddon_setting_'+a.options.fieldName+'_"]').each(function(e){jQuery(this).removeAttr("name")})})},a.setupData=function(){var e=jQuery("#"+a.options.fieldId).val();a.data=e?jQuery.parseJSON(e):null,a.data||(a.data=[{key:"",value:"",custom_key:""}])},a.setupRepeater=function(){var e=0<a.options.limit?a.options.limit:0;a.UI.find("tbody.repeater").repeater({limit:e,items:a.data,addButtonMarkup:'<i class="gficon-add"></i>',removeButtonMarkup:'<i class="gficon-subtract"></i>',callbacks:{add:function(e,t,n){var i=t.find('select[name="_gaddon_setting_'+a.options.keyFieldName+'"]');(!n.custom_key&&0<i.length?t.find(".custom-key-container"):t.find(".key")).hide(),gform.doAction("gform_fieldmap_add_row",e,t,n)},save:function(e,t){t=jQuery.extend({},t);for(var n=0;n<t.length;n++)""!=t[n].custom_key&&(t[n].custom=1,t[n].key=t[n].custom_key),delete t[n].custom_key;jQuery("#"+a.options.fieldId).val(jQuery.toJSON(t))}}})},a.init()};
|
||||
@@ -0,0 +1,247 @@
|
||||
|
||||
var GFFrontendFeeds = function( args ) {
|
||||
|
||||
var self = this,
|
||||
$ = jQuery;
|
||||
|
||||
/**
|
||||
* Initialize Feed Ordering
|
||||
*/
|
||||
self.init = function() {
|
||||
|
||||
// Assign options to instance.
|
||||
self.options = args;
|
||||
|
||||
self.triggerInputIds = self.getTriggerInputIds( self.options.feeds );
|
||||
|
||||
self.activeFeeds = [];
|
||||
|
||||
self.evaluateFeeds();
|
||||
|
||||
self.bindEvents();
|
||||
|
||||
};
|
||||
|
||||
self.bindEvents = function() {
|
||||
|
||||
gform.addAction( 'gform_input_change', function( elem, formId, inputId ) {
|
||||
gform.logger.log( 'GFFrontendFeeds: gform_input_change event triggered. formId ' + formId + ', inputId ' + inputId );
|
||||
|
||||
var fieldId = parseInt( inputId ) + '';
|
||||
var isTriggeredInput = $.inArray( inputId, self.triggerInputIds ) !== -1 || $.inArray( fieldId , self.triggerInputIds ) !== -1 ;
|
||||
|
||||
if( self.options.formId == formId && isTriggeredInput ) {
|
||||
gform.logger.log( 'GFFrontendFeeds: Evaluating feeds. self.options.formId=' + self.options.formId + ', isTriggeredInput=' + isTriggeredInput + ', triggerInputIds=' + self.triggerInputIds.join( ',' ) );
|
||||
self.evaluateFeeds();
|
||||
} else {
|
||||
gform.logger.log( 'GFFrontendFeeds: Bypassing feed evaluation. self.options.formId=' + self.options.formId + ', isTriggeredInput=' + isTriggeredInput + ', triggerInputIds=' + self.triggerInputIds.join( ',' ) );
|
||||
}
|
||||
} );
|
||||
|
||||
};
|
||||
|
||||
self.saveToState = function() {
|
||||
const feeds = self.options.feeds.map(({ feedId, isActivated, transactionType }) => ({ feedId, isActivated, transactionType }));
|
||||
|
||||
gform.logger.log( 'GFFrontendFeeds: Saving feeds to state: ' + JSON.stringify( feeds ) );
|
||||
|
||||
gform.state.set( self.options.formId, 'feeds', self.options.feeds );
|
||||
}
|
||||
|
||||
self.evaluateFeeds = function() {
|
||||
|
||||
var feed, isMatch, isActivated;
|
||||
|
||||
for( i = 0; i < self.options.feeds.length; i++ ) {
|
||||
|
||||
feed = self.options.feeds[ i ];
|
||||
isMatch = self.evaluateFeed( feed, self.options.formId );
|
||||
isActivated = self.isFeedActivated( feed );
|
||||
|
||||
if( ! isMatch && isActivated !== null ) {
|
||||
self.deactivateFeed( feed );
|
||||
} else if( isMatch && ! isActivated && ( ! feed.isSingleFeed || ( feed.isSingleFeed && self.hasPriority( feed.feedId, feed.addonSlug ) ) ) ) {
|
||||
self.activateFeed( feed );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires after the conditional logic on the form has been evaluated.
|
||||
*
|
||||
* @since 2.4
|
||||
*
|
||||
* @param array $feeds A collection of feed objects.
|
||||
* @param int $formId The form id.
|
||||
*/
|
||||
gform.doAction( 'gform_frontend_feeds_evaluated', self.options.feeds, self.options.formId, self );
|
||||
gform.doAction( 'gform_frontend_feeds_evaluated_{0}'.gformFormat( self.options.formId ), self.options.feeds, self.options.formId, self );
|
||||
gform.doAction( 'gform_{0}_frontend_feeds_evaluated'.gformFormat( feed.addonSlug ), self.options.feeds, self.options.formId, self );
|
||||
gform.doAction( 'gform_{0}_frontend_feeds_evaluated_{0}'.gformFormat( feed.addonSlug, self.options.formId ), self.options.feeds, self.options.formId, self );
|
||||
|
||||
self.saveToState();
|
||||
};
|
||||
|
||||
self.evaluateFeed = function( feed, formId ) {
|
||||
|
||||
// Feeds with no configured conditional logic always a match.
|
||||
if( ! feed.conditionalLogic ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return gf_get_field_action( formId, feed.conditionalLogic ) == 'show';
|
||||
};
|
||||
|
||||
self.getTriggerInputIds = function() {
|
||||
var inputIds = [];
|
||||
for( var i = 0; i < self.options.feeds.length; i++ ) {
|
||||
|
||||
var feed = self.options.feeds[ i ];
|
||||
|
||||
if( ! feed.conditionalLogic ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for( var j = 0; j < feed.conditionalLogic.rules.length; j++ ) {
|
||||
var rule = self.options.feeds[i].conditionalLogic.rules[j];
|
||||
if( $.inArray( rule.fieldId, inputIds ) == -1 ) {
|
||||
inputIds.push( rule.fieldId );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return inputIds;
|
||||
};
|
||||
|
||||
self.isFeedActivated = function( feed ) {
|
||||
|
||||
if( typeof feed != 'object' ) {
|
||||
feed = self.getFeed( feed );
|
||||
if( ! feed ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return typeof feed.isActivated != 'undefined' ? feed.isActivated : null;
|
||||
};
|
||||
|
||||
self.getFeed = function( feedId ) {
|
||||
for( var i = 0; i < self.options.feeds.length; i++ ) {
|
||||
var feed = self.options.feeds[ i ];
|
||||
if( feed.feedId == feedId ) {
|
||||
return feed;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
self.getFeedsByAddon = function( addonSlug, currentFeed, onlyActive ) {
|
||||
var feeds = [];
|
||||
for( var i = 0; i < self.options.feeds.length; i++ ) {
|
||||
var feed = self.options.feeds[ i ];
|
||||
if( feed.addonSlug == addonSlug
|
||||
&& ! ( currentFeed && feed.feedId == currentFeed.feedId )
|
||||
) {
|
||||
if( onlyActive ) {
|
||||
if( self.isFeedActivated( feed ) ) {
|
||||
feeds.push( feed );
|
||||
}
|
||||
} else {
|
||||
feeds.push( feed );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return feeds;
|
||||
};
|
||||
|
||||
self.activateFeed = function( feeds ) {
|
||||
|
||||
if( feeds.feedId ) {
|
||||
feeds = [ feeds ];
|
||||
}
|
||||
|
||||
for( var i = 0; i < feeds.length; i++ ) {
|
||||
|
||||
var feed = feeds[ i ];
|
||||
|
||||
feed.isActivated = true;
|
||||
|
||||
/**
|
||||
* Fires after the conditional logic on the form has been evaluated and the feed has been found to be active.
|
||||
*
|
||||
* @since 2.4
|
||||
*
|
||||
* @param array $feeds A collection of feed objects.
|
||||
* @param int $formId The form id.
|
||||
*/
|
||||
|
||||
gform.doAction( 'gform_frontend_feed_activated', feed, self.options.formId );
|
||||
gform.doAction( 'gform_frontend_feed_activated_{0}'.gformFormat( self.options.formId ), feed, self.options.formId );
|
||||
gform.doAction( 'gform_{0}_frontend_feed_activated'.gformFormat( feed.addonSlug ), feed, self.options.formId );
|
||||
gform.doAction( 'gform_{0}_frontend_feed_activated_{0}'.gformFormat( feed.addonSlug, self.options.formId ), feed, self.options.formId );
|
||||
|
||||
if( feed.isSingleFeed ) {
|
||||
self.deactivateFeed( self.getFeedsByAddon( feed.addonSlug, feed ) );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
self.deactivateFeed = function( feeds ) {
|
||||
|
||||
if( feeds.feedId ) {
|
||||
feeds = [ feeds ];
|
||||
}
|
||||
|
||||
for( var i = 0; i < feeds.length; i++ ) {
|
||||
|
||||
var feed = feeds[ i ],
|
||||
isActivated = self.isFeedActivated( feed );
|
||||
|
||||
if( isActivated === null || isActivated === false ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
feed.isActivated = false;
|
||||
|
||||
/**
|
||||
* Fires after the conditional logic on the form has been evaluated and the feed has become inactive.
|
||||
*
|
||||
* @since 2.4
|
||||
*
|
||||
* @param array $feeds A collection of feed objects.
|
||||
* @param int $formId The form id.
|
||||
*/
|
||||
gform.doAction( 'gform_frontend_feed_deactivated', feed, self.options.formId );
|
||||
gform.doAction( 'gform_frontend_feed_deactivated_{0}'.gformFormat( self.options.formId ), feed, self.options.formId );
|
||||
gform.doAction( 'gform_{0}_frontend_feed_deactivated'.gformFormat( feed.addonSlug ), feed, self.options.formId );
|
||||
gform.doAction( 'gform_{0}_frontend_feed_deactivated_{0}'.gformFormat( feed.addonSlug, self.options.formId ), feed, self.options.formId );
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
self.hasPriority = function( feedId, addonSlug ) {
|
||||
|
||||
var addonFeeds = self.getFeedsByAddon( addonSlug );
|
||||
|
||||
for( var i = 0; i <= addonFeeds.length; i++ ) {
|
||||
|
||||
var feed = addonFeeds[i];
|
||||
|
||||
if( feed.feedId != feedId && feed.isActivated ) {
|
||||
return false;
|
||||
} else if ( feed.feedId == feedId ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
this.init();
|
||||
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
var GFFrontendFeeds=function(e){var f=this,r=jQuery;f.init=function(){f.options=e,f.triggerInputIds=f.getTriggerInputIds(f.options.feeds),f.activeFeeds=[],f.evaluateFeeds(),f.bindEvents()},f.bindEvents=function(){gform.addAction("gform_input_change",function(e,o,t){gform.logger.log("GFFrontendFeeds: gform_input_change event triggered. formId "+o+", inputId "+t);var d=parseInt(t)+"",t=-1!==r.inArray(t,f.triggerInputIds)||-1!==r.inArray(d,f.triggerInputIds);f.options.formId==o&&t?(gform.logger.log("GFFrontendFeeds: Evaluating feeds. self.options.formId="+f.options.formId+", isTriggeredInput="+t+", triggerInputIds="+f.triggerInputIds.join(",")),f.evaluateFeeds()):gform.logger.log("GFFrontendFeeds: Bypassing feed evaluation. self.options.formId="+f.options.formId+", isTriggeredInput="+t+", triggerInputIds="+f.triggerInputIds.join(","))})},f.saveToState=function(){var e=f.options.feeds.map(({feedId:e,isActivated:o,transactionType:t})=>({feedId:e,isActivated:o,transactionType:t}));gform.logger.log("GFFrontendFeeds: Saving feeds to state: "+JSON.stringify(e)),gform.state.set(f.options.formId,"feeds",f.options.feeds)},f.evaluateFeeds=function(){var e,o,t;for(i=0;i<f.options.feeds.length;i++)e=f.options.feeds[i],o=f.evaluateFeed(e,f.options.formId),t=f.isFeedActivated(e),o||null===t?!o||t||e.isSingleFeed&&(e.isSingleFeed,!f.hasPriority(e.feedId,e.addonSlug))||f.activateFeed(e):f.deactivateFeed(e);gform.doAction("gform_frontend_feeds_evaluated",f.options.feeds,f.options.formId,f),gform.doAction("gform_frontend_feeds_evaluated_{0}".gformFormat(f.options.formId),f.options.feeds,f.options.formId,f),gform.doAction("gform_{0}_frontend_feeds_evaluated".gformFormat(e.addonSlug),f.options.feeds,f.options.formId,f),gform.doAction("gform_{0}_frontend_feeds_evaluated_{0}".gformFormat(e.addonSlug,f.options.formId),f.options.feeds,f.options.formId,f),f.saveToState()},f.evaluateFeed=function(e,o){return!e.conditionalLogic||"show"==gf_get_field_action(o,e.conditionalLogic)},f.getTriggerInputIds=function(){for(var e=[],o=0;o<f.options.feeds.length;o++){var t=f.options.feeds[o];if(t.conditionalLogic)for(var d=0;d<t.conditionalLogic.rules.length;d++){var n=f.options.feeds[o].conditionalLogic.rules[d];-1==r.inArray(n.fieldId,e)&&e.push(n.fieldId)}}return e},f.isFeedActivated=function(e){return!("object"!=typeof e&&!(e=f.getFeed(e)))&&(void 0!==e.isActivated?e.isActivated:null)},f.getFeed=function(e){for(var o=0;o<f.options.feeds.length;o++){var t=f.options.feeds[o];if(t.feedId==e)return t}return!1},f.getFeedsByAddon=function(e,o,t){for(var d=[],n=0;n<f.options.feeds.length;n++){var r=f.options.feeds[n];r.addonSlug!=e||o&&r.feedId==o.feedId||t&&!f.isFeedActivated(r)||d.push(r)}return d},f.activateFeed=function(e){e.feedId&&(e=[e]);for(var o=0;o<e.length;o++){var t=e[o];t.isActivated=!0,gform.doAction("gform_frontend_feed_activated",t,f.options.formId),gform.doAction("gform_frontend_feed_activated_{0}".gformFormat(f.options.formId),t,f.options.formId),gform.doAction("gform_{0}_frontend_feed_activated".gformFormat(t.addonSlug),t,f.options.formId),gform.doAction("gform_{0}_frontend_feed_activated_{0}".gformFormat(t.addonSlug,f.options.formId),t,f.options.formId),t.isSingleFeed&&f.deactivateFeed(f.getFeedsByAddon(t.addonSlug,t))}},f.deactivateFeed=function(e){e.feedId&&(e=[e]);for(var o=0;o<e.length;o++){var t=e[o],d=f.isFeedActivated(t);null!==d&&!1!==d&&(t.isActivated=!1,gform.doAction("gform_frontend_feed_deactivated",t,f.options.formId),gform.doAction("gform_frontend_feed_deactivated_{0}".gformFormat(f.options.formId),t,f.options.formId),gform.doAction("gform_{0}_frontend_feed_deactivated".gformFormat(t.addonSlug),t,f.options.formId),gform.doAction("gform_{0}_frontend_feed_deactivated_{0}".gformFormat(t.addonSlug,f.options.formId),t,f.options.formId))}},f.hasPriority=function(e,o){for(var t=f.getFeedsByAddon(o),d=0;d<=t.length;d++){var n=t[d];if(n.feedId!=e&&n.isActivated)return!1;if(n.feedId==e)return!0}return!1},this.init()};
|
||||
@@ -0,0 +1,166 @@
|
||||
var GFGenericMap = function( options ) {
|
||||
|
||||
var self = this;
|
||||
|
||||
self.options = options;
|
||||
self.UI = jQuery( '#gaddon-setting-row-'+ self.options.fieldName );
|
||||
|
||||
self.init = function() {
|
||||
|
||||
self.bindEvents();
|
||||
|
||||
self.setupData();
|
||||
|
||||
self.setupRepeater();
|
||||
|
||||
};
|
||||
|
||||
self.bindEvents = function() {
|
||||
|
||||
self.UI.on( 'change', 'select[name="_gaddon_setting_'+ self.options.keyFieldName +'"]', function() {
|
||||
|
||||
var $select = jQuery( this ),
|
||||
$selectElm = $select.data( 'chosen' ) ? $select.siblings( '.chosen-container' ) : ( $select.data( 'select2' ) ? $select.siblings( '.select2-container' ) : $select ),
|
||||
$input = $select.siblings( '.custom-key-container' );
|
||||
|
||||
if( $select.val() != 'gf_custom' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$selectElm.fadeOut( function() {
|
||||
$input.fadeIn().focus();
|
||||
} );
|
||||
|
||||
} );
|
||||
|
||||
self.UI.on( 'change', 'select[name="_gaddon_setting_'+ self.options.valueFieldName +'"]', function() {
|
||||
|
||||
var $select = jQuery( this ),
|
||||
$selectElm = $select.data( 'chosen' ) ? $select.siblings( '.chosen-container' ) : ( $select.data( 'select2' ) ? $select.siblings( '.select2-container' ) : $select ),
|
||||
$input = $select.siblings( '.custom-value-container' );
|
||||
|
||||
if ( $select.val() != 'gf_custom' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$selectElm.fadeOut( function() {
|
||||
$input.fadeIn().focus();
|
||||
} );
|
||||
|
||||
} );
|
||||
|
||||
self.UI.on( 'click', 'a.custom-key-reset', function( event ) {
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
var $reset = jQuery( this ),
|
||||
$input = $reset.parents( '.custom-key-container' ),
|
||||
$select = $input.siblings( 'select.key' ),
|
||||
$selectElm = $select.data( 'chosen' ) ? $select.siblings( '.chosen-container' ) : ( $select.data( 'select2' ) ? $select.siblings( '.select2-container' ) : $select );
|
||||
|
||||
$input.fadeOut( function() {
|
||||
$input.find( 'input' ).val( '' ).change();
|
||||
$select.val( '' ).trigger( 'change' );
|
||||
$selectElm.fadeIn().focus();
|
||||
} );
|
||||
|
||||
} );
|
||||
|
||||
self.UI.on( 'click', 'a.custom-value-reset', function( event ) {
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
var $reset = jQuery( this ),
|
||||
$input = $reset.parents( '.custom-value-container' ),
|
||||
$select = $input.siblings( 'select.value' ),
|
||||
$selectElm = $select.data( 'chosen' ) ? $select.siblings( '.chosen-container' ) : ( $select.data( 'select2' ) ? $select.siblings( '.select2-container' ) : $select );
|
||||
|
||||
$input.fadeOut( function() {
|
||||
$input.find( 'input' ).val( '' ).change();
|
||||
$select.val( '' ).trigger( 'change' );
|
||||
$selectElm.fadeIn().focus();
|
||||
} );
|
||||
|
||||
} );
|
||||
|
||||
self.UI.closest( 'form' ).on( 'submit', function( event ) {
|
||||
|
||||
jQuery( '[name^="_gaddon_setting_'+ self.options.fieldName +'_"]' ).each( function( i ) {
|
||||
|
||||
jQuery( this ).removeAttr( 'name' );
|
||||
|
||||
} );
|
||||
|
||||
} );
|
||||
|
||||
};
|
||||
|
||||
self.setupData = function() {
|
||||
|
||||
var data = jQuery( '#' + self.options.fieldId ).val();
|
||||
self.data = data ? jQuery.parseJSON( data ) : null;
|
||||
|
||||
if ( ! self.data ) {
|
||||
self.data = [ {
|
||||
key: '',
|
||||
value: '',
|
||||
custom_key: '',
|
||||
custom_value: ''
|
||||
} ];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
self.setupRepeater = function() {
|
||||
|
||||
var limit = self.options.limit > 0 ? self.options.limit : 0;
|
||||
|
||||
self.UI.find( 'tbody.repeater' ).repeater( {
|
||||
|
||||
limit: limit,
|
||||
items: self.data,
|
||||
addButtonMarkup: '<i class="gficon-add"></i>',
|
||||
removeButtonMarkup: '<i class="gficon-subtract"></i>',
|
||||
callbacks: {
|
||||
add: function( obj, $elem, item ) {
|
||||
|
||||
var key_select = $elem.find( 'select[name="_gaddon_setting_'+ self.options.keyFieldName +'"]' );
|
||||
|
||||
if ( ! item.custom_key && ( key_select.length > 0 && key_select.val() !== 'gf_custom' ) ) {
|
||||
$elem.find( '.custom-key-container' ).hide();
|
||||
} else {
|
||||
$elem.find( '.key' ).hide();
|
||||
}
|
||||
|
||||
var value_select = $elem.find( 'select[name="_gaddon_setting_'+ self.options.valueFieldName +'"]' );
|
||||
|
||||
if ( ! item.custom_value && ( value_select.length > 0 && value_select.val() !== 'gf_custom' ) ) {
|
||||
$elem.find( '.custom-value-container' ).hide();
|
||||
} else {
|
||||
$elem.find( '.value' ).hide();
|
||||
}
|
||||
|
||||
if ( self.options.mergeTags ) {
|
||||
new gfMergeTagsObj( form, $elem.find( '.custom-value-container input' ) );
|
||||
$elem.find( '.custom-value-container' ).addClass( 'supports-merge-tags' );
|
||||
}
|
||||
|
||||
if ( window.hasOwnProperty( 'gform' ) ) {
|
||||
gform.doAction( 'gform_fieldmap_add_row', obj, $elem, item );
|
||||
}
|
||||
|
||||
},
|
||||
save: function( obj, data ) {
|
||||
|
||||
jQuery( '#'+ self.options.fieldId ).val( JSON.stringify( data ) );
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
return self.init();
|
||||
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
var GFGenericMap=function(e){var i=this;return i.options=e,i.UI=jQuery("#gaddon-setting-row-"+i.options.fieldName),i.init=function(){i.bindEvents(),i.setupData(),i.setupRepeater()},i.bindEvents=function(){i.UI.on("change",'select[name="_gaddon_setting_'+i.options.keyFieldName+'"]',function(){var e=jQuery(this),n=e.data("chosen")?e.siblings(".chosen-container"):e.data("select2")?e.siblings(".select2-container"):e,t=e.siblings(".custom-key-container");"gf_custom"==e.val()&&n.fadeOut(function(){t.fadeIn().focus()})}),i.UI.on("change",'select[name="_gaddon_setting_'+i.options.valueFieldName+'"]',function(){var e=jQuery(this),n=e.data("chosen")?e.siblings(".chosen-container"):e.data("select2")?e.siblings(".select2-container"):e,t=e.siblings(".custom-value-container");"gf_custom"==e.val()&&n.fadeOut(function(){t.fadeIn().focus()})}),i.UI.on("click","a.custom-key-reset",function(e){e.preventDefault();var n=jQuery(this).parents(".custom-key-container"),t=n.siblings("select.key"),a=t.data("chosen")?t.siblings(".chosen-container"):t.data("select2")?t.siblings(".select2-container"):t;n.fadeOut(function(){n.find("input").val("").change(),t.val("").trigger("change"),a.fadeIn().focus()})}),i.UI.on("click","a.custom-value-reset",function(e){e.preventDefault();var n=jQuery(this).parents(".custom-value-container"),t=n.siblings("select.value"),a=t.data("chosen")?t.siblings(".chosen-container"):t.data("select2")?t.siblings(".select2-container"):t;n.fadeOut(function(){n.find("input").val("").change(),t.val("").trigger("change"),a.fadeIn().focus()})}),i.UI.closest("form").on("submit",function(e){jQuery('[name^="_gaddon_setting_'+i.options.fieldName+'_"]').each(function(e){jQuery(this).removeAttr("name")})})},i.setupData=function(){var e=jQuery("#"+i.options.fieldId).val();i.data=e?jQuery.parseJSON(e):null,i.data||(i.data=[{key:"",value:"",custom_key:"",custom_value:""}])},i.setupRepeater=function(){var e=0<i.options.limit?i.options.limit:0;i.UI.find("tbody.repeater").repeater({limit:e,items:i.data,addButtonMarkup:'<i class="gficon-add"></i>',removeButtonMarkup:'<i class="gficon-subtract"></i>',callbacks:{add:function(e,n,t){var a=n.find('select[name="_gaddon_setting_'+i.options.keyFieldName+'"]'),a=((!t.custom_key&&0<a.length&&"gf_custom"!==a.val()?n.find(".custom-key-container"):n.find(".key")).hide(),n.find('select[name="_gaddon_setting_'+i.options.valueFieldName+'"]'));(!t.custom_value&&0<a.length&&"gf_custom"!==a.val()?n.find(".custom-value-container"):n.find(".value")).hide(),i.options.mergeTags&&(new gfMergeTagsObj(form,n.find(".custom-value-container input")),n.find(".custom-value-container").addClass("supports-merge-tags")),window.hasOwnProperty("gform")&&gform.doAction("gform_fieldmap_add_row",e,n,t)},save:function(e,n){jQuery("#"+i.options.fieldId).val(JSON.stringify(n))}}})},i.init()};
|
||||
@@ -0,0 +1,74 @@
|
||||
function loadBillingLength(setting_name){
|
||||
var intervals = window[setting_name + "_intervals"]
|
||||
if(!intervals)
|
||||
return;
|
||||
|
||||
var unit = jQuery("#" + setting_name + "_unit").val();
|
||||
var min = gform.utils.escapeHtml( intervals[unit]["min"] );
|
||||
var max = gform.utils.escapeHtml( intervals[unit]["max"] );
|
||||
var lengthField = jQuery("#" + setting_name + "_length");
|
||||
var length = lengthField.val();
|
||||
var str = "";
|
||||
for(var i=min; i<=max; i++){
|
||||
var selected = length == i ? "selected='selected'" : "";
|
||||
str += "<option value='" + i + "' " + selected + ">" + i + "</option>";
|
||||
}
|
||||
lengthField.html( str );
|
||||
}
|
||||
|
||||
function cancel_subscription( entryId ) {
|
||||
|
||||
if ( !confirm( gaddon_payment_strings.subscriptionCancelWarning ) )
|
||||
return;
|
||||
|
||||
jQuery( "#subscription_cancel_spinner" ).show();
|
||||
jQuery( "#cancelsub" ).prop( "disabled", true );
|
||||
jQuery.post(
|
||||
ajaxurl,
|
||||
{
|
||||
action: "gaddon_cancel_subscription",
|
||||
entry_id: entryId,
|
||||
gaddon_cancel_subscription: gaddon_payment_strings.subscriptionCancelNonce
|
||||
},
|
||||
function ( response ) {
|
||||
jQuery( "#subscription_cancel_spinner" ).hide();
|
||||
if ( response.success === true ) {
|
||||
jQuery( "#gform_payment_status" ).html( gform.utils.escapeHtml( gaddon_payment_strings.subscriptionCanceled ) );
|
||||
jQuery( "#cancelsub" ).hide();
|
||||
} else {
|
||||
jQuery( "#cancelsub" ).prop( "disabled", false );
|
||||
if ( response.success === false ) {
|
||||
alert( gaddon_payment_strings.subscriptionError );
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function gaddon_cancel_subscription_confirmed( element ) {
|
||||
var entryId = jQuery( element ).data( 'entry-id' );
|
||||
|
||||
jQuery( '#subscription_cancel_spinner' ).show();
|
||||
jQuery( '#cancelsub' ).prop( 'disabled', true );
|
||||
|
||||
jQuery.post(
|
||||
ajaxurl,
|
||||
{
|
||||
action: 'gaddon_cancel_subscription',
|
||||
entry_id: entryId,
|
||||
gaddon_cancel_subscription: gaddon_payment_strings.subscriptionCancelNonce
|
||||
},
|
||||
function ( response ) {
|
||||
jQuery( '#subscription_cancel_spinner' ).hide();
|
||||
if ( response.success === true ) {
|
||||
jQuery( '#gform_payment_status' ).html( gform.utils.escapeHtml( gaddon_payment_strings.subscriptionCanceled ) );
|
||||
jQuery( '#cancelsub' ).hide();
|
||||
} else {
|
||||
jQuery( '#cancelsub' ).prop( 'disabled', false );
|
||||
if ( response.success === false ) {
|
||||
gform.alert( gaddon_payment_strings.subscriptionError );
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
function loadBillingLength(n){var s=window[n+"_intervals"];if(s){for(var e=jQuery("#"+n+"_unit").val(),c=gform.utils.escapeHtml(s[e].min),i=gform.utils.escapeHtml(s[e].max),s=jQuery("#"+n+"_length"),r=s.val(),a="",t=c;t<=i;t++)a+="<option value='"+t+"' "+(r==t?"selected='selected'":"")+">"+t+"</option>";s.html(a)}}function cancel_subscription(n){confirm(gaddon_payment_strings.subscriptionCancelWarning)&&(jQuery("#subscription_cancel_spinner").show(),jQuery("#cancelsub").prop("disabled",!0),jQuery.post(ajaxurl,{action:"gaddon_cancel_subscription",entry_id:n,gaddon_cancel_subscription:gaddon_payment_strings.subscriptionCancelNonce},function(n){jQuery("#subscription_cancel_spinner").hide(),!0===n.success?(jQuery("#gform_payment_status").html(gform.utils.escapeHtml(gaddon_payment_strings.subscriptionCanceled)),jQuery("#cancelsub").hide()):(jQuery("#cancelsub").prop("disabled",!1),!1===n.success&&alert(gaddon_payment_strings.subscriptionError))}))}function gaddon_cancel_subscription_confirmed(n){n=jQuery(n).data("entry-id");jQuery("#subscription_cancel_spinner").show(),jQuery("#cancelsub").prop("disabled",!0),jQuery.post(ajaxurl,{action:"gaddon_cancel_subscription",entry_id:n,gaddon_cancel_subscription:gaddon_payment_strings.subscriptionCancelNonce},function(n){jQuery("#subscription_cancel_spinner").hide(),!0===n.success?(jQuery("#gform_payment_status").html(gform.utils.escapeHtml(gaddon_payment_strings.subscriptionCanceled)),jQuery("#cancelsub").hide()):(jQuery("#cancelsub").prop("disabled",!1),!1===n.success&&gform.alert(gaddon_payment_strings.subscriptionError))})}
|
||||
@@ -0,0 +1,265 @@
|
||||
var gresultsAjaxRequest;
|
||||
|
||||
var gresults = {
|
||||
|
||||
drawCharts: function () {
|
||||
var containers = jQuery('.gresults-chart-wrapper');
|
||||
containers.each(function (index, elem) {
|
||||
var id = jQuery(elem).attr('id');
|
||||
var options = jQuery(elem).data('options');
|
||||
var datatable = jQuery(elem).data('datatable');
|
||||
var chartType = jQuery(elem).data('charttype');
|
||||
var data_array = datatable;
|
||||
var data = google.visualization.arrayToDataTable(data_array);
|
||||
var cont = document.getElementById(id);
|
||||
var chart;
|
||||
if (chartType == "bar") {
|
||||
chart = new google.visualization.BarChart(cont);
|
||||
} else if (chartType == "pie") {
|
||||
chart = new google.visualization.PieChart(cont);
|
||||
} else if (chartType == "column") {
|
||||
chart = new google.visualization.ColumnChart(cont);
|
||||
}
|
||||
chart.draw(data, options);
|
||||
});
|
||||
|
||||
// For RTL languages, set the text-anchor of bar chart labels to "start" to keep proper alignment.
|
||||
const isRTL = document.documentElement.dir === 'rtl';
|
||||
const isResultsWrapper = document.querySelector('.gresults-chart-wrapper svg')
|
||||
if (isResultsWrapper && isRTL) {
|
||||
document.querySelectorAll('text[text-anchor="end"]').forEach(function(el) {
|
||||
el.setAttribute('text-anchor', 'start');
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
renderStateData: function (state) {
|
||||
var results = jQuery("#gresults-results");
|
||||
results.data('searchcriteria', state.searchCriteria);
|
||||
jQuery("#gresults-results-filter").html(state.filterUI);
|
||||
results.css('opacity', 0);
|
||||
results.html(state.html);
|
||||
gresults.drawCharts();
|
||||
results.fadeTo("slow", 1);
|
||||
|
||||
var filterContainer = jQuery("#gresults-results-field-filters-container");
|
||||
filterContainer.resizable();
|
||||
filterContainer.resizable('destroy');
|
||||
filterContainer.resizable({
|
||||
handles: 's'
|
||||
});
|
||||
},
|
||||
|
||||
getResults: function () {
|
||||
gresults.recordFormState();
|
||||
var gresultsData = jQuery('#gresults-results-filter-form').serialize();
|
||||
gresults.sendRequest(gresultsData)
|
||||
},
|
||||
|
||||
sendRequest: function (gresultsData, serverStateObject, checkSum) {
|
||||
var results = jQuery("#gresults-results");
|
||||
var filterButtons = jQuery("#gresults-results-filter-buttons input");
|
||||
var viewSlug = jQuery("#gresults-view-slug").val();
|
||||
var nonce = jQuery("#_gf_results_nonce").val()
|
||||
var data_str = "action=gresults_get_results_" + viewSlug + "&" + gresultsData + '&_gf_results_nonce' + nonce ;
|
||||
if (serverStateObject)
|
||||
data_str += "&state=" + serverStateObject + "&checkSum=" + checkSum;
|
||||
|
||||
gresultsAjaxRequest = jQuery.ajax({
|
||||
url : ajaxurl,
|
||||
type : 'POST',
|
||||
dataType : 'json',
|
||||
data : data_str,
|
||||
beforeSend: function (xhr, opts) {
|
||||
results.fadeTo("slow", 0.33);
|
||||
results.html('');
|
||||
gform.utils.trigger( { event: 'gform/page_loader/show' } );
|
||||
filterButtons.attr('disabled', 'disabled');
|
||||
}
|
||||
})
|
||||
.done(function (response) {
|
||||
if (!response || response === -1) {
|
||||
gform.utils.trigger( { event: 'gform/page_loader/hide' } );
|
||||
results.html(gresultsStrings.ajaxError);
|
||||
} else {
|
||||
if (response.status === "complete") {
|
||||
filterButtons.removeAttr('disabled');
|
||||
gform.utils.trigger( { event: 'gform/page_loader/hide' } );
|
||||
results.html(response.html);
|
||||
jQuery("#gresults-results").data('searchcriteria', response.searchCriteria); //used in 'more' links
|
||||
|
||||
var filterUI = jQuery("#gresults-results-filter").html();
|
||||
|
||||
gresults.drawCharts();
|
||||
results.fadeTo("slow", 1);
|
||||
if (window.history.replaceState) {
|
||||
if (!history.state) {
|
||||
history.replaceState({"html": response.html, "filterUI": filterUI, "searchCriteria": response.searchCriteria}, "", "?" + gresultsData);
|
||||
} else {
|
||||
history.pushState({"html": response.html, "filterUI": filterUI, "searchCriteria": response.searchCriteria}, "", "?" + gresultsData);
|
||||
}
|
||||
}
|
||||
gresults.drawCharts();
|
||||
if (window["gform_initialize_tooltips"])
|
||||
gform_initialize_tooltips();
|
||||
} else if (response.status === "incomplete") {
|
||||
serverStateObject = response.stateObject;
|
||||
gresults.sendRequest(gresultsData, serverStateObject, response.checkSum);
|
||||
results.html(response.html);
|
||||
} else {
|
||||
gform.utils.trigger( { event: 'gform/page_loader/hide' } );
|
||||
results.html(gresultsStrings.ajaxError);
|
||||
}
|
||||
}
|
||||
})
|
||||
.fail(function (error) {
|
||||
filterButtons.removeAttr('disabled');
|
||||
results.fadeTo("fast", 1);
|
||||
var msg = error.statusText;
|
||||
gform.utils.trigger( { event: 'gform/page_loader/hide' } );
|
||||
if (msg == "abort") {
|
||||
msg = "Request cancelled";
|
||||
} else {
|
||||
msg = gresultsStrings.ajaxError;
|
||||
}
|
||||
results.html(msg);
|
||||
})
|
||||
},
|
||||
|
||||
getMoreResults: function (formId, fieldId) {
|
||||
var container = jQuery('#gresults-results-field-content-' + fieldId),
|
||||
results = jQuery("#gresults-results"),
|
||||
offset = jQuery(container).data('offset'),
|
||||
viewSlug = jQuery("#gresults-view-slug").val(),
|
||||
searchCriteria = results.data('searchcriteria'),
|
||||
nonce = jQuery("#_gf_results_nonce").val();
|
||||
|
||||
jQuery.ajax({
|
||||
url : ajaxurl,
|
||||
type : 'POST',
|
||||
dataType: 'json',
|
||||
data : {
|
||||
action: 'gresults_get_more_results_' + viewSlug,
|
||||
view: viewSlug,
|
||||
form_id: formId,
|
||||
field_id: fieldId,
|
||||
offset: offset,
|
||||
search_criteria: searchCriteria,
|
||||
_gf_results_nonce: nonce
|
||||
},
|
||||
success : function (response) {
|
||||
if (response === -1) {
|
||||
//permission denied
|
||||
}
|
||||
else {
|
||||
if (response.html)
|
||||
jQuery(container).append(response.html);
|
||||
if (!response.more_remaining)
|
||||
jQuery('#gresults-results-field-more-link-' + fieldId).hide();
|
||||
|
||||
jQuery(container).data('offset', response.offset);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return false;
|
||||
|
||||
},
|
||||
|
||||
clearFilterForm: function () {
|
||||
jQuery("#gresults-results-field-filters-container").off('click', '.gform-add').gfFilterUI(gresultsFilterSettings, [], true);
|
||||
jQuery('#gresults-results-filter-form').find('input, select').each(function () {
|
||||
switch (this.type) {
|
||||
case 'text':
|
||||
case 'select-one':
|
||||
jQuery(this).val('').change();
|
||||
break;
|
||||
case 'checkbox':
|
||||
case 'radio':
|
||||
this.checked = false;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
recordFormState: function () {
|
||||
jQuery("#gresults-results-filter-form input[type='radio']").each(function () {
|
||||
if (this.checked) {
|
||||
jQuery(this).prop("defaultChecked", true);
|
||||
} else {
|
||||
jQuery(this).prop("defaultChecked", false);
|
||||
}
|
||||
});
|
||||
jQuery("#gresults-results-filter-form input[type='checkbox']").each(function () {
|
||||
if (this.checked) {
|
||||
jQuery(this).prop("defaultChecked", true);
|
||||
} else {
|
||||
jQuery(this).prop("defaultChecked", false);
|
||||
}
|
||||
});
|
||||
jQuery("#gresults-results-filter-form input[type='text']").each(function () {
|
||||
jQuery(this).prop("defaultValue", jQuery(this).val());
|
||||
});
|
||||
jQuery("#gresults-results-filter-form select option").each(function () {
|
||||
jQuery(this).prop("defaultSelected", jQuery(this).prop('selected'));
|
||||
});
|
||||
},
|
||||
|
||||
setCustomFilter: function(key, value){
|
||||
elementId = "gresults-custom-" + key;
|
||||
if(jQuery('#' + elementId).length == 0)
|
||||
jQuery('#gresults-results-filter-form').append("<input type='hidden' id='" + elementId + "' name='" + key + "' value='" + value + "'>");
|
||||
else
|
||||
jQuery('#' + elementId).val(value);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
google.load('visualization', '1', {packages: ['corechart']});
|
||||
google.setOnLoadCallback(gresults.drawCharts);
|
||||
|
||||
|
||||
jQuery( window ).on( 'load', function () {
|
||||
|
||||
if (jQuery("#gresults-results").length > 0) {
|
||||
|
||||
jQuery("#gresults-results-field-filters-container").gfFilterUI(gresultsFilterSettings, gresultsInitVars, true);
|
||||
var $window = jQuery(window);
|
||||
|
||||
$window.resize(function (e) {
|
||||
if (e.target === window) {
|
||||
gresults.drawCharts();
|
||||
}
|
||||
});
|
||||
|
||||
window.onpopstate = function (e) {
|
||||
if (e.state)
|
||||
gresults.renderStateData(e.state)
|
||||
};
|
||||
|
||||
|
||||
jQuery("#gresults-results-filter-date-start, #gresults-results-filter-date-end").datepicker({dateFormat: 'yy-mm-dd', changeMonth: true, changeYear: true});
|
||||
|
||||
jQuery('.ui-datepicker-trigger').on('click', function() {
|
||||
jQuery(this).parent().find('input').datepicker( 'show' );
|
||||
});
|
||||
|
||||
jQuery("#gresults-results-filter-form").submit(function (e) {
|
||||
gresults.getResults();
|
||||
return false;
|
||||
});
|
||||
|
||||
// Ensure we only treat a state as valid if it contains our expected data.
|
||||
const initialState = history.state;
|
||||
const hasGResultsState = initialState && typeof initialState === 'object' && ( 'html' in initialState ) && ( 'searchCriteria' in initialState );
|
||||
|
||||
if ( hasGResultsState ) {
|
||||
gresults.renderStateData( initialState );
|
||||
} else {
|
||||
gresults.getResults();
|
||||
}
|
||||
|
||||
if (window["gform_initialize_tooltips"])
|
||||
gform_initialize_tooltips();
|
||||
|
||||
}
|
||||
});
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,131 @@
|
||||
window.GFToken = null;
|
||||
|
||||
( function( $ ) {
|
||||
|
||||
GFToken = function( args ) {
|
||||
|
||||
for ( var prop in args ) {
|
||||
if ( args.hasOwnProperty( prop ) )
|
||||
this[prop] = args[prop];
|
||||
}
|
||||
|
||||
this.form = $( '#gform_' + this.formId );
|
||||
|
||||
this.init = function() {
|
||||
|
||||
var GFTokenObj = this;
|
||||
|
||||
this.tokens = {};
|
||||
|
||||
/* Initialize spinner. */
|
||||
if ( ! this.isAjax )
|
||||
gformInitSpinner( this.formId );
|
||||
|
||||
/* If multipage form, run on gform_page_loaded. */
|
||||
if ( this.hasPages ) {
|
||||
|
||||
$( document ).bind( 'gform_page_loaded', function( event, form_id, current_page ) {
|
||||
|
||||
if ( form_id != GFTokenObj.formId)
|
||||
return;
|
||||
|
||||
if ( current_page != GFTokenObj.pageCount)
|
||||
GFTokenObj.saveEntryData();
|
||||
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
this.form.submit( function() {
|
||||
GFTokenObj.onSubmit();
|
||||
} );
|
||||
|
||||
};
|
||||
|
||||
this.onSubmit = function() {
|
||||
|
||||
if ( this.form.data('gftokensubmitting') ) {
|
||||
return;
|
||||
} else {
|
||||
event.preventDefault();
|
||||
this.form.data( 'gftokensubmitting', true );
|
||||
}
|
||||
|
||||
this.saveEntryData();
|
||||
this.processTokens();
|
||||
|
||||
}
|
||||
|
||||
this.processTokens = function() {
|
||||
|
||||
/* Process feeds. */
|
||||
for ( var feed_id in this.feeds ) {
|
||||
|
||||
this.active_feed = this.feeds[feed_id];
|
||||
|
||||
/* Create new feed object so we can store the billing information. */
|
||||
var feed = {
|
||||
'billing_fields': {},
|
||||
'id': this.active_feed.id,
|
||||
'name': this.active_feed.name
|
||||
};
|
||||
|
||||
/* Add billing information to feed object. */
|
||||
for ( var billing_field in this.active_feed.billing_fields ) {
|
||||
|
||||
field_id = this.active_feed.billing_fields[ billing_field ];
|
||||
feed.billing_fields[ billing_field ] = this.entry_data[ field_id ];
|
||||
|
||||
}
|
||||
|
||||
/* Get credit card token response. */
|
||||
window[ this.callback ].createToken( feed, this );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this.saveEntryData = function() {
|
||||
|
||||
var GFPaymentObj = this,
|
||||
input_prefix = 'input_' + this.formId + '_';
|
||||
|
||||
if ( ! this.entry_data )
|
||||
this.entry_data = {};
|
||||
|
||||
this.form.find( 'input[id^="' + input_prefix + '"], select[id^="' + input_prefix + '"], textarea[id^="' + input_prefix + '"]' ).each( function() {
|
||||
|
||||
var input_id = $( this ).attr( 'id' ).replace( input_prefix, '' ).replace( '_', '.' );
|
||||
|
||||
if ( $.inArray( input_id, GFPaymentObj.fields ) >= 0 )
|
||||
GFPaymentObj.entry_data[ input_id ] = $( this ).val();
|
||||
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
this.saveToken = function( token ) {
|
||||
|
||||
/* Add token response to tokens array. */
|
||||
this.tokens[ this.active_feed.id ] = {
|
||||
'feed_id': this.active_feed.id,
|
||||
'response': token
|
||||
};
|
||||
|
||||
if ( this.tokens.length == this.feeds.length ) {
|
||||
|
||||
/* Add tokens to form. */
|
||||
this.form.find( this.responseField ).val( $.toJSON( this.tokens ) );
|
||||
|
||||
/* Submit the form. */
|
||||
this.form.submit();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this.init();
|
||||
|
||||
}
|
||||
|
||||
} )( jQuery );
|
||||
@@ -0,0 +1 @@
|
||||
window.GFToken=null,(n=>{GFToken=function(i){for(var t in i)i.hasOwnProperty(t)&&(this[t]=i[t]);this.form=n("#gform_"+this.formId),this.init=function(){var s=this;this.tokens={},this.isAjax||gformInitSpinner(this.formId),this.hasPages&&n(document).bind("gform_page_loaded",function(i,t,e){t==s.formId&&e!=s.pageCount&&s.saveEntryData()}),this.form.submit(function(){s.onSubmit()})},this.onSubmit=function(){this.form.data("gftokensubmitting")||(event.preventDefault(),this.form.data("gftokensubmitting",!0),this.saveEntryData(),this.processTokens())},this.processTokens=function(){for(var i in this.feeds){this.active_feed=this.feeds[i];var t,e={billing_fields:{},id:this.active_feed.id,name:this.active_feed.name};for(t in this.active_feed.billing_fields)field_id=this.active_feed.billing_fields[t],e.billing_fields[t]=this.entry_data[field_id];window[this.callback].createToken(e,this)}},this.saveEntryData=function(){var t=this,e="input_"+this.formId+"_";this.entry_data||(this.entry_data={}),this.form.find('input[id^="'+e+'"], select[id^="'+e+'"], textarea[id^="'+e+'"]').each(function(){var i=n(this).attr("id").replace(e,"").replace("_",".");0<=n.inArray(i,t.fields)&&(t.entry_data[i]=n(this).val())})},this.saveToken=function(i){this.tokens[this.active_feed.id]={feed_id:this.active_feed.id,response:i},this.tokens.length==this.feeds.length&&(this.form.find(this.responseField).val(n.toJSON(this.tokens)),this.form.submit())},this.init()}})(jQuery);
|
||||
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
//Nothing to see here
|
||||
@@ -0,0 +1,329 @@
|
||||
/**
|
||||
* jQuery Repeater
|
||||
*
|
||||
* Easily create a section of repeatable items.
|
||||
*
|
||||
* 1. Include repeater.js
|
||||
* 2. Define a template to be used by the repeater.
|
||||
* a. Input elements should have a class "property_{i}" (do not replace {i} with an index, the script will handle this.
|
||||
* b. The template should include a container for the "row" of elements.
|
||||
* c. Use the {buttons} merge tag to indicate the location of the repeater buttons.
|
||||
*
|
||||
* Example:
|
||||
* <div class="repeater">
|
||||
* <!-- Template Start -->
|
||||
* <div class="row">
|
||||
* <input class="name_{i}" />
|
||||
* <input class="age_{i}" />
|
||||
* {buttons}
|
||||
* </div>
|
||||
* <!-- / Template Ends -->
|
||||
* </div>
|
||||
*
|
||||
* 3. Define a "save" callback to handle how your data is saved. It will give you an array of objects representing your data.
|
||||
*
|
||||
*/
|
||||
|
||||
jQuery.fn.repeater = function( options ) {
|
||||
|
||||
var self = this,
|
||||
defaults = {
|
||||
template: '',
|
||||
limit: 5,
|
||||
items: [{}],
|
||||
saveEvents: 'blur change',
|
||||
saveElements: 'input, select',
|
||||
addButtonMarkup: '+',
|
||||
removeButtonMarkup: '-',
|
||||
minItemCount: 1,
|
||||
callbacks: {
|
||||
save: function() { },
|
||||
beforeAdd: function() { },
|
||||
add: function() { },
|
||||
beforeAddNew: function() { },
|
||||
addNew: function() { },
|
||||
beforeRemove: function() { },
|
||||
remove: function() { },
|
||||
repeaterButtons: function() { return false; }
|
||||
}
|
||||
};
|
||||
|
||||
self.options = jQuery.extend( true, {}, defaults, options );
|
||||
self.elem = jQuery( this );
|
||||
self.items = self.options.items;
|
||||
self.callbacks = self.options.callbacks;
|
||||
self._template = self.options.template;
|
||||
self._baseObj = self.items[0];
|
||||
|
||||
self.init = function() {
|
||||
|
||||
self.stashTemplate();
|
||||
|
||||
self.elem.addClass( 'repeater' );
|
||||
self.refresh();
|
||||
|
||||
self.bindEvents();
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
self.bindEvents = function() {
|
||||
|
||||
self.options.saveEvents = self.getNamespacedEvents( self.options.saveEvents );
|
||||
|
||||
self.elem.off( 'click.repeater', 'a.add-item' );
|
||||
self.elem.on( 'click.repeater', 'a.add-item:not(.inactive)', function() {
|
||||
self.addNewItem( this );
|
||||
});
|
||||
|
||||
self.elem.off( 'click.repeater', 'a.remove-item' );
|
||||
self.elem.on( 'click.repeater', 'a.remove-item', function( event ){
|
||||
self.removeItem( this );
|
||||
});
|
||||
|
||||
self.elem.off( self.options.saveEvents, self.options.saveElements );
|
||||
self.elem.on( self.options.saveEvents, self.options.saveElements, function() {
|
||||
self.save();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
self.stashTemplate = function() {
|
||||
|
||||
// if no template provided or in "storage", use current HTML
|
||||
if( ! self._template )
|
||||
self._template = self.elem.html();
|
||||
|
||||
self._template = jQuery.trim( self._template );
|
||||
|
||||
}
|
||||
|
||||
self.addItem = function( item, index ) {
|
||||
|
||||
var itemMarkup = self.getItemMarkup( item, index),
|
||||
itemElem = jQuery( itemMarkup ).addClass( 'item-' + index );
|
||||
|
||||
self.callbacks.beforeAdd( self, itemElem, item, index );
|
||||
|
||||
self.append( itemElem );
|
||||
self.populateSelects( item, index );
|
||||
|
||||
self.callbacks.add( self, itemElem, item, index );
|
||||
|
||||
}
|
||||
|
||||
self.getItemMarkup = function( item, index ) {
|
||||
|
||||
var itemMarkup = self._template;
|
||||
|
||||
for( var property in item ) {
|
||||
|
||||
if( ! item.hasOwnProperty( property ) )
|
||||
continue;
|
||||
|
||||
itemMarkup = itemMarkup.replace( /{i}/g, index );
|
||||
itemMarkup = itemMarkup.replace( '{buttons}', self.getRepeaterButtonsMarkup( index ) );
|
||||
itemMarkup = itemMarkup.replace( new RegExp( '{' + property + '}', 'g' ), escapeAttr( item[property] ) );
|
||||
|
||||
}
|
||||
|
||||
return itemMarkup;
|
||||
}
|
||||
|
||||
self.getRepeaterButtonsMarkup = function( index ) {
|
||||
|
||||
var buttonsMarkup = self.callbacks.repeaterButtons( self, index );
|
||||
|
||||
if( ! buttonsMarkup )
|
||||
buttonsMarkup = self.getDefaultButtonsMarkup( index );
|
||||
|
||||
return buttonsMarkup;
|
||||
}
|
||||
|
||||
self.getDefaultButtonsMarkup = function( index ) {
|
||||
|
||||
var cssClass = self.items.length >= self.options.limit && self.options.limit !== 0 ? 'inactive' : '',
|
||||
buttons = '<a class="add-item ' + cssClass + '" data-index="' + index + '">' + self.options.addButtonMarkup + '</a>';
|
||||
|
||||
if( self.items.length > self.options.minItemCount )
|
||||
buttons += '<a class="remove-item" data-index="' + index + '">' + self.options.removeButtonMarkup + '</a>';
|
||||
|
||||
return '<div class="repeater-buttons">' + buttons + '</div>';
|
||||
}
|
||||
|
||||
self.populateSelects = function( item, index ) {
|
||||
|
||||
// after appending the row, check each property to see if it is a select and then populate
|
||||
for ( var property in item ) {
|
||||
|
||||
if ( ! item.hasOwnProperty( property ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var input = self.elem.find( '.' + property + '_' + index );
|
||||
|
||||
if ( ! input.is( 'select' ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( jQuery.isArray( item[ property ] ) ) {
|
||||
input.val( item[ property ] );
|
||||
} else {
|
||||
input.find( 'option[value="' + item[ property ] + '"]' ).prop( 'selected', true );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
self.addNewItem = function( elemOrItem, index ) {
|
||||
|
||||
var isElem = self.isElement( elemOrItem ),
|
||||
index = parseInt( typeof index !== 'undefined' ? index : ( isElem ? parseInt( jQuery( elemOrItem ).attr( 'data-index' ), 10 ) + 1 : self.items.length ), 10 ),
|
||||
item = isElem ? self.getBaseObject() : elemOrItem;
|
||||
|
||||
self.callbacks.beforeAddNew( self, index );
|
||||
self.items.splice( index, 0, item );
|
||||
self.callbacks.addNew( self, index );
|
||||
|
||||
self.refresh().save();
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
self.removeItem = function( elemOrIndex ) {
|
||||
|
||||
var index = self.isElement( elemOrIndex ) ? jQuery( elemOrIndex ).attr( 'data-index' ) : elemOrIndex;
|
||||
|
||||
self.callbacks.beforeRemove( self, index );
|
||||
|
||||
// using delete (over splice) to maintain the correct indexes for
|
||||
// the items array when saving the data from the UI
|
||||
delete self.items[index];
|
||||
|
||||
self.callbacks.remove( self, index );
|
||||
|
||||
self.save().refresh();
|
||||
|
||||
}
|
||||
|
||||
self.refresh = function() {
|
||||
|
||||
self.elem.empty();
|
||||
|
||||
for( var i = 0; i < self.items.length; i++ ) {
|
||||
self.addItem( self.items[i], i );
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
self.save = function() {
|
||||
|
||||
var keys = self.getBaseObjectKeys(),
|
||||
data = [];
|
||||
|
||||
for( var i = 0; i < self.items.length; i++ ) {
|
||||
|
||||
if( typeof self.items[i] == 'undefined' )
|
||||
continue;
|
||||
|
||||
var item = {};
|
||||
|
||||
for( var j = 0; j < keys.length; j++ ) {
|
||||
|
||||
var key = keys[j],
|
||||
id = '.' + key + '_' + i,
|
||||
value = self.elem.find( id ).val();
|
||||
|
||||
item[key] = typeof value == 'undefined' ? false : value;
|
||||
|
||||
}
|
||||
|
||||
data.push( item );
|
||||
|
||||
}
|
||||
|
||||
// save data to items
|
||||
self.items = data;
|
||||
|
||||
// save data externally via callback
|
||||
self.callbacks.save( self, data );
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loops through the current items array and retrieves the object properties of the
|
||||
* first valid item object. Originally this would simply pull the object keys from
|
||||
* the first index of the items array; however, when the first item has been
|
||||
* 'deleted' (see the save() method), it will be undefined.
|
||||
*/
|
||||
self.getBaseObjectKeys = function() {
|
||||
|
||||
var keys = [],
|
||||
items = self.items.length > 0 ? self.items : [ self._baseObj ];
|
||||
|
||||
for( var i = 0; i < items.length; i++ ) {
|
||||
|
||||
if( typeof items[i] == 'undefined' )
|
||||
continue;
|
||||
|
||||
for( var key in items[i] ) {
|
||||
if( ! items[i].hasOwnProperty( key ) )
|
||||
continue;
|
||||
keys.push( key );
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
self.getBaseObject = function() {
|
||||
|
||||
var item = {},
|
||||
keys = self.getBaseObjectKeys();
|
||||
|
||||
for( var i = 0; i < keys.length; i++ ) {
|
||||
item[keys[i]] = '';
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
self.getNamespacedEvents = function( events ) {
|
||||
|
||||
var events = events.split( ' ' ),
|
||||
namespacedEvents = [];
|
||||
|
||||
for( var i = 0; i < events.length; i++ ) {
|
||||
namespacedEvents.push( events[i] + '.repeater' );
|
||||
}
|
||||
|
||||
return namespacedEvents.join( ' ' );
|
||||
}
|
||||
|
||||
/**
|
||||
* http://stackoverflow.com/questions/384286/javascript-isdom-how-do-you-check-if-a-javascript-object-is-a-dom-object
|
||||
* @param obj
|
||||
* @returns {boolean}
|
||||
*/
|
||||
self.isElement = function( obj ) {
|
||||
try {
|
||||
//Using W3 DOM2 (works for FF, Opera and Chrom)
|
||||
return obj instanceof HTMLElement;
|
||||
}
|
||||
catch(e){
|
||||
//Browsers not supporting W3 DOM2 don't have HTMLElement and
|
||||
//an exception is thrown and we end up here. Testing some
|
||||
//properties that all elements have. (works on IE7)
|
||||
return (typeof obj==="object") &&
|
||||
(obj.nodeType===1) && (typeof obj.style === "object") &&
|
||||
(typeof obj.ownerDocument ==="object");
|
||||
}
|
||||
}
|
||||
|
||||
return self.init();
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
jQuery.fn.repeater=function(e){var i=this;return i.options=jQuery.extend(!0,{},{template:"",limit:5,items:[{}],saveEvents:"blur change",saveElements:"input, select",addButtonMarkup:"+",removeButtonMarkup:"-",minItemCount:1,callbacks:{save:function(){},beforeAdd:function(){},add:function(){},beforeAddNew:function(){},addNew:function(){},beforeRemove:function(){},remove:function(){},repeaterButtons:function(){return!1}}},e),i.elem=jQuery(this),i.items=i.options.items,i.callbacks=i.options.callbacks,i._template=i.options.template,i._baseObj=i.items[0],i.init=function(){return i.stashTemplate(),i.elem.addClass("repeater"),i.refresh(),i.bindEvents(),i},i.bindEvents=function(){i.options.saveEvents=i.getNamespacedEvents(i.options.saveEvents),i.elem.off("click.repeater","a.add-item"),i.elem.on("click.repeater","a.add-item:not(.inactive)",function(){i.addNewItem(this)}),i.elem.off("click.repeater","a.remove-item"),i.elem.on("click.repeater","a.remove-item",function(e){i.removeItem(this)}),i.elem.off(i.options.saveEvents,i.options.saveElements),i.elem.on(i.options.saveEvents,i.options.saveElements,function(){i.save()})},i.stashTemplate=function(){i._template||(i._template=i.elem.html()),i._template=jQuery.trim(i._template)},i.addItem=function(e,t){var n=i.getItemMarkup(e,t),n=jQuery(n).addClass("item-"+t);i.callbacks.beforeAdd(i,n,e,t),i.append(n),i.populateSelects(e,t),i.callbacks.add(i,n,e,t)},i.getItemMarkup=function(e,t){var n,a=i._template;for(n in e)e.hasOwnProperty(n)&&(a=(a=(a=a.replace(/{i}/g,t)).replace("{buttons}",i.getRepeaterButtonsMarkup(t))).replace(new RegExp("{"+n+"}","g"),escapeAttr(e[n])));return a},i.getRepeaterButtonsMarkup=function(e){return i.callbacks.repeaterButtons(i,e)||i.getDefaultButtonsMarkup(e)},i.getDefaultButtonsMarkup=function(e){var t='<a class="add-item '+(i.items.length>=i.options.limit&&0!==i.options.limit?"inactive":"")+'" data-index="'+e+'">'+i.options.addButtonMarkup+"</a>";return i.items.length>i.options.minItemCount&&(t+='<a class="remove-item" data-index="'+e+'">'+i.options.removeButtonMarkup+"</a>"),'<div class="repeater-buttons">'+t+"</div>"},i.populateSelects=function(e,t){for(var n in e){var a;e.hasOwnProperty(n)&&(a=i.elem.find("."+n+"_"+t)).is("select")&&(jQuery.isArray(e[n])?a.val(e[n]):a.find('option[value="'+e[n]+'"]').prop("selected",!0))}},i.addNewItem=function(e,t){var n=i.isElement(e),t=parseInt(void 0!==t?t:n?parseInt(jQuery(e).attr("data-index"),10)+1:i.items.length,10),n=n?i.getBaseObject():e;return i.callbacks.beforeAddNew(i,t),i.items.splice(t,0,n),i.callbacks.addNew(i,t),i.refresh().save(),i},i.removeItem=function(e){e=i.isElement(e)?jQuery(e).attr("data-index"):e;i.callbacks.beforeRemove(i,e),delete i.items[e],i.callbacks.remove(i,e),i.save().refresh()},i.refresh=function(){i.elem.empty();for(var e=0;e<i.items.length;e++)i.addItem(i.items[e],e);return i},i.save=function(){for(var e=i.getBaseObjectKeys(),t=[],n=0;n<i.items.length;n++)if(void 0!==i.items[n]){for(var a={},r=0;r<e.length;r++){var s=e[r],o=i.elem.find("."+s+"_"+n).val();a[s]=void 0!==o&&o}t.push(a)}return i.items=t,i.callbacks.save(i,t),i},i.getBaseObjectKeys=function(){for(var e=[],t=0<i.items.length?i.items:[i._baseObj],n=0;n<t.length;n++)if(void 0!==t[n]){for(var a in t[n])t[n].hasOwnProperty(a)&&e.push(a);break}return e},i.getBaseObject=function(){for(var e={},t=i.getBaseObjectKeys(),n=0;n<t.length;n++)e[t[n]]="";return e},i.getNamespacedEvents=function(e){for(var e=e.split(" "),t=[],n=0;n<e.length;n++)t.push(e[n]+".repeater");return t.join(" ")},i.isElement=function(t){try{return t instanceof HTMLElement}catch(e){return"object"==typeof t&&1===t.nodeType&&"object"==typeof t.style&&"object"==typeof t.ownerDocument}},i.init()};
|
||||
@@ -0,0 +1,302 @@
|
||||
<?php
|
||||
/**
|
||||
* Handles AJAX services such as validation and submission.
|
||||
*
|
||||
* @package Gravity_Forms\Gravity_Forms\Ajax
|
||||
*/
|
||||
namespace Gravity_Forms\Gravity_Forms\Ajax;
|
||||
|
||||
use GFCommon;
|
||||
|
||||
/**
|
||||
* Class GF_Ajax_Handler
|
||||
*
|
||||
* @since 2.9.0
|
||||
*
|
||||
* Provides functionality for handling AJAX validation and submission.
|
||||
*/
|
||||
class GF_Ajax_Handler {
|
||||
|
||||
|
||||
/**
|
||||
* Handles the form validation AJAX requests. Uses the global $_POST array and sends the form validation result as a JSON response.
|
||||
*
|
||||
* @since 2.9.0
|
||||
*
|
||||
* @deprecated 2.9.9 Use GFAPI::validate_form() instead.
|
||||
* @remove-in 4.0
|
||||
*/
|
||||
public function validate_form() {
|
||||
|
||||
_deprecated_function( __METHOD__, '2.9.9', 'GFAPI::validate_form()' );
|
||||
|
||||
// Check nonce.
|
||||
$nonce_result = check_ajax_referer( 'gform_ajax_submission', 'gform_ajax_nonce', false );
|
||||
|
||||
if ( ! $nonce_result ) {
|
||||
wp_send_json_error( $this->nonce_validation_message() );
|
||||
}
|
||||
|
||||
$form_id = absint( rgpost( 'form_id' ) );
|
||||
$target_page = absint( rgpost( 'gform_target_page_number_' . $form_id ) );
|
||||
$source_page = absint( rgpost( 'gform_source_page_number_' . $form_id ) );
|
||||
|
||||
$this->hydrate_get_from_current_page_url();
|
||||
|
||||
$result = \GFAPI::validate_form( $form_id, array(), rgpost( 'gform_field_values' ), $target_page, $source_page );
|
||||
if ( is_wp_error( $result ) ) {
|
||||
wp_send_json_error( $result->get_error_message() );
|
||||
}
|
||||
|
||||
$form = $result['form'];
|
||||
if ( ! $result['is_valid'] ) {
|
||||
$result = $this->add_validation_summary( $form, $result );
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the form validation result.
|
||||
*
|
||||
* @since 2.9.0
|
||||
*
|
||||
* @param array $result The form validation result to be filtered.
|
||||
*
|
||||
* @return array The filtered form validation result.
|
||||
*/
|
||||
$result = gf_apply_filters( array( 'gform_ajax_validation_result', $form['id'] ), $result );
|
||||
|
||||
// Remove form from result.
|
||||
unset( $result['form'] );
|
||||
|
||||
wp_send_json_success( $result );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handles the form submission AJAX requests. Uses the global $_POST array and sends the form submission result as a JSON response.
|
||||
*
|
||||
* @since 2.9.0
|
||||
*/
|
||||
public function submit_form() {
|
||||
|
||||
// Check nonce.
|
||||
$nonce_result = check_ajax_referer( 'gform_ajax_submission', 'gform_ajax_nonce', false );
|
||||
|
||||
if ( ! $nonce_result ) {
|
||||
GFCommon::send_json_error( $this->nonce_validation_message() );
|
||||
}
|
||||
|
||||
$this->hydrate_get_from_current_page_url();
|
||||
|
||||
$form_id = absint( rgpost( 'form_id' ) );
|
||||
|
||||
/**
|
||||
* Allows actions to be performed right before an AJAX form submission.
|
||||
*
|
||||
* @since 2.9.0
|
||||
*
|
||||
* @param int $form_id The form ID.
|
||||
*/
|
||||
gf_do_action( array( 'gform_ajax_pre_submit_form', $form_id ), $form_id );
|
||||
|
||||
// Handling the save link submission.
|
||||
if ( isset( $_POST['gform_send_resume_link'] ) ) {
|
||||
$this->submit_save_link();
|
||||
return;
|
||||
}
|
||||
|
||||
// Getting posted values.
|
||||
$target_page = absint( rgpost( 'gform_target_page_number_' . $form_id ) );
|
||||
$source_page = absint( rgpost( 'gform_source_page_number_' . $form_id ) );
|
||||
$field_values = rgpost( 'gform_field_values' );
|
||||
$theme = rgpost( 'gform_theme' );
|
||||
$style = rgpost( 'gform_style_settings' );
|
||||
$submission_method = rgpost( 'gform_submission_method' );
|
||||
|
||||
require_once \GFCommon::get_base_path() . '/form_display.php';
|
||||
|
||||
$result = \GFAPI::submit_form( $form_id, array(), $field_values, $target_page, $source_page, \GFFormDisplay::SUBMISSION_INITIATED_BY_WEBFORM );
|
||||
|
||||
if ( is_wp_error( $result ) ) {
|
||||
if ( $result->get_error_code() === 'button_logic_error' ) {
|
||||
$message = esc_html__( 'There was a problem with your submission.', 'gravityforms' ) . ' ' . $result->get_error_message();
|
||||
} else {
|
||||
$message = $result->get_error_message();
|
||||
}
|
||||
|
||||
GFCommon::send_json_error( $message );
|
||||
}
|
||||
|
||||
$form = $result['form'];
|
||||
|
||||
// Adding confirmation markup if there is a confirmation message to be displayed.
|
||||
if ( rgar( $result, 'confirmation_type' ) == 'message' && ! empty( rgar( $result, 'confirmation_message' ) ) ) {
|
||||
// Get confirmation markup from get_form(). This is necessary to ensure that confirmation markup is properly formatted.
|
||||
$result['confirmation_markup'] = \GFFormDisplay::get_form( $form_id, false, false, false, $field_values, false, 0, $theme, $style );
|
||||
} elseif ( ! $result['is_valid'] ) {
|
||||
$result = $this->add_validation_summary( $form, $result );
|
||||
|
||||
// Refresh the form markup if single page or multipage forms have validation errors.
|
||||
$result['form_markup'] = \GFFormDisplay::get_form( $form_id, (bool) rgpost( 'display_title' ), (bool) rgpost( 'display_description' ), false, $field_values, false, 0, $theme, $style );
|
||||
} elseif ( $target_page > 0 ) {
|
||||
// Getting the target page number taking page conditional logic into account.
|
||||
$page_number = \GFFormDisplay::get_target_page( $form, $source_page, $field_values );
|
||||
|
||||
$result = $this->add_validation_summary( $form, $result );
|
||||
|
||||
// Getting the field markup for the target page if the form is a multipage form.
|
||||
$result['page_markup'] = \GFFormDisplay::get_page( $form_id, $page_number, $field_values, $theme, $style, $submission_method );
|
||||
|
||||
// Ensure the form UUID is maintained on page changes.
|
||||
$result['form_unique_id'] = \GFFormsModel::get_form_unique_id( $form_id );
|
||||
}
|
||||
|
||||
$result['submission_type'] = $this->get_submission_type( $target_page, $source_page );
|
||||
|
||||
/**
|
||||
* Filters the ajax form submission result.
|
||||
*
|
||||
* @since 2.9.0
|
||||
*
|
||||
* @param array $result The form submission result to be filtered.
|
||||
*
|
||||
* @return array The filtered form submission result.
|
||||
*/
|
||||
$result = gf_apply_filters( array( 'gform_ajax_submission_result', $form_id ), $result );
|
||||
|
||||
// Remove form from result.
|
||||
unset( $result['form'] );
|
||||
|
||||
GFCommon::send_json_success( $result );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the submission type based on the target and source page numbers.
|
||||
*
|
||||
* @since 2.9.7
|
||||
*
|
||||
* @param int $target_page The target page number.
|
||||
* @param int $source_page The source page number.
|
||||
*
|
||||
* @return string The submission type. Possible values are SUBMISSION_TYPE_SUBMIT, SUBMISSION_TYPE_NEXT, SUBMISSION_TYPE_PREVIOUS, and SUBMISSION_TYPE_SAVE_AND_CONTINUE.
|
||||
*/
|
||||
public function get_submission_type( $target_page, $source_page ) {
|
||||
if ( isset( $_POST['gform_send_resume_link'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
|
||||
return \GFFormDisplay::SUBMISSION_TYPE_SEND_LINK;
|
||||
} elseif ( rgpost( 'gform_save') ) {
|
||||
return \GFFormDisplay::SUBMISSION_TYPE_SAVE_AND_CONTINUE;
|
||||
} elseif ( $target_page === 0 ) {
|
||||
return \GFFormDisplay::SUBMISSION_TYPE_SUBMIT;
|
||||
} elseif ( $target_page > $source_page ) {
|
||||
return \GFFormDisplay::SUBMISSION_TYPE_NEXT;
|
||||
} else {
|
||||
return \GFFormDisplay::SUBMISSION_TYPE_PREVIOUS;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the save link submission. Uses the $_POST array and sends the save link result as a JSON response.
|
||||
*
|
||||
* @since 2.9.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function submit_save_link() {
|
||||
$form_id = absint( rgpost( 'form_id' ) );
|
||||
|
||||
\GFFormDisplay::process_send_resume_link();
|
||||
|
||||
$confirmation = \GFFormDisplay::get_form( $form_id, false, false, false, rgpost( 'gform_field_values' ) );
|
||||
|
||||
GFCommon::send_json_success(
|
||||
array(
|
||||
'is_valid' => true,
|
||||
'confirmation_type' => 'message',
|
||||
'confirmation_message' => $confirmation,
|
||||
'confirmation_markup' => $confirmation,
|
||||
'submission_type' => \GFFormDisplay::SUBMISSION_TYPE_SEND_LINK,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the lifespan of the nonce used for AJAX submissions and validation.
|
||||
*
|
||||
* @since 2.9.0
|
||||
*
|
||||
* @param int $lifespan_in_seconds The lifespan of the nonce in seconds. Defaults to 3 days
|
||||
* @param string $action The nonce action (gform_ajax_submission or gform_ajax_validation).
|
||||
*
|
||||
* @return int The filtered lifespan of the nonce in seconds.
|
||||
*/
|
||||
public function nonce_life( $lifespan_in_seconds, $action = '' ) {
|
||||
if ( in_array( $action, array( 'gform_ajax_submission', 'gform_ajax_validation' ) ) ) {
|
||||
|
||||
/**
|
||||
* Filters the lifespan of the nonce used for AJAX submissions and validation.
|
||||
*
|
||||
* @since 2.9.0
|
||||
*
|
||||
* @param int $lifespan_in_seconds The lifespan of the nonce in seconds (defaults to 3 days).
|
||||
* @param string $action The nonce action (gform_ajax_submission or gform_ajax_validation).
|
||||
*
|
||||
* @return int The lifespan of the nonce in seconds.
|
||||
*/
|
||||
$lifespan_in_seconds = apply_filters( 'gform_nonce_life', 3 * DAY_IN_SECONDS, $action );
|
||||
}
|
||||
|
||||
return $lifespan_in_seconds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the nonce validation message.
|
||||
*
|
||||
* @since 2.9.0
|
||||
*
|
||||
* @return string The nonce validation message.
|
||||
*/
|
||||
private function nonce_validation_message() {
|
||||
return esc_html__( 'Your session has expired. Please refresh the page and try again.', 'gravityforms' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the validation summary properties to the form validation result.
|
||||
*
|
||||
* @since 2.9.0
|
||||
*
|
||||
* @param array $form The form being validated.
|
||||
* @param array $result The form validation result.
|
||||
*
|
||||
* @return mixed Returns the form validation result with the validation summary properties added.
|
||||
*/
|
||||
private function add_validation_summary( $form, $result ) {
|
||||
$summary = \GFFormDisplay::get_validation_errors_markup( $form, array(), rgar( $form, 'validationSummary' ) );
|
||||
$result['validation_summary'] = wp_kses( $summary, wp_kses_allowed_html( 'post' ) );
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Making the current page query string available for use with form filters.
|
||||
*
|
||||
* @since 2.9.5
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function hydrate_get_from_current_page_url() {
|
||||
$url = rgpost( 'current_page_url' );
|
||||
|
||||
if ( empty( $url ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$query_string = parse_url( rawurldecode( $url ), PHP_URL_QUERY );
|
||||
|
||||
if ( empty( $query_string ) || ! is_string( $query_string ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
parse_str( $query_string, $query );
|
||||
unset( $query['gf_page'] ); // Removing so it doesn't conflict with gf_ajax_page=preview.
|
||||
$_GET = array_merge( $_GET, $query ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
/**
|
||||
* Service Provider for AJAX Service
|
||||
*
|
||||
* @package Gravity_Forms\Gravity_Forms\Ajax
|
||||
*/
|
||||
|
||||
namespace Gravity_Forms\Gravity_Forms\Ajax;
|
||||
|
||||
use Gravity_Forms\Gravity_Forms\Config\GF_Config_Service_Provider;
|
||||
use Gravity_Forms\Gravity_Forms\GF_Service_Container;
|
||||
use Gravity_Forms\Gravity_Forms\GF_Service_Provider;
|
||||
use Gravity_Forms\Gravity_Forms\Ajax\Config\GF_Ajax_Config;
|
||||
|
||||
/**
|
||||
* Class GF_Ajax_Service_Provider
|
||||
*
|
||||
* Service provider for the Ajax Service.
|
||||
*/
|
||||
class GF_Ajax_Service_Provider extends GF_Service_Provider {
|
||||
|
||||
const GF_AJAX_HANDLER = 'gf_ajax_handler';
|
||||
const GF_AJAX_CONFIG = 'gf_ajax_config';
|
||||
|
||||
/**
|
||||
* Includes all related files and adds all containers.
|
||||
*
|
||||
* @param GF_Service_Container $container Container singleton object.
|
||||
*/
|
||||
public function register( GF_Service_Container $container ) {
|
||||
|
||||
require_once plugin_dir_path( __FILE__ ) . 'class-gf-ajax-handler.php';
|
||||
require_once plugin_dir_path( __FILE__ ) . 'config/class-gf-ajax-config.php';
|
||||
|
||||
// Registering handler
|
||||
$container->add(
|
||||
self::GF_AJAX_HANDLER,
|
||||
function () {
|
||||
return new GF_Ajax_Handler();
|
||||
}
|
||||
);
|
||||
|
||||
// Registering config
|
||||
$container->add(
|
||||
self::GF_AJAX_CONFIG,
|
||||
function () use ( $container ) {
|
||||
return new GF_Ajax_Config( $container->get( GF_Config_Service_Provider::DATA_PARSER ) );
|
||||
}
|
||||
);
|
||||
$container->get( GF_Config_Service_Provider::CONFIG_COLLECTION )->add_config( $container->get( self::GF_AJAX_CONFIG ) );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes service.
|
||||
*
|
||||
* @param GF_Service_Container $container Service Container.
|
||||
*/
|
||||
public function init( GF_Service_Container $container ) {
|
||||
parent::init( $container );
|
||||
|
||||
$ajax_handler = $container->get( self::GF_AJAX_HANDLER );
|
||||
|
||||
// Register nonce lifespan hook.
|
||||
add_filter( 'nonce_life', array( $ajax_handler, 'nonce_life' ), 10, 2 );
|
||||
|
||||
// Register AJAX submission.
|
||||
add_action( 'wp_ajax_gform_submit_form', array( $ajax_handler, 'submit_form' ) );
|
||||
add_action( 'wp_ajax_nopriv_gform_submit_form', array( $ajax_handler, 'submit_form' ) );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace Gravity_Forms\Gravity_Forms\Ajax\Config;
|
||||
|
||||
use Gravity_Forms\Gravity_Forms\Config\GF_Config;
|
||||
use GFForms;
|
||||
|
||||
/**
|
||||
* Config items for Ajax operations
|
||||
*
|
||||
* @since 2.9.0
|
||||
*/
|
||||
class GF_Ajax_Config extends GF_Config {
|
||||
|
||||
protected $name = 'gform_theme_config';
|
||||
protected $script_to_localize = 'gform_gravityforms_theme';
|
||||
|
||||
/**
|
||||
* Config data.
|
||||
*
|
||||
* @return array[]
|
||||
*/
|
||||
public function data() {
|
||||
$preview_query_string = \GFCommon::is_preview() ? '?gf_ajax_page=preview' : '';
|
||||
return array(
|
||||
'common' => array(
|
||||
'form' => array(
|
||||
'ajax' => array(
|
||||
'ajaxurl' => admin_url( 'admin-ajax.php' ) . $preview_query_string,
|
||||
'ajax_submission_nonce' => wp_create_nonce( 'gform_ajax_submission' ),
|
||||
'i18n' => array(
|
||||
/* Translators: This is used to announce the current step of a multipage form, 1. first step number, 2. total steps number, example: Step 1 of 5 */
|
||||
'step_announcement' => esc_html__( 'Step %1$s of %2$s, %3$s', 'gravityforms' ),
|
||||
'unknown_error' => esc_html__( 'There was an unknown error processing your request. Please try again.', 'gravityforms' ),
|
||||
'error_403' => esc_html__( 'The request was blocked (403 error) for unknown security reasons. Remove any code-like text (scripts or DB queries) and try again.', 'gravityforms' ),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
+67
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace Gravity_Forms\Gravity_Forms\Assets\Admin_Dependencies;
|
||||
|
||||
use Gravity_Forms\Gravity_Forms\Assets\GF_Dependencies;
|
||||
use Gravity_Forms\Gravity_Forms\GF_Service_Container;
|
||||
use Gravity_Forms\Gravity_Forms\GF_Service_Provider;
|
||||
|
||||
/**
|
||||
* Class GF_Admin_Script_Dependencies
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @package Gravity_Forms\Gravity_Forms\Assets\Admin_Dependencies;
|
||||
*/
|
||||
class GF_Admin_Script_Dependencies extends GF_Dependencies {
|
||||
|
||||
/**
|
||||
* Items to enqueue globally in admin.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $items = array(
|
||||
'gform_gravityforms_admin',
|
||||
);
|
||||
|
||||
/**
|
||||
* Enqueue the item by handle.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @param $handle
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function do_enqueue( $handle ) {
|
||||
wp_enqueue_script( $handle );
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the global scripts should enqueue.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function should_enqueue() {
|
||||
/***
|
||||
* The newer JavaScript added in 2.5 is now enqueued globally in the admin.
|
||||
* We implemented this as we are now using code splitting to only inject JavaScript
|
||||
* dynamically as it is needed, and to also allow our addons easy access to the core libraries
|
||||
* we use.
|
||||
*
|
||||
* This filter allows users to make our admin scripts only load on Gravity Forms admin screens.
|
||||
* Setting it to false may cause unexpected behavior/feature loss in some addons or core.
|
||||
*
|
||||
* @since 2.6.0
|
||||
*
|
||||
* @param bool true Load admin scripts globally?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
return apply_filters( 'gform_load_admin_scripts_globally', true );
|
||||
}
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace Gravity_Forms\Gravity_Forms\Assets\Admin_Dependencies;
|
||||
|
||||
use Gravity_Forms\Gravity_Forms\Assets\GF_Dependencies;
|
||||
use Gravity_Forms\Gravity_Forms\GF_Service_Container;
|
||||
use Gravity_Forms\Gravity_Forms\GF_Service_Provider;
|
||||
|
||||
/**
|
||||
* Class GF_Admin_Style_Dependencies
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @package Gravity_Forms\Gravity_Forms\Assets\Admin_Dependencies;
|
||||
*/
|
||||
class GF_Admin_Style_Dependencies extends GF_Dependencies {
|
||||
|
||||
/**
|
||||
* Items to enqueue globally in admin.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $items = array(
|
||||
'gform_common_css_utilities',
|
||||
'gform_common_icons',
|
||||
'gform_admin_icons',
|
||||
'gform_admin_components',
|
||||
'gform_admin_css_utilities',
|
||||
);
|
||||
|
||||
/**
|
||||
* Enqueue the item by handle.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @param $handle
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function do_enqueue( $handle ) {
|
||||
wp_enqueue_style( $handle );
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
namespace Gravity_Forms\Gravity_Forms\Assets;
|
||||
|
||||
class GF_Asset_Processor {
|
||||
|
||||
/**
|
||||
* @var array $map - The Hash Map generated by our node scripts.
|
||||
*/
|
||||
private $map;
|
||||
|
||||
/**
|
||||
* @var string $asset_path - The path to the js dist directory.
|
||||
*/
|
||||
private $asset_path;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @param array $map
|
||||
* @param string $asset_path
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct( $map, $asset_path ) {
|
||||
$this->map = $map;
|
||||
$this->asset_path = $asset_path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform processing actions on assets.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process_assets() {
|
||||
$this->process_versions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the ver values for all of the registered scripts in order to append a
|
||||
* file hash (if it exists) or the filemtime (if required).
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function process_versions() {
|
||||
global $wp_scripts;
|
||||
|
||||
$registered = $wp_scripts->registered;
|
||||
|
||||
foreach( $registered as &$asset ) {
|
||||
|
||||
// Bail if not one of our assets or asset is empty.
|
||||
if ( $asset->src && strpos( $asset->src, 'gravityforms/assets/js/dist' ) === false || is_null( $asset->src ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$basename = basename( $asset->src );
|
||||
$path = sprintf( '%s/%s', $this->asset_path, $basename );
|
||||
|
||||
// Asset doesn't exist in hash_map, skip.
|
||||
if ( ! array_key_exists( $basename, $this->map ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// The hash is either the value from our map, or the filemtime for dev.
|
||||
$hash = defined( 'GF_DEV_TIME_AS_VER' ) && GF_DEV_TIME_AS_VER ?
|
||||
filemtime( $path ) :
|
||||
$this->map[ $basename ]['version'];
|
||||
|
||||
$asset->ver = $hash;
|
||||
}
|
||||
|
||||
$wp_scripts->registered = $registered;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
<?php
|
||||
|
||||
namespace Gravity_Forms\Gravity_Forms\Assets;
|
||||
|
||||
use Gravity_Forms\Gravity_Forms\Assets\Theme_Dependencies\GF_Theme_Script_Dependencies;
|
||||
use Gravity_Forms\Gravity_Forms\GF_Service_Container;
|
||||
use Gravity_Forms\Gravity_Forms\GF_Service_Provider;
|
||||
|
||||
use Gravity_Forms\Gravity_Forms\Assets\Admin_Dependencies\GF_Admin_Script_Dependencies;
|
||||
use Gravity_Forms\Gravity_Forms\Assets\Admin_Dependencies\GF_Admin_Style_Dependencies;
|
||||
|
||||
/**
|
||||
* Class GF_Asset_Service_Provider
|
||||
*
|
||||
* Service provider for assets.
|
||||
*
|
||||
* @package Gravity_Forms\Gravity_Forms\Merge_Tags;
|
||||
*/
|
||||
class GF_Asset_Service_Provider extends GF_Service_Provider {
|
||||
|
||||
const HASH_MAP = 'hash_map';
|
||||
const ASSET_PROCESSOR = 'asset_processor';
|
||||
const STYLE_DEPS = 'gf_global_style_deps';
|
||||
const SCRIPT_DEPS = 'gf_global_script_deps';
|
||||
const SCRIPT_DEPS_THEME = 'gf_global_script_deps_theme';
|
||||
const SVG_OPTIONS = 'gf_svg_options';
|
||||
|
||||
private $plugin_dir;
|
||||
|
||||
public function __construct( $plugin_dir ) {
|
||||
$this->plugin_dir = $plugin_dir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register services to the container.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @param GF_Service_Container $container
|
||||
*/
|
||||
public function register( GF_Service_Container $container ) {
|
||||
require_once( plugin_dir_path( __FILE__ ) . '/class-gf-asset-processor.php' );
|
||||
require_once( plugin_dir_path( __FILE__ ) . '/class-gf-dependencies.php' );
|
||||
require_once( plugin_dir_path( __FILE__ ) . '/admin-dependencies/class-gf-admin-script-dependencies.php' );
|
||||
require_once( plugin_dir_path( __FILE__ ) . '/admin-dependencies/class-gf-admin-style-dependencies.php' );
|
||||
require_once( plugin_dir_path( __FILE__ ) . '/theme-dependencies/class-gf-theme-script-dependencies.php' );
|
||||
|
||||
$container->add( self::HASH_MAP, function () {
|
||||
if ( ! file_exists( \GFCommon::get_base_path() . '/assets/js/dist/assets.php' ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$map = require( \GFCommon::get_base_path() . '/assets/js/dist/assets.php' );
|
||||
|
||||
return rgar( $map, 'hash_map', array() );
|
||||
} );
|
||||
|
||||
$container->add( self::ASSET_PROCESSOR, function () use ( $container ) {
|
||||
$basepath = \GFCommon::get_base_path();
|
||||
$asset_path = sprintf( '%s/assets/js/dist/', $basepath );
|
||||
|
||||
return new GF_Asset_Processor( $container->get( self::HASH_MAP ), $asset_path );
|
||||
} );
|
||||
|
||||
$container->add( self::STYLE_DEPS, function () {
|
||||
return new GF_Admin_Style_Dependencies();
|
||||
} );
|
||||
|
||||
$container->add( self::SCRIPT_DEPS, function () {
|
||||
return new GF_Admin_Script_Dependencies();
|
||||
} );
|
||||
|
||||
$container->add( self::SCRIPT_DEPS_THEME, function () {
|
||||
return new GF_Theme_Script_Dependencies();
|
||||
} );
|
||||
|
||||
$this->svg_delivery( $container );
|
||||
}
|
||||
|
||||
public function init( GF_Service_Container $container ) {
|
||||
add_action( 'init', function () use ( $container ) {
|
||||
$container->get( self::ASSET_PROCESSOR )->process_assets();
|
||||
}, 9999 );
|
||||
|
||||
add_action( 'admin_enqueue_scripts', function () use ( $container ) {
|
||||
$container->get( self::STYLE_DEPS )->enqueue();
|
||||
$container->get( self::SCRIPT_DEPS )->enqueue();
|
||||
|
||||
// Styles and scripts required for the tooltips.
|
||||
wp_enqueue_style( 'gform_font_awesome' );
|
||||
wp_enqueue_script( 'gform_tooltip_init' );
|
||||
} );
|
||||
|
||||
add_action( 'gform_enqueue_scripts', function () use ( $container ) {
|
||||
$container->get( self::SCRIPT_DEPS_THEME )->enqueue();
|
||||
} );
|
||||
|
||||
add_filter( 'gform_noconflict_styles', function ( $styles ) use ( $container ) {
|
||||
return array_merge( $styles, $container->get( self::STYLE_DEPS )->get_items() );
|
||||
}, 1 );
|
||||
}
|
||||
|
||||
private function svg_delivery( GF_Service_Container $container ) {
|
||||
$default_path = sprintf( '%s/assets/img/base', untrailingslashit( $this->plugin_dir ) );
|
||||
|
||||
/**
|
||||
* Allows users to filter the path used to glob the available SVGs to use for display in themes.
|
||||
*
|
||||
* @since 2.7
|
||||
*
|
||||
* @param string $path The default orbital theme svg path within our plugin.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
$svg_path = apply_filters( 'gform_svg_theme_path', $default_path );
|
||||
|
||||
$svgs = array();
|
||||
|
||||
foreach ( \GFCommon::glob( '*.svg', trailingslashit( $svg_path ) ) as $filename ) {
|
||||
$key = pathinfo( $filename, PATHINFO_FILENAME );
|
||||
$svgs[ $key ] = file_get_contents( $filename );
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows users to filter the SVG options available to output in themes.
|
||||
*
|
||||
* @since 2.7
|
||||
*
|
||||
* @param array $svgs The current SVG options.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
$svgs = apply_filters( 'gform_svg_theme_options', $svgs );
|
||||
|
||||
$container->add( self::SVG_OPTIONS, $svgs );
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
/**
|
||||
* Gravity Forms Abstract Asset
|
||||
*
|
||||
* Provides base functionality for enqueueable/printable assets.
|
||||
*
|
||||
* @since 2.5
|
||||
* @package gravityforms
|
||||
*/
|
||||
|
||||
if ( ! class_exists( 'GFForms' ) ) {
|
||||
die();
|
||||
}
|
||||
|
||||
/**
|
||||
* Class GF_Asset
|
||||
*/
|
||||
abstract class GF_Asset {
|
||||
|
||||
/**
|
||||
* @var string $handle
|
||||
*/
|
||||
protected $handle;
|
||||
|
||||
/**
|
||||
* @var string $url
|
||||
*/
|
||||
protected $url;
|
||||
|
||||
/**
|
||||
* GF_Asset constructor.
|
||||
*
|
||||
* @param string $handle
|
||||
* @param string $url
|
||||
*/
|
||||
public function __construct( $handle, $url = '' ) {
|
||||
$this->handle = $handle;
|
||||
$this->url = $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle enqueueing the asset this class represents (e.g., using wp_enqueue_script() or wp_enqueue_style())
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
abstract public function enqueue_asset();
|
||||
|
||||
/**
|
||||
* Handle printing the asset this class represents (e.g., using wp_print_scripts() or wp_print_styles())
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
abstract public function print_asset();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace Gravity_Forms\Gravity_Forms\Assets;
|
||||
|
||||
abstract class GF_Dependencies {
|
||||
|
||||
/**
|
||||
* Items to enqueue
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $items = array();
|
||||
|
||||
/**
|
||||
* The method for actually enqueueing the items.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @param $handle
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
abstract protected function do_enqueue( $handle );
|
||||
|
||||
/**
|
||||
* Get the dependency items.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_items() {
|
||||
return $this->items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue the items.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @param $items
|
||||
*/
|
||||
public function enqueue() {
|
||||
if ( ! $this->should_enqueue() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ( $this->items as $handle ) {
|
||||
$this->do_enqueue( $handle );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Override to determine whether the assets outlined should be enqueued.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function should_enqueue() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
/**
|
||||
* Gravity Forms Script Asset
|
||||
*
|
||||
* @since 2.5
|
||||
* @package gravityforms
|
||||
*/
|
||||
|
||||
if ( ! class_exists( 'GFForms' ) ) {
|
||||
die();
|
||||
}
|
||||
|
||||
/**
|
||||
* Class GF_Script_Asset
|
||||
*/
|
||||
class GF_Script_Asset extends GF_Asset {
|
||||
|
||||
/**
|
||||
* The data for any localized information.
|
||||
*
|
||||
* @since 2.5
|
||||
*
|
||||
* @var array $localize_data
|
||||
*/
|
||||
private $localize_data = array();
|
||||
|
||||
/**
|
||||
* Localize data to this script.
|
||||
*
|
||||
* @since 2.5
|
||||
*
|
||||
* @param string $object_name
|
||||
* @param array $data
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function add_localize_data( $object_name, $data ) {
|
||||
$this->localize_data[ $object_name ] = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue the asset.
|
||||
*
|
||||
* @since 2.5
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function enqueue_asset() {
|
||||
wp_enqueue_script( $this->handle, $this->url );
|
||||
|
||||
if ( empty( $this->localize_data ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ( $this->localize_data as $object_name => $data ) {
|
||||
wp_localize_script( $this->handle, $object_name, $data );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Print the asset.
|
||||
*
|
||||
* @since 2.5
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function print_asset() {
|
||||
$this->enqueue_asset();
|
||||
|
||||
wp_print_scripts( $this->handle );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
/**
|
||||
* Gravity Forms Style Asset
|
||||
*
|
||||
* @since 2.5
|
||||
* @package gravityforms
|
||||
*/
|
||||
|
||||
if ( ! class_exists( 'GFForms' ) ) {
|
||||
die();
|
||||
}
|
||||
|
||||
/**
|
||||
* Class GF_Style_Asset
|
||||
*/
|
||||
class GF_Style_Asset extends GF_Asset {
|
||||
|
||||
/**
|
||||
* Enqueue the asset.
|
||||
*
|
||||
* @since 2.5
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function enqueue_asset() {
|
||||
wp_enqueue_style( $this->handle, $this->url );
|
||||
}
|
||||
|
||||
/**
|
||||
* Print the asset.
|
||||
*
|
||||
* @since 2.5
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function print_asset() {
|
||||
$this->enqueue_asset();
|
||||
wp_print_styles( $this->handle );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
//Nothing to see here
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace Gravity_Forms\Gravity_Forms\Assets\Theme_Dependencies;
|
||||
|
||||
use Gravity_Forms\Gravity_Forms\Assets\GF_Dependencies;
|
||||
use Gravity_Forms\Gravity_Forms\GF_Service_Container;
|
||||
use Gravity_Forms\Gravity_Forms\GF_Service_Provider;
|
||||
|
||||
/**
|
||||
* Class GF_Theme_Script_Dependencies
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @package Gravity_Forms\Gravity_Forms\Assets\Theme_Dependencies;
|
||||
*/
|
||||
class GF_Theme_Script_Dependencies extends GF_Dependencies {
|
||||
|
||||
/**
|
||||
* Items to enqueue globally in admin.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $items = array(
|
||||
'gform_gravityforms_theme',
|
||||
);
|
||||
|
||||
/**
|
||||
* Enqueue the item by handle.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @param $handle
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function do_enqueue( $handle ) {
|
||||
wp_enqueue_script( $handle );
|
||||
}
|
||||
|
||||
}
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
/**
|
||||
* Service provider for async (background) processors.
|
||||
*
|
||||
* @package Gravity_Forms\Gravity_Forms
|
||||
*/
|
||||
|
||||
namespace Gravity_Forms\Gravity_Forms\Async;
|
||||
|
||||
use Gravity_Forms\Gravity_Forms\GF_Service_Container;
|
||||
use Gravity_Forms\Gravity_Forms\GF_Service_Provider;
|
||||
use Gravity_Forms\Gravity_Forms\Telemetry\GF_Telemetry_Processor;
|
||||
use Gravity_Forms\Gravity_Forms\Bulk_Actions\GF_Entry_Bulk_Action_Processor;
|
||||
use Gravity_Forms\Gravity_Forms\Bulk_Actions\Endpoints\GF_Bulk_Action_Endpoint_Start;
|
||||
use Gravity_Forms\Gravity_Forms\Bulk_Actions\Endpoints\GF_Bulk_Action_Endpoint_Status;
|
||||
use Gravity_Forms\Gravity_Forms\Bulk_Actions\Endpoints\GF_Bulk_Action_Endpoint_Cancel;
|
||||
use GFForms;
|
||||
use GF_Background_Upgrader;
|
||||
use GF_Feed_Processor;
|
||||
|
||||
if ( ! class_exists( 'GFForms' ) ) {
|
||||
die();
|
||||
}
|
||||
|
||||
/**
|
||||
* Class GF_Background_Processing_Service_Provider
|
||||
*
|
||||
* @since 2.6.9
|
||||
*/
|
||||
class GF_Background_Process_Service_Provider extends GF_Service_Provider {
|
||||
|
||||
const UPGRADER = 'upgrade_processor';
|
||||
const FEEDS = 'feeds_processor';
|
||||
const NOTIFICATIONS = 'notifications_processor';
|
||||
const TELEMETRY = 'telemetry_processor';
|
||||
const BULK_ACTION = 'bulk_action_processor';
|
||||
const BULK_ACTION_ENDPOINT_START = 'bulk_action_endpoint_start';
|
||||
const BULK_ACTION_ENDPOINT_STATUS = 'bulk_action_endpoint_status';
|
||||
const BULK_ACTION_ENDPOINT_CANCEL = 'bulk_action_endpoint_cancel';
|
||||
|
||||
/**
|
||||
* The names and classes of the async (background) processors.
|
||||
*
|
||||
* @since 2.6.9
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $processors = array(
|
||||
self::UPGRADER => GF_Background_Upgrader::class,
|
||||
self::FEEDS => GF_Feed_Processor::class,
|
||||
self::NOTIFICATIONS => GF_Notifications_Processor::class,
|
||||
self::TELEMETRY => GF_Telemetry_Processor::class,
|
||||
self::BULK_ACTION => GF_Entry_Bulk_Action_Processor::class,
|
||||
);
|
||||
|
||||
/**
|
||||
* Initializing the processors and adding them to the container as services.
|
||||
*
|
||||
* @since 2.6.9
|
||||
*
|
||||
* @param GF_Service_Container $container
|
||||
*/
|
||||
public function register( GF_Service_Container $container ) {
|
||||
GFForms::init_background_upgrader();
|
||||
require_once GF_PLUGIN_DIR_PATH . 'includes/addon/class-gf-feed-processor.php';
|
||||
require_once GF_PLUGIN_DIR_PATH . 'includes/async/class-gf-notifications-processor.php';
|
||||
require_once GF_PLUGIN_DIR_PATH . 'includes/telemetry/class-gf-telemetry-processor.php';
|
||||
require_once GF_PLUGIN_DIR_PATH . 'includes/bulk-actions/class-gf-entry-bulk-action-processor.php';
|
||||
require_once GF_PLUGIN_DIR_PATH . 'includes/bulk-actions/endpoints/class-gf-bulk-action-endpoint-start.php';
|
||||
require_once GF_PLUGIN_DIR_PATH . 'includes/bulk-actions/endpoints/class-gf-bulk-action-endpoint-status.php';
|
||||
require_once GF_PLUGIN_DIR_PATH . 'includes/bulk-actions/endpoints/class-gf-bulk-action-endpoint-cancel.php';
|
||||
|
||||
foreach ( $this->processors as $name => $class ) {
|
||||
$container->add( $name, function () use ( $name, $class ) {
|
||||
if ( $name === self::UPGRADER ) {
|
||||
return GFForms::$background_upgrader;
|
||||
}
|
||||
|
||||
$callback = array( $class, 'get_instance' );
|
||||
if ( is_callable( $callback ) ) {
|
||||
return call_user_func( $callback );
|
||||
}
|
||||
|
||||
return new $class();
|
||||
} );
|
||||
}
|
||||
|
||||
$container->add( self::BULK_ACTION_ENDPOINT_START, function() {
|
||||
return new GF_Bulk_Action_Endpoint_Start();
|
||||
} );
|
||||
|
||||
$container->add( self::BULK_ACTION_ENDPOINT_STATUS, function() {
|
||||
return new GF_Bulk_Action_Endpoint_Status();
|
||||
} );
|
||||
|
||||
$container->add( self::BULK_ACTION_ENDPOINT_CANCEL, function() {
|
||||
return new GF_Bulk_Action_Endpoint_Cancel();
|
||||
} );
|
||||
}
|
||||
|
||||
public function init( GF_Service_Container $container ) {
|
||||
add_action( 'wp_ajax_' . GF_Bulk_Action_Endpoint_Start::ACTION_NAME, function() use ( $container ) {
|
||||
$container->get( self::BULK_ACTION_ENDPOINT_START )->handle();
|
||||
} );
|
||||
|
||||
add_action( 'wp_ajax_' . GF_Bulk_Action_Endpoint_Status::ACTION_NAME, function() use ( $container ) {
|
||||
$container->get( self::BULK_ACTION_ENDPOINT_STATUS )->handle();
|
||||
} );
|
||||
|
||||
add_action( 'wp_ajax_' . GF_Bulk_Action_Endpoint_Cancel::ACTION_NAME, function() use ( $container ) {
|
||||
$container->get( self::BULK_ACTION_ENDPOINT_CANCEL )->handle();
|
||||
} );
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,160 @@
|
||||
<?php
|
||||
|
||||
namespace Gravity_Forms\Gravity_Forms\Async;
|
||||
|
||||
use GFCommon;
|
||||
use GFAPI;
|
||||
|
||||
if ( ! class_exists( 'GFForms' ) ) {
|
||||
die();
|
||||
}
|
||||
|
||||
if ( ! class_exists( 'Gravity_Forms\Gravity_Forms\Async\GF_Background_Process' ) ) {
|
||||
require_once GF_PLUGIN_DIR_PATH . 'includes/async/class-gf-background-process.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* GF_Notifications_Processor Class.
|
||||
*
|
||||
* @since 2.6.9
|
||||
*/
|
||||
class GF_Notifications_Processor extends GF_Background_Process {
|
||||
|
||||
/**
|
||||
* The action name.
|
||||
*
|
||||
* @since 2.6.9
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $action = 'gf_notifications_processor';
|
||||
|
||||
/**
|
||||
* Indicates if the task uses an array that supports the attempts key.
|
||||
*
|
||||
* @since 2.9.9
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $supports_attempts = true;
|
||||
|
||||
/**
|
||||
* Processes the task.
|
||||
*
|
||||
* @since 2.6.9
|
||||
*
|
||||
* @param array $item The task arguments.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function task( $item ) {
|
||||
$key = isset( $item['notification'] ) ? 'notification' : 'notifications';
|
||||
$notification_or_ids = rgar( $item, $key );
|
||||
if ( empty( $notification_or_ids ) || ! is_array( $notification_or_ids ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! empty( $item['entry'] ) ) {
|
||||
$entry = $item['entry'];
|
||||
} else {
|
||||
$entry = GFAPI::get_entry( rgar( $item, 'entry_id' ) );
|
||||
if ( is_wp_error( $entry ) ) {
|
||||
GFCommon::log_debug( __METHOD__ . sprintf( '(): Aborting; Entry #%d not found.', rgar( $item, 'entry_id' ) ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$form = GFAPI::get_form( rgar( $item, 'form_id' ) );
|
||||
if ( empty( $form ) ) {
|
||||
GFCommon::log_debug( __METHOD__ . sprintf( '(): Aborting; Form #%d not found.', rgar( $item, 'form_id' ) ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$form = $this->filter_form( $form, $entry );
|
||||
$event = rgar( $item, 'event', 'form_submission' );
|
||||
$data = rgar( $item, 'data' );
|
||||
if ( ! is_array( $data ) ) {
|
||||
$data = array();
|
||||
}
|
||||
|
||||
if ( $key === 'notification' ) {
|
||||
$notifications = array( rgar( $notification_or_ids, 'id', 'custom' ) );
|
||||
} else {
|
||||
$notifications = $notification_or_ids;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows custom actions to be performed before notifications are sent asynchronously.
|
||||
*
|
||||
* @since 2.7.1
|
||||
*
|
||||
* @param string $event The event being processed.
|
||||
* @param array $notifications An array containing the IDs of the notifications being processed.
|
||||
* @param array $form The form being processed.
|
||||
* @param array $entry The entry being processed.
|
||||
* @param array $data An array of data which can be used in the notifications via the generic {object:property} merge tag. Defaults to empty array.
|
||||
*/
|
||||
do_action( 'gform_pre_process_async_notifications', $event, $notifications, $form, $entry, $data );
|
||||
|
||||
if ( $key === 'notification' ) {
|
||||
GFCommon::send_notification( $notification_or_ids, $form, $entry, $data );
|
||||
} else {
|
||||
GFCommon::send_notifications( $notifications, $form, $entry, true, $event, $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows custom actions to be performed after notifications are sent asynchronously.
|
||||
*
|
||||
* @since 2.7.1
|
||||
*
|
||||
* @param string $event The event being processed.
|
||||
* @param array $notifications An array containing the IDs of the notifications being processed.
|
||||
* @param array $form The form being processed.
|
||||
* @param array $entry The entry being processed.
|
||||
* @param array $data An array of data which can be used in the notifications via the generic {object:property} merge tag. Defaults to empty array.
|
||||
*/
|
||||
do_action( 'gform_post_process_async_notifications', $event, $notifications, $form, $entry, $data );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if async (background) processing of notifications is enabled.
|
||||
*
|
||||
* @since 2.7.1
|
||||
* @since 2.10.0 Background notifications enabled by default for new installs.
|
||||
*
|
||||
* @param array $notifications An array containing the IDs of the notifications to be sent.
|
||||
* @param array $form The form being processed.
|
||||
* @param array $entry The entry being processed.
|
||||
* @param string $event The event being processed.
|
||||
* @param array $data An array of data which can be used in the notifications via the generic {object:property} merge tag. Defaults to empty array.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_enabled( $notifications, $form, $entry, $event = 'form_submission', $data = array() ) {
|
||||
$form_id = absint( rgar( $form, 'id' ) );
|
||||
$is_enabled = (bool) get_option( 'gform_enable_async_notifications' );
|
||||
|
||||
/**
|
||||
* Allows async (background) processing of notifications to be enabled or disabled.
|
||||
*
|
||||
* @since 2.6.9
|
||||
* @since 2.10.0 Updated to use the gform_enable_async_notifications option value as the default.
|
||||
*
|
||||
* @param bool $is_enabled Is async (background) processing of notifications enabled? Defaults to the value of the gform_enable_async_notifications option.
|
||||
* @param string $event The event the notifications are to be sent for.
|
||||
* @param array $notifications An array containing the IDs of the notifications to be sent.
|
||||
* @param array $form The form currently being processed.
|
||||
* @param array $entry The entry currently being processed.
|
||||
* @param array $data An array of data which can be used in the notifications via the generic {object:property} merge tag. Defaults to empty array.
|
||||
*/
|
||||
return gf_apply_filters( array(
|
||||
'gform_is_asynchronous_notifications_enabled',
|
||||
$form_id,
|
||||
), $is_enabled, $event, $notifications, $form, $entry, $data );
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
<?php
|
||||
/**
|
||||
* WP Async Request
|
||||
*
|
||||
* @package WP-Background-Processing
|
||||
*/
|
||||
|
||||
namespace Gravity_Forms\Gravity_Forms\Async;
|
||||
|
||||
/**
|
||||
* Abstract WP_Async_Request class.
|
||||
*
|
||||
* @since 2.2
|
||||
* @since 2.9.8 Namespaced.
|
||||
*
|
||||
* @abstract
|
||||
*/
|
||||
abstract class WP_Async_Request {
|
||||
|
||||
/**
|
||||
* Prefix
|
||||
*
|
||||
* (default value: 'wp')
|
||||
*
|
||||
* @var string
|
||||
* @access protected
|
||||
*/
|
||||
protected $prefix = 'wp';
|
||||
|
||||
/**
|
||||
* Action
|
||||
*
|
||||
* (default value: 'async_request')
|
||||
*
|
||||
* @var string
|
||||
* @access protected
|
||||
*/
|
||||
protected $action = 'async_request';
|
||||
|
||||
/**
|
||||
* Identifier
|
||||
*
|
||||
* @var mixed
|
||||
* @access protected
|
||||
*/
|
||||
protected $identifier;
|
||||
|
||||
/**
|
||||
* Data
|
||||
*
|
||||
* (default value: array())
|
||||
*
|
||||
* @var array
|
||||
* @access protected
|
||||
*/
|
||||
protected $data = array();
|
||||
|
||||
/**
|
||||
* Initiate new async request
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->identifier = $this->prefix . '_' . $this->action;
|
||||
|
||||
add_action( 'wp_ajax_' . $this->identifier, array( $this, 'maybe_handle' ) ); // @phpstan-ignore-line
|
||||
add_action( 'wp_ajax_nopriv_' . $this->identifier, array( $this, 'maybe_handle' ) ); // @phpstan-ignore-line
|
||||
}
|
||||
|
||||
/**
|
||||
* Set data used during the request
|
||||
*
|
||||
* @param array $data Data.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function data( $data ) {
|
||||
$this->data = $data;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch the async request
|
||||
*
|
||||
* @return array|\WP_Error
|
||||
*/
|
||||
public function dispatch() {
|
||||
$url = add_query_arg( $this->get_query_args(), $this->get_query_url() );
|
||||
$args = $this->get_post_args();
|
||||
|
||||
return wp_remote_post( esc_url_raw( $url ), $args );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get query args
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_query_args() {
|
||||
if ( property_exists( $this, 'query_args' ) ) {
|
||||
return $this->query_args;
|
||||
}
|
||||
|
||||
$args = array(
|
||||
'action' => $this->identifier,
|
||||
'nonce' => wp_create_nonce( $this->identifier ),
|
||||
);
|
||||
|
||||
/**
|
||||
* Filters the query arguments used during an async request.
|
||||
*
|
||||
* @since 2.9.7
|
||||
*
|
||||
* @param array $args
|
||||
*/
|
||||
return apply_filters( $this->identifier . '_query_args', $args );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get query URL
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_query_url() {
|
||||
if ( property_exists( $this, 'query_url' ) ) {
|
||||
return $this->query_url;
|
||||
}
|
||||
|
||||
$url = admin_url( 'admin-ajax.php' );
|
||||
|
||||
/**
|
||||
* Filters the query URL used during an async request.
|
||||
*
|
||||
* @since 2.9.7
|
||||
*
|
||||
* @param string $url
|
||||
*/
|
||||
return apply_filters( $this->identifier . '_query_url', $url );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get post args
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_post_args() {
|
||||
if ( property_exists( $this, 'post_args' ) ) {
|
||||
return $this->post_args;
|
||||
}
|
||||
|
||||
$args = array(
|
||||
'timeout' => 5,
|
||||
'blocking' => false,
|
||||
'body' => $this->data,
|
||||
'cookies' => $_COOKIE, // Passing cookies ensures request is performed as initiating user.
|
||||
'sslverify' => apply_filters( 'https_local_ssl_verify', false ), // Local requests, fine to pass false.
|
||||
);
|
||||
|
||||
/**
|
||||
* Filters the post arguments used during an async request.
|
||||
*
|
||||
* @since 2.9.7
|
||||
*
|
||||
* @param array $args
|
||||
*/
|
||||
return apply_filters( $this->identifier . '_post_args', $args );
|
||||
}
|
||||
|
||||
/**
|
||||
* Maybe handle
|
||||
*
|
||||
* Check for correct nonce and pass to handler.
|
||||
*
|
||||
* @return void|mixed
|
||||
*/
|
||||
public function maybe_handle() {
|
||||
// Don't lock up other requests while processing
|
||||
session_write_close();
|
||||
|
||||
check_ajax_referer( $this->identifier, 'nonce' );
|
||||
|
||||
$this->handle();
|
||||
|
||||
return $this->maybe_wp_die();
|
||||
}
|
||||
|
||||
/**
|
||||
* Should the process exit with wp_die?
|
||||
*
|
||||
* @since 2.9.7
|
||||
*
|
||||
* @param mixed $return What to return if filter says don't die, default is null.
|
||||
*
|
||||
* @return void|mixed
|
||||
*/
|
||||
protected function maybe_wp_die( $return = null ) {
|
||||
/**
|
||||
* Should wp_die be used?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
if ( apply_filters( $this->identifier . '_wp_die', true ) ) {
|
||||
wp_die();
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle
|
||||
*
|
||||
* Override this method to perform any actions required
|
||||
* during the async request.
|
||||
*/
|
||||
abstract protected function handle();
|
||||
|
||||
}
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
<?php
|
||||
|
||||
namespace Gravity_Forms\Gravity_Forms\Author_Select;
|
||||
|
||||
use Gravity_Forms\Gravity_Forms\Config\GF_Config;
|
||||
use Gravity_Forms\Gravity_Forms\Config\GF_Config_Data_Parser;
|
||||
|
||||
defined( 'ABSPATH' ) || die();
|
||||
|
||||
/**
|
||||
* Config items for the Author Select dropdown
|
||||
*
|
||||
* @since 2.9.20
|
||||
*/
|
||||
class GF_Author_Select_Config extends GF_Config {
|
||||
|
||||
protected $name = 'gform_admin_config';
|
||||
protected $script_to_localize = 'gform_gravityforms_admin_vendors';
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @since 2.9.20
|
||||
*
|
||||
* @param GF_Config_Data_Parser $data_parser
|
||||
*/
|
||||
public function __construct( GF_Config_Data_Parser $data_parser ) {
|
||||
parent::__construct( $data_parser );
|
||||
}
|
||||
|
||||
/**
|
||||
* Only enqueue in the form editor
|
||||
*
|
||||
* @since 2.9.20
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function should_enqueue() {
|
||||
return \GFForms::get_page() === 'form_editor';
|
||||
}
|
||||
|
||||
/**
|
||||
* Config data for the author select dropdown
|
||||
*
|
||||
* @since 2.9.20
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function data() {
|
||||
$form_id = isset( $_GET['id'] ) ? intval( $_GET['id'] ) : 0; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
|
||||
$form = \GFFormsModel::get_form_meta( $form_id );
|
||||
$current_author = rgar( $form, 'postAuthor' );
|
||||
$selected_label = '';
|
||||
|
||||
if ( $current_author ) {
|
||||
$current_user = get_user_by( 'id', $current_author );
|
||||
if ( $current_user ) {
|
||||
$selected_label = esc_html( $current_user->display_name );
|
||||
}
|
||||
}
|
||||
|
||||
return array(
|
||||
'components' => array(
|
||||
'author_select' => array(
|
||||
'endpoints' => array(
|
||||
'get' => array(
|
||||
'action' => 'gf_get_users',
|
||||
'nonce' => wp_create_nonce( 'gf_get_users' ),
|
||||
),
|
||||
),
|
||||
'data' => $this->get_initial_users( $form_id ),
|
||||
'form_id' => $form_id,
|
||||
'selected_value' => $current_author,
|
||||
'selected_label' => $selected_label,
|
||||
'strings' => array(
|
||||
'search_placeholder' => esc_html__( 'Search users...', 'gravityforms' ),
|
||||
'trigger_placeholder' => esc_html__( 'Select an author', 'gravityforms' ),
|
||||
'trigger_aria_text' => esc_html__( 'Default Post Author', 'gravityforms' ),
|
||||
'search_aria_text' => esc_html__( 'Search for users', 'gravityforms' ),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the initial list of users for the dropdown
|
||||
*
|
||||
* @since 2.9.20
|
||||
*
|
||||
* @param int $form_id The form ID
|
||||
* @return array
|
||||
*/
|
||||
private function get_initial_users( $form_id ) {
|
||||
$args = array(
|
||||
'number' => 10,
|
||||
'fields' => array( 'ID', 'display_name' ),
|
||||
);
|
||||
|
||||
// Apply existing filter for backward compatibility
|
||||
$args = gf_apply_filters( array( 'gform_author_dropdown_args', $form_id ), $args );
|
||||
|
||||
$users = get_users( $args );
|
||||
|
||||
if ( ! is_array( $users ) || empty( $users ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$initial_users = array();
|
||||
foreach ( $users as $user ) {
|
||||
$initial_users[] = array(
|
||||
'value' => $user->ID,
|
||||
'label' => esc_html( $user->display_name ),
|
||||
);
|
||||
}
|
||||
|
||||
return $initial_users;
|
||||
}
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
namespace Gravity_Forms\Gravity_Forms\Author_Select;
|
||||
|
||||
use Gravity_Forms\Gravity_Forms\Config\GF_Config_Service_Provider;
|
||||
use Gravity_Forms\Gravity_Forms\GF_Service_Container;
|
||||
use Gravity_Forms\Gravity_Forms\GF_Service_Provider;
|
||||
|
||||
defined( 'ABSPATH' ) || die();
|
||||
|
||||
/**
|
||||
* Class GF_Author_Select_Service_Provider
|
||||
*
|
||||
* Service provider for the Author Select functionality
|
||||
*
|
||||
* @since 2.9.20
|
||||
*/
|
||||
class GF_Author_Select_Service_Provider extends GF_Service_Provider {
|
||||
|
||||
const AUTHOR_SELECT = 'author_select';
|
||||
const AUTHOR_SELECT_CONFIG = 'author_select_config';
|
||||
|
||||
/**
|
||||
* Register services to the container
|
||||
*
|
||||
* @since 2.9.20
|
||||
*
|
||||
* @param GF_Service_Container $container
|
||||
*/
|
||||
public function register( GF_Service_Container $container ) {
|
||||
require_once( plugin_dir_path( __FILE__ ) . 'class-gf-author-select.php' );
|
||||
require_once( plugin_dir_path( __FILE__ ) . 'class-gf-author-select-config.php' );
|
||||
|
||||
$container->add( self::AUTHOR_SELECT, function() {
|
||||
return new GF_Author_Select();
|
||||
} );
|
||||
|
||||
$container->add( self::AUTHOR_SELECT_CONFIG, function() use ( $container ) {
|
||||
$data_parser = $container->get( GF_Config_Service_Provider::DATA_PARSER );
|
||||
return new GF_Author_Select_Config( $data_parser );
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize any actions or hooks
|
||||
*
|
||||
* @since 2.9.20
|
||||
*
|
||||
* @param GF_Service_Container $container
|
||||
*/
|
||||
public function init( GF_Service_Container $container ) {
|
||||
$container->get( self::AUTHOR_SELECT )->init();
|
||||
|
||||
if ( \GFForms::get_page() === 'form_editor' ) {
|
||||
$config_collection = $container->get( GF_Config_Service_Provider::CONFIG_COLLECTION );
|
||||
$config_collection->add_config( $container->get( self::AUTHOR_SELECT_CONFIG ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
namespace Gravity_Forms\Gravity_Forms\Author_Select;
|
||||
|
||||
defined( 'ABSPATH' ) || die();
|
||||
|
||||
use GFCommon;
|
||||
|
||||
/**
|
||||
* Class GF_Author_Select
|
||||
*
|
||||
* Handles AJAX requests for the author select dropdown in the form editor
|
||||
*
|
||||
* @since 2.9.20
|
||||
*/
|
||||
class GF_Author_Select {
|
||||
|
||||
/**
|
||||
* Initialize the author select functionality
|
||||
*
|
||||
* @since 2.9.20
|
||||
*/
|
||||
public function init() {
|
||||
add_action( 'wp_ajax_gf_get_users', array( $this, 'ajax_get_users' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle AJAX request to get users
|
||||
*
|
||||
* @since 2.9.20
|
||||
*/
|
||||
public function ajax_get_users() {
|
||||
if ( ! GFCommon::current_user_can_any( 'gravityforms_edit_forms' ) ) {
|
||||
wp_send_json_error( array( 'message' => __( 'Access denied', 'gravityforms' ) ) );
|
||||
}
|
||||
|
||||
check_ajax_referer( 'gf_get_users', 'nonce' );
|
||||
|
||||
$search = isset( $_POST['search'] ) ? sanitize_text_field( wp_unslash( $_POST['search'] ) ) : '';
|
||||
$form_id = isset( $_POST['form_id'] ) ? intval( $_POST['form_id'] ) : 0;
|
||||
|
||||
$args = array(
|
||||
'number' => 10,
|
||||
'fields' => array( 'ID', 'display_name' ),
|
||||
);
|
||||
|
||||
/**
|
||||
* Filter the arguments used to query users for the author dropdown.
|
||||
* Originally added to limit users for performance.
|
||||
* Now with AJAX and 10-result limit by default, useful for role restrictions or custom filtering.
|
||||
*
|
||||
* @since 1.3.10
|
||||
*
|
||||
* @param array $args WP_User_Query arguments for get_users()
|
||||
*/
|
||||
$args = gf_apply_filters( array( 'gform_author_dropdown_args', $form_id ), $args );
|
||||
|
||||
if ( ! empty( $search ) ) {
|
||||
$args['search'] = '*' . $search . '*';
|
||||
$args['search_columns'] = array( 'user_login', 'user_email', 'display_name' );
|
||||
}
|
||||
|
||||
$users = get_users( $args );
|
||||
|
||||
$response = array();
|
||||
foreach ( $users as $user ) {
|
||||
$response[] = array(
|
||||
'value' => $user->ID,
|
||||
'label' => esc_html( $user->display_name ),
|
||||
);
|
||||
}
|
||||
|
||||
wp_send_json_success( $response );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace Gravity_Forms\Gravity_Forms\Blocks;
|
||||
|
||||
/**
|
||||
* Gravity Forms Block Attributes class.
|
||||
*
|
||||
* @since 2.7.4
|
||||
*
|
||||
* Class GF_Block_Attributes
|
||||
*/
|
||||
class GF_Block_Attributes {
|
||||
|
||||
public function store( $attributes ) {
|
||||
add_filter( 'gform_form_block_attribute_values', function( $attr ) use ( $attributes ) {
|
||||
$form_id = rgar( $attributes, 'formId', 0 );
|
||||
|
||||
if ( ! array_key_exists( $form_id, $attr ) ) {
|
||||
$attr[ $form_id ] = array();
|
||||
}
|
||||
|
||||
$attr[ $form_id ][] = $attributes;
|
||||
return $attr;
|
||||
} );
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,320 @@
|
||||
<?php
|
||||
|
||||
// If Gravity Forms Block Manager is not available, do not run.
|
||||
if ( ! class_exists( 'GF_Blocks' ) || ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
class GF_Block_Form extends GF_Block {
|
||||
|
||||
/**
|
||||
* Contains an instance of this block, if available.
|
||||
*
|
||||
* @since 2.4.10
|
||||
* @var GF_Block $_instance If available, contains an instance of this block.
|
||||
*/
|
||||
private static $_instance = null;
|
||||
|
||||
/**
|
||||
* Block type.
|
||||
*
|
||||
* @since 2.4.10
|
||||
* @var string
|
||||
*/
|
||||
public $type = 'gravityforms/form';
|
||||
|
||||
/**
|
||||
* Handle of primary block script.
|
||||
*
|
||||
* @since 2.4.10
|
||||
* @var string
|
||||
*/
|
||||
public $script_handle = 'gform_editor_block_form';
|
||||
|
||||
/**
|
||||
* Handle of primary block style.
|
||||
*
|
||||
* @since 2.5.6
|
||||
* @var string
|
||||
*/
|
||||
public $style_handle = 'gform_editor_block_form';
|
||||
|
||||
public function __construct() {
|
||||
$this->assign_attributes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register block type and add filters specific to the form block.
|
||||
*
|
||||
* @since 2.10.5
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init() {
|
||||
parent::init();
|
||||
|
||||
// Move WordPress's custom CSS classes from the <script> to the <div class="gform_wrapper">, so that the form is styled correctly.
|
||||
add_filter( 'render_block_gravityforms/form', array( $this, 'fix_custom_css_class_placement' ), 10, 2 );
|
||||
}
|
||||
|
||||
private function assign_attributes() {
|
||||
$default_attributes = GFForms::get_service_container()->get( \Gravity_Forms\Gravity_Forms\Blocks\GF_Blocks_Service_Provider::FORM_BLOCK_ATTRIBUTES );
|
||||
$attributes = apply_filters( 'gform_form_block_attributes', $default_attributes );
|
||||
|
||||
array_walk( $attributes, function ( &$value ) {
|
||||
$value = array( 'type' => $value['type'] );
|
||||
} );
|
||||
|
||||
$this->attributes = $attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get instance of this class.
|
||||
*
|
||||
* @since 2.4.10
|
||||
*
|
||||
* @return GF_Block_Form
|
||||
*/
|
||||
public static function get_instance() {
|
||||
|
||||
if ( null === self::$_instance ) {
|
||||
self::$_instance = new self();
|
||||
}
|
||||
|
||||
return self::$_instance;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Move the WordPress "Custom CSS" block-support classes from a leading <script> to the gform_wrapper <div>.
|
||||
*
|
||||
* The block "Custom CSS" setting introduced in WP 7.0 adds custom CSS classes to the first HTML element in a block,
|
||||
* which might be the <script> tag added by GFForms::maybe_prepend_hooks_js_script() instead of the <div class="gform_wrapper">.
|
||||
* This filter corrects that.
|
||||
*
|
||||
* @since 2.10.5
|
||||
*
|
||||
* @param string $block_content The rendered block HTML.
|
||||
* @param array $block The parsed block data.
|
||||
*
|
||||
* @return string The (possibly corrected) block HTML.
|
||||
*/
|
||||
public function fix_custom_css_class_placement( $block_content, $block ) {
|
||||
if ( ! class_exists( 'WP_HTML_Tag_Processor' ) ) {
|
||||
return $block_content;
|
||||
}
|
||||
|
||||
$tags = new WP_HTML_Tag_Processor( $block_content );
|
||||
|
||||
// Advance to the first tag. If the content is empty or has no tags, bail.
|
||||
if ( ! $tags->next_tag() ) {
|
||||
return $block_content;
|
||||
}
|
||||
|
||||
// Only act when the first element is a <script> — i.e. the hooks JS was prepended.
|
||||
if ( 'SCRIPT' !== $tags->get_tag() ) {
|
||||
return $block_content;
|
||||
}
|
||||
|
||||
// Only act when WordPress has placed the custom-CSS marker classes on this <script>.
|
||||
$class_attr = $tags->get_attribute( 'class' );
|
||||
if ( ! $class_attr || false === strpos( $class_attr, 'has-custom-css' ) ) {
|
||||
return $block_content;
|
||||
}
|
||||
|
||||
// Extract the unique `wp-custom-css-*` class generated for this block instance.
|
||||
$custom_css_class = null;
|
||||
foreach ( preg_split( '/\s+/', $class_attr ) as $class ) {
|
||||
if ( 0 === strpos( $class, 'wp-custom-css-' ) ) {
|
||||
$custom_css_class = $class;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the custom-CSS classes from the <script> tag.
|
||||
$tags->remove_class( 'has-custom-css' );
|
||||
if ( $custom_css_class ) {
|
||||
$tags->remove_class( $custom_css_class );
|
||||
}
|
||||
|
||||
// Find the form wrapper <div> and add the classes to it instead.
|
||||
// next_tag() searches forward from the current cursor position.
|
||||
if ( $tags->next_tag( array( 'class_name' => 'gform_wrapper' ) ) ) {
|
||||
$tags->add_class( 'has-custom-css' );
|
||||
if ( $custom_css_class ) {
|
||||
$tags->add_class( $custom_css_class );
|
||||
}
|
||||
}
|
||||
|
||||
return $tags->get_updated_html();
|
||||
}
|
||||
|
||||
|
||||
// # SCRIPT / STYLES -----------------------------------------------------------------------------------------------
|
||||
public function register_block_assets() {
|
||||
parent::register_block_assets();
|
||||
if ( function_exists( 'wp_enqueue_block_style' ) && is_admin() ) {
|
||||
wp_enqueue_block_style( $this->type, array( 'handle' => 'gravity_forms_theme_reset' ) );
|
||||
wp_enqueue_block_style( $this->type, array( 'handle' => 'gravity_forms_theme_foundation' ) );
|
||||
wp_enqueue_block_style( $this->type, array( 'handle' => 'gravity_forms_theme_framework' ) );
|
||||
wp_enqueue_block_style( $this->type, array( 'handle' => 'gravity_forms_orbital_theme' ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Register scripts for block.
|
||||
*
|
||||
* @since 2.4.10
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function scripts() {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Localize Form block script.
|
||||
*
|
||||
* @since 2.4.10
|
||||
*
|
||||
* @param array $script Script arguments.
|
||||
*/
|
||||
public function localize_script( $script = array() ) {
|
||||
|
||||
wp_localize_script(
|
||||
$script['handle'],
|
||||
'gform_block_form',
|
||||
array(
|
||||
'adminURL' => admin_url( 'admin.php' ),
|
||||
'forms' => $this->get_forms(),
|
||||
'preview' => GFCommon::get_base_url() . '/images/gf_block_preview.svg',
|
||||
)
|
||||
);
|
||||
|
||||
if ( function_exists( 'wp_set_script_translations' ) ) {
|
||||
wp_set_script_translations( $script['handle'], 'gravityforms', GFCommon::get_base_path() . '/languages' );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Register styles for block.
|
||||
*
|
||||
* @since 2.4.10
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function styles() {
|
||||
|
||||
// Prepare styling dependencies.
|
||||
$deps = array( 'wp-edit-blocks' );
|
||||
|
||||
// Add Gravity Forms styling if CSS is enabled.
|
||||
if ( ! GFCommon::is_frontend_default_css_disabled() ) {
|
||||
$deps = array_merge( $deps, array( 'gforms_reset_css', 'gform_basic', 'gforms_formsmain_css', 'gforms_ready_class_css', 'gforms_browsers_css', 'gform_theme' ) );
|
||||
|
||||
/**
|
||||
* Allows users to disable the main theme.css file from being loaded on the Front End.
|
||||
*
|
||||
* @since 2.5-beta-3
|
||||
*
|
||||
* @param boolean Whether to disable the theme css.
|
||||
*/
|
||||
$disable_theme_css = apply_filters( 'gform_disable_form_theme_css', false );
|
||||
|
||||
if ( ! $disable_theme_css ) {
|
||||
$deps[] = 'gform_theme';
|
||||
}
|
||||
}
|
||||
|
||||
$dev_min = defined( 'GF_SCRIPT_DEBUG' ) && GF_SCRIPT_DEBUG ? '' : '.min';
|
||||
|
||||
return array(
|
||||
array(
|
||||
'handle' => $this->style_handle,
|
||||
'src' => GFCommon::get_base_url() . "/assets/css/dist/blocks{$dev_min}.css",
|
||||
'deps' => $deps,
|
||||
'version' => defined( 'GF_SCRIPT_DEBUG' ) && GF_SCRIPT_DEBUG ? filemtime( GFCommon::get_base_path() . "/assets/css/dist/blocks{$dev_min}.css" ) : GFForms::$version,
|
||||
),
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
||||
// # BLOCK RENDER -------------------------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Display block contents on frontend.
|
||||
*
|
||||
* @since 2.4.10
|
||||
*
|
||||
* @param array $attributes Block attributes.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function render_block( $attributes = array() ) {
|
||||
GFForms::get_service_container()->get( 'block_attributes' )->store( $attributes );
|
||||
|
||||
// Prepare variables.
|
||||
$form_id = rgar( $attributes, 'formId' ) ? $attributes['formId'] : false;
|
||||
$title = isset( $attributes['title'] ) ? $attributes['title'] : true;
|
||||
$description = isset( $attributes['description'] ) ? $attributes['description'] : true;
|
||||
$ajax = isset( $attributes['ajax'] ) ? $attributes['ajax'] : false;
|
||||
$tabindex = isset( $attributes['tabindex'] ) ? intval( $attributes['tabindex'] ) : 0;
|
||||
$field_values = isset( $attributes['fieldValues'] ) ? $attributes['fieldValues'] : '';
|
||||
|
||||
// If form ID was not provided or form does not exist, return.
|
||||
if ( ! $form_id || ( $form_id && ! GFAPI::get_form( $form_id ) ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// Use Gravity Forms function for REST API requests.
|
||||
if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) {
|
||||
|
||||
// Start output buffering.
|
||||
ob_start();
|
||||
|
||||
// Prepare field values.
|
||||
if ( ! empty( $field_values ) ) {
|
||||
$field_values = str_replace( '&', '&', $field_values );
|
||||
parse_str( $field_values, $field_value_array );
|
||||
$field_values = stripslashes_deep( $field_value_array );
|
||||
}
|
||||
|
||||
// Get form output string.
|
||||
$form_string = gravity_form( $form_id, $title, $description, false, $field_values, $ajax, $tabindex, false, rgar( $attributes, 'theme' ), json_encode( $attributes ) );
|
||||
|
||||
// Get output buffer contents.
|
||||
$buffer_contents = ob_get_contents();
|
||||
ob_end_clean();
|
||||
|
||||
// Return buffer contents with form string.
|
||||
return $buffer_contents . $form_string; // nosemgrep audit.php.wp.security.xss.block-attr
|
||||
|
||||
}
|
||||
|
||||
// Encode field values.
|
||||
$field_values = htmlspecialchars_decode( $field_values );
|
||||
$field_values = str_replace( array( '&', '[', ']' ), array( '&', '[', ']' ), $field_values );
|
||||
parse_str( $field_values, $field_value_array ); //parsing query string like string for field values and placing them into an associative array
|
||||
$field_values = stripslashes_deep( $field_value_array );
|
||||
|
||||
// If no field values are set, set field values to an empty string
|
||||
if ( empty( $field_values ) ) {
|
||||
$field_values = '';
|
||||
}
|
||||
|
||||
return gravity_form( $form_id, $title, $description, false, $field_values, $ajax, $tabindex, false, rgar( $attributes, 'theme' ), json_encode( $attributes ) ); // nosemgrep audit.php.wp.security.xss.block-attr
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Register block.
|
||||
if ( true !== ( $registered = GF_Blocks::register( GF_Block_Form::get_instance() ) ) && is_wp_error( $registered ) ) {
|
||||
|
||||
// Log that block could not be registered.
|
||||
GFCommon::log_error( 'Unable to register block; ' . $registered->get_error_message() );
|
||||
|
||||
}
|
||||
@@ -0,0 +1,375 @@
|
||||
<?php
|
||||
|
||||
// If Gravity Forms Block Manager is not available, do not run.
|
||||
if ( ! class_exists( 'GF_Blocks' ) || ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Base Gravity Forms Block class.
|
||||
*
|
||||
* @since 2.4.10
|
||||
*
|
||||
* Class GF_Block
|
||||
*/
|
||||
class GF_Block {
|
||||
|
||||
/**
|
||||
* Contains an instance of this block, if available.
|
||||
*
|
||||
* @since 2.4.10
|
||||
* @var GF_Block $_instance If available, contains an instance of this block.
|
||||
*/
|
||||
private static $_instance = null;
|
||||
|
||||
/**
|
||||
* Block type.
|
||||
*
|
||||
* @since 2.4.10
|
||||
* @var string
|
||||
*/
|
||||
public $type = '';
|
||||
|
||||
/**
|
||||
* Handle of primary block editor script.
|
||||
*
|
||||
* @since 2.4.10
|
||||
* @var string
|
||||
*/
|
||||
public $script_handle = '';
|
||||
|
||||
/**
|
||||
* Handle of primary block editor style.
|
||||
*
|
||||
* @since 2.5.6
|
||||
* @var string
|
||||
*/
|
||||
public $style_handle = '';
|
||||
|
||||
/**
|
||||
* Handle of primary block FE script.
|
||||
*
|
||||
* @since 2.4.10
|
||||
* @var string
|
||||
*/
|
||||
public $fe_script_handle = '';
|
||||
|
||||
/**
|
||||
* Handle of primary block FE style.
|
||||
*
|
||||
* @since 2.5.6
|
||||
* @var string
|
||||
*/
|
||||
public $fe_style_handle = '';
|
||||
|
||||
/**
|
||||
* Block attributes.
|
||||
*
|
||||
* @since 2.4.10
|
||||
* @var array
|
||||
*/
|
||||
public $attributes = array();
|
||||
|
||||
/**
|
||||
* Register block type.
|
||||
* Enqueue editor assets.
|
||||
*
|
||||
* @since 2.4.10
|
||||
*
|
||||
* @uses GF_Block::register_block_type()
|
||||
*/
|
||||
public function init() {
|
||||
|
||||
$this->register_block_type();
|
||||
|
||||
$this->register_block_assets();
|
||||
|
||||
add_action( 'gform_post_enqueue_scripts', array( $this, 'post_enqueue_scripts' ), 10, 3 );
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// # BLOCK REGISTRATION --------------------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Get block type.
|
||||
*
|
||||
* @since 2.4.10
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_type() {
|
||||
|
||||
return $this->type;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Register block with WordPress.
|
||||
*
|
||||
* @since 2.4.10
|
||||
*/
|
||||
public function register_block_type() {
|
||||
register_block_type( $this->get_type(), $this->get_block_properties() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array representing the properties for this block. Can be overriden by inheriting
|
||||
* classes in order to provide more/fewer/different properties.
|
||||
*
|
||||
* @since 2.5.6
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_block_properties() {
|
||||
return array(
|
||||
'render_callback' => array( $this, 'render_block' ),
|
||||
'editor_script' => $this->script_handle,
|
||||
'editor_style' => $this->style_handle,
|
||||
'attributes' => $this->attributes,
|
||||
'script' => $this->fe_script_handle,
|
||||
'style' => $this->fe_style_handle,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue/register the block's assets upon init
|
||||
*
|
||||
* @since 2.5.6
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register_block_assets() {
|
||||
add_action( 'enqueue_block_assets', array( $this, 'register_scripts' ) );
|
||||
add_action( 'enqueue_block_assets', array( $this, 'register_styles' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks allowed blocks for Gravity forms blocks to only enqueue block editor assets when necessary.
|
||||
*
|
||||
* @since 2.4.18
|
||||
*
|
||||
* @deprecated since 2.5.6. See GF_Block::register_block_assets()
|
||||
*
|
||||
* @param bool|array $allowed_block_types Array of block type slugs, or boolean to enable/disable all.
|
||||
*
|
||||
* @return bool|array
|
||||
*/
|
||||
public function check_allowed_blocks( $allowed_block_types ) {
|
||||
|
||||
// Only enqueue block editor assets if all blocks are allowed or if the current block type is an allowed block.
|
||||
if ( $allowed_block_types === true || ( is_array( $allowed_block_types ) && in_array( $this->get_type(), $allowed_block_types ) ) ) {
|
||||
add_action( 'enqueue_block_assets', array( $this, 'register_scripts' ) );
|
||||
add_action( 'enqueue_block_assets', array( $this, 'register_styles' ) );
|
||||
}
|
||||
|
||||
return $allowed_block_types;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// # SCRIPT ENQUEUEING ---------------------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Enqueue block scripts.
|
||||
*
|
||||
* @since 2.4.10
|
||||
*
|
||||
* @uses GF_Block::scripts()
|
||||
*/
|
||||
public function register_scripts() {
|
||||
if ( ! is_admin() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get registered scripts.
|
||||
$scripts = $this->scripts();
|
||||
|
||||
// If no scripts are registered, return.
|
||||
if ( empty( $scripts ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Loop through scripts.
|
||||
foreach ( $scripts as $script ) {
|
||||
|
||||
// Prepare parameters.
|
||||
$src = isset( $script['src'] ) ? $script['src'] : false;
|
||||
$deps = isset( $script['deps'] ) ? $script['deps'] : array();
|
||||
$version = isset( $script['version'] ) ? $script['version'] : false;
|
||||
$in_footer = isset( $script['in_footer'] ) ? $script['in_footer'] : false;
|
||||
|
||||
// Enqueue script.
|
||||
if ( $this->script_handle === $script['handle'] ) {
|
||||
// Support for the editor_style property, if a style_handle is defined. No need to enqueue.
|
||||
wp_register_script( $script['handle'], $src, $deps, $version, $in_footer );
|
||||
} else {
|
||||
// style_handle isn't defined, or this is an additional style. Enqueue it manually.
|
||||
wp_enqueue_script( $script['handle'], $src, $deps, $version, $in_footer );
|
||||
}
|
||||
|
||||
// Localize script.
|
||||
if ( rgar( $script, 'strings' ) ) {
|
||||
wp_localize_script( $script['handle'], $script['handle'] . '_strings', $script['strings'] );
|
||||
}
|
||||
|
||||
// Run script callback.
|
||||
if ( rgar( $script, 'callback' ) && is_callable( $script['callback'] ) ) {
|
||||
call_user_func( $script['callback'], $script );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue scripts
|
||||
*
|
||||
* @depecated since 2.5.6. Use ::register_scripts() instead.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function enqueue_scripts() {
|
||||
$this->register_scripts();
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this function to provide a list of scripts to be enqueued.
|
||||
* Following is an example of the array that is expected to be returned by this function:
|
||||
* <pre>
|
||||
* <code>
|
||||
*
|
||||
* array(
|
||||
* array(
|
||||
* 'handle' => 'super_signature_script',
|
||||
* 'src' => $this->get_base_url() . '/super_signature/ss.js',
|
||||
* 'version' => $this->_version,
|
||||
* 'deps' => array( 'jquery'),
|
||||
* 'callback' => array( $this, 'localize_scripts' ),
|
||||
* 'strings' => array(
|
||||
* // Accessible in JavaScript using the global variable "[script handle]_strings"
|
||||
* 'stringKey1' => __( 'The string', 'gravityforms' ),
|
||||
* 'stringKey2' => __( 'Another string.', 'gravityforms' )
|
||||
* )
|
||||
* )
|
||||
* );
|
||||
*
|
||||
* </code>
|
||||
* </pre>
|
||||
*
|
||||
* @since 2.4.10
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function scripts() {
|
||||
|
||||
return array();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// # STYLE ENQUEUEING ----------------------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Enqueue block styles.
|
||||
*
|
||||
* @since 2.4.10
|
||||
*/
|
||||
public function register_styles() {
|
||||
if ( ! is_admin() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get registered styles.
|
||||
$styles = $this->styles();
|
||||
|
||||
// If no styles are registered, return.
|
||||
if ( empty( $styles ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Loop through styles.
|
||||
foreach ( $styles as $style ) {
|
||||
|
||||
// Prepare parameters.
|
||||
$src = isset( $style['src'] ) ? $style['src'] : false;
|
||||
$deps = isset( $style['deps'] ) ? $style['deps'] : array();
|
||||
$version = isset( $style['version'] ) ? $style['version'] : false;
|
||||
$media = isset( $style['media'] ) ? $style['media'] : 'all';
|
||||
|
||||
if ( $this->style_handle === $style['handle'] ) {
|
||||
// Support for the editor_style property, if a style_handle is defined. No need to enqueue.
|
||||
wp_register_style( $style['handle'], $src, $deps, $version, $media );
|
||||
} else {
|
||||
// style_handle isn't defined, or this is an additional style. Enqueue it manually.
|
||||
wp_enqueue_style( $style['handle'], $src, $deps, $version, $media );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue styles
|
||||
*
|
||||
* @depecated since 2.5.6. Use ::register_styles() instead.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function enqueue_styles() {
|
||||
$this->register_styles();
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this function to provide a list of styles to be enqueued.
|
||||
* See scripts() for an example of the format expected to be returned.
|
||||
*
|
||||
* @since 2.4.10
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function styles() {
|
||||
|
||||
return array();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// # BLOCK RENDER -------------------------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Display block contents on frontend.
|
||||
*
|
||||
* @since 2.4.10
|
||||
*
|
||||
* @param array $attributes Block attributes.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function render_block( $attributes = array() ) {
|
||||
|
||||
return '';
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Override to perform additional actions when scripts/styles are enqueued on the wp_enqueue_scripts hook.
|
||||
*
|
||||
* @since 2.4.18
|
||||
*
|
||||
* @param array $found_forms An array of found forms using the form ID as the key to the ajax status.
|
||||
* @param array $found_blocks An array of found GF blocks.
|
||||
* @param WP_Post $post The post which was processed.
|
||||
*/
|
||||
public function post_enqueue_scripts( $found_forms, $found_blocks, $post ) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,245 @@
|
||||
<?php
|
||||
|
||||
namespace Gravity_Forms\Gravity_Forms\Blocks;
|
||||
|
||||
use Gravity_Forms\Gravity_Forms\Config\GF_Config_Service_Provider;
|
||||
use Gravity_Forms\Gravity_Forms\Blocks\Config\GF_Blocks_Config;
|
||||
use Gravity_Forms\Gravity_Forms\Blocks\GF_Block_Attributes;
|
||||
|
||||
use Gravity_Forms\Gravity_Forms\GF_Service_Container;
|
||||
use Gravity_Forms\Gravity_Forms\GF_Service_Provider;
|
||||
|
||||
use GFFormDisplay;
|
||||
use GFCommon;
|
||||
|
||||
/**
|
||||
* Class GF_Blocks_Service_Provider
|
||||
*
|
||||
* Service provider for the Blocks Service.
|
||||
*
|
||||
* @package Gravity_Forms\Gravity_Forms\Blocks;
|
||||
*/
|
||||
class GF_Blocks_Service_Provider extends GF_Service_Provider {
|
||||
|
||||
// Configs
|
||||
const BLOCKS_CONFIG = 'blocks_config';
|
||||
|
||||
// Attributes
|
||||
const FORM_BLOCK_ATTRIBUTES = 'form_block_attributes';
|
||||
|
||||
const BLOCK_ATTRIBUTES = 'block_attributes';
|
||||
|
||||
/**
|
||||
* Array mapping config class names to their container ID.
|
||||
*
|
||||
* @since
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $configs = array(
|
||||
self::BLOCKS_CONFIG => GF_Blocks_Config::class,
|
||||
);
|
||||
|
||||
/**
|
||||
* Register services to the container.
|
||||
*
|
||||
* @since
|
||||
*
|
||||
* @param GF_Service_Container $container
|
||||
*/
|
||||
public function register( GF_Service_Container $container ) {
|
||||
|
||||
require_once( plugin_dir_path( __FILE__ ) . 'class-gf-block-attributes.php' );
|
||||
require_once( plugin_dir_path( __FILE__ ) . '/config/class-gf-blocks-config.php' );
|
||||
|
||||
$container->add( self::FORM_BLOCK_ATTRIBUTES, function () {
|
||||
require_once( GFCommon::get_base_path() . '/form_display.php' );
|
||||
$global_styles = GFFormDisplay::validate_form_styles( apply_filters( 'gform_default_styles', false ) );
|
||||
|
||||
return array(
|
||||
'formId' =>
|
||||
array(
|
||||
'type' => 'string',
|
||||
),
|
||||
'title' =>
|
||||
array(
|
||||
'type' => 'boolean',
|
||||
'default' => true,
|
||||
),
|
||||
'description' =>
|
||||
array(
|
||||
'type' => 'boolean',
|
||||
'default' => true,
|
||||
),
|
||||
'ajax' =>
|
||||
array(
|
||||
'type' => 'boolean',
|
||||
'default' => false,
|
||||
),
|
||||
'tabindex' =>
|
||||
array(
|
||||
'type' => 'string',
|
||||
),
|
||||
'fieldValues' =>
|
||||
array(
|
||||
'type' => 'string',
|
||||
),
|
||||
'formPreview' =>
|
||||
array(
|
||||
'type' => 'boolean',
|
||||
'default' => true,
|
||||
),
|
||||
'imgPreview' =>
|
||||
array(
|
||||
'type' => 'boolean',
|
||||
'default' => false,
|
||||
),
|
||||
'theme' =>
|
||||
array(
|
||||
'type' => 'string',
|
||||
'default' => '',
|
||||
),
|
||||
'inputSize' =>
|
||||
array(
|
||||
'type' => 'string',
|
||||
'default' => rgar( $global_styles, 'inputSize' ) ? $global_styles['inputSize'] : 'md',
|
||||
),
|
||||
'inputBorderRadius' =>
|
||||
array(
|
||||
'type' => 'string',
|
||||
'default' => rgar( $global_styles, 'inputBorderRadius' ) ? $global_styles['inputBorderRadius'] : 3,
|
||||
),
|
||||
'inputBorderColor' =>
|
||||
array(
|
||||
'type' => 'string',
|
||||
'default' => rgar( $global_styles, 'inputBorderColor' ) ? $global_styles['inputBorderColor'] : '#686e77',
|
||||
),
|
||||
'inputBackgroundColor' =>
|
||||
array(
|
||||
'type' => 'string',
|
||||
'default' => rgar( $global_styles, 'inputBackgroundColor' ) ? $global_styles['inputBackgroundColor'] : '#fff',
|
||||
),
|
||||
'inputColor' =>
|
||||
array(
|
||||
'type' => 'string',
|
||||
'default' => rgar( $global_styles, 'inputColor' ) ? $global_styles['inputColor'] : '#112337',
|
||||
),
|
||||
'inputPrimaryColor' =>
|
||||
array(
|
||||
'type' => 'string',
|
||||
// Setting this to empty allows us to set this to what the appropriate default
|
||||
// should be from within the block. When empty, it defaults to:
|
||||
// buttonPrimaryBackgroundColor
|
||||
'default' => rgar( $global_styles, 'inputPrimaryColor' ) ? $global_styles['inputPrimaryColor'] : '', // #204ce5
|
||||
),
|
||||
'inputImageChoiceAppearance' =>
|
||||
array(
|
||||
'type' => 'string',
|
||||
'default' => rgar( $global_styles, 'inputImageChoiceAppearance' ) ? $global_styles['inputImageChoiceAppearance'] : 'card',
|
||||
),
|
||||
'inputImageChoiceStyle' =>
|
||||
array(
|
||||
'type' => 'string',
|
||||
'default' => rgar( $global_styles, 'inputImageChoiceStyle' ) ? $global_styles['inputImageChoiceStyle'] : 'square',
|
||||
),
|
||||
'inputImageChoiceSize' =>
|
||||
array(
|
||||
'type' => 'string',
|
||||
'default' => rgar( $global_styles, 'inputImageChoiceSize' ) ? $global_styles['inputImageChoiceSize'] : 'md',
|
||||
),
|
||||
'labelFontSize' =>
|
||||
array(
|
||||
'type' => 'string',
|
||||
'default' => rgar( $global_styles, 'labelFontSize' ) ? $global_styles['labelFontSize'] : 14,
|
||||
),
|
||||
'labelColor' =>
|
||||
array(
|
||||
'type' => 'string',
|
||||
'default' => rgar( $global_styles, 'labelColor' ) ? $global_styles['labelColor'] : '#112337',
|
||||
),
|
||||
'descriptionFontSize' =>
|
||||
array(
|
||||
'type' => 'string',
|
||||
'default' => rgar( $global_styles, 'descriptionFontSize' ) ? $global_styles['descriptionFontSize'] : 13,
|
||||
),
|
||||
'descriptionColor' =>
|
||||
array(
|
||||
'type' => 'string',
|
||||
'default' => rgar( $global_styles, 'descriptionColor' ) ? $global_styles['descriptionColor'] : '#585e6a',
|
||||
),
|
||||
'buttonPrimaryBackgroundColor' =>
|
||||
array(
|
||||
'type' => 'string',
|
||||
'default' => rgar( $global_styles, 'buttonPrimaryBackgroundColor' ) ? $global_styles['buttonPrimaryBackgroundColor'] : '#204ce5',
|
||||
),
|
||||
'buttonPrimaryColor' =>
|
||||
array(
|
||||
'type' => 'string',
|
||||
'default' => rgar( $global_styles, 'buttonPrimaryColor' ) ? $global_styles['buttonPrimaryColor'] : '#fff',
|
||||
),
|
||||
);
|
||||
} );
|
||||
|
||||
$this->add_configs( $container );
|
||||
$this->block_attributes( $container );
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize any actions or hooks.
|
||||
*
|
||||
* @since
|
||||
*
|
||||
* @param GF_Service_Container $container
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init( GF_Service_Container $container ) {
|
||||
|
||||
add_action( 'gform_post_enqueue_scripts', function( $found_forms, $found_blocks, $post ) use ( $container ) {
|
||||
foreach( $found_blocks as $block ) {
|
||||
$attributes = $block['attrs'];
|
||||
$container->get( self::BLOCK_ATTRIBUTES )->store( $attributes );
|
||||
}
|
||||
}, -10, 3 );
|
||||
}
|
||||
|
||||
/**
|
||||
* For each config defined in $configs, instantiate and add to container.
|
||||
*
|
||||
* @since
|
||||
*
|
||||
* @param GF_Service_Container $container
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function add_configs( GF_Service_Container $container ) {
|
||||
foreach ( $this->configs as $name => $class ) {
|
||||
$container->add( $name, function () use ( $container, $class ) {
|
||||
if ( $class == GF_Blocks_Config::class ) {
|
||||
return new $class( $container->get( GF_Config_Service_Provider::DATA_PARSER ), $container->get( self::FORM_BLOCK_ATTRIBUTES ) );
|
||||
}
|
||||
|
||||
return new $class( $container->get( GF_Config_Service_Provider::DATA_PARSER ) );
|
||||
} );
|
||||
|
||||
$container->get( GF_Config_Service_Provider::CONFIG_COLLECTION )->add_config( $container->get( $name ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register Block services.
|
||||
*
|
||||
* @since 2.7.4
|
||||
*
|
||||
* @param GF_Service_Container $container
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function block_attributes( GF_Service_Container $container ) {
|
||||
$container->add( self::BLOCK_ATTRIBUTES, function () use ( $container ) {
|
||||
return new GF_Block_Attributes();
|
||||
} );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
defined( 'ABSPATH' ) or die();
|
||||
|
||||
// Load core Block class.
|
||||
require_once( plugin_dir_path( __FILE__ ) . 'class-gf-block.php' );
|
||||
|
||||
/**
|
||||
* Handles management of Gravity Forms editor blocks.
|
||||
*
|
||||
* @since 2.4.10
|
||||
*
|
||||
* Class GF_Blocks
|
||||
*/
|
||||
class GF_Blocks {
|
||||
|
||||
/**
|
||||
* Registered Gravity Forms editor blocks.
|
||||
*
|
||||
* @since 2.4.10
|
||||
* @var GF_Block[]
|
||||
*/
|
||||
private static $_blocks = array();
|
||||
|
||||
/**
|
||||
* Register a block type.
|
||||
*
|
||||
* @since 2.4.10
|
||||
*
|
||||
* @param GF_Block $block Block class.
|
||||
*
|
||||
* @return bool|WP_Error
|
||||
*/
|
||||
public static function register( $block ) {
|
||||
|
||||
if ( ! is_subclass_of( $block, 'GF_Block' ) ) {
|
||||
return new WP_Error( 'block_not_subclass', 'Must be a subclass of GF_Block' );
|
||||
}
|
||||
|
||||
// Get block type.
|
||||
$block_type = $block->get_type();
|
||||
|
||||
if ( empty( $block_type ) ) {
|
||||
return new WP_Error( 'block_type_undefined', 'The type must be set' );
|
||||
}
|
||||
|
||||
if ( isset( self::$_blocks[ $block_type ] ) ) {
|
||||
return new WP_Error( 'block_already_registered', 'Block type already registered: ' . $block_type );
|
||||
}
|
||||
|
||||
// Register block.
|
||||
self::$_blocks[ $block_type ] = $block;
|
||||
|
||||
// Initialize block.
|
||||
call_user_func( array( $block, 'init' ) );
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get instance of block.
|
||||
*
|
||||
* @since 2.4.10
|
||||
*
|
||||
* @param string $block_type Block type.
|
||||
*
|
||||
* @return GF_Block|bool
|
||||
*/
|
||||
public static function get( $block_type ) {
|
||||
|
||||
return isset( self::$_blocks[ $block_type ] ) ? self::$_blocks[ $block_type ] : false;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of registered block types.
|
||||
*
|
||||
* @since 2.4.18
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_all_types() {
|
||||
return array_keys( self::$_blocks );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
new GF_Blocks();
|
||||
@@ -0,0 +1,174 @@
|
||||
<?php
|
||||
|
||||
namespace Gravity_Forms\Gravity_Forms\Blocks\Config;
|
||||
|
||||
use GFSettings;
|
||||
use Gravity_Forms\Gravity_Forms\Config\GF_Config;
|
||||
use Gravity_Forms\Gravity_Forms\Config\GF_Config_Data_Parser;
|
||||
use \GFCommon;
|
||||
use \GFAPI;
|
||||
use \GFFormDisplay;
|
||||
|
||||
/**
|
||||
* Config items for Blocks.
|
||||
*
|
||||
* @since
|
||||
*/
|
||||
class GF_Blocks_Config extends GF_Config {
|
||||
|
||||
protected $name = 'gform_admin_config';
|
||||
protected $script_to_localize = 'gform_gravityforms_admin_vendors';
|
||||
protected $attributes = array();
|
||||
|
||||
public function __construct( GF_Config_Data_Parser $parser, array $attributes ) {
|
||||
parent::__construct( $parser );
|
||||
$this->attributes = $attributes;
|
||||
}
|
||||
|
||||
public function should_enqueue() {
|
||||
return GFCommon::is_block_editor_page();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of forms for Block control.
|
||||
*
|
||||
* @since 2.4.10
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_forms() {
|
||||
|
||||
// Initialize forms array.
|
||||
$forms = array();
|
||||
|
||||
// Load GFFormDisplay class.
|
||||
if ( ! class_exists( 'GFFormDisplay' ) ) {
|
||||
require_once GFCommon::get_base_path() . '/form_display.php';
|
||||
}
|
||||
|
||||
// Get form objects.
|
||||
$form_objects = GFAPI::get_forms( true, false, 'title', 'ASC' );
|
||||
|
||||
// Loop through forms, add conditional logic check.
|
||||
foreach ( $form_objects as $form ) {
|
||||
$forms[] = array(
|
||||
'id' => $form['id'],
|
||||
'title' => $form['title'],
|
||||
'hasConditionalLogic' => GFFormDisplay::has_conditional_logic( $form ),
|
||||
'isLegacyMarkup' => GFCommon::is_legacy_markup_enabled( $form ),
|
||||
'hasImageChoices' => GFFormDisplay::has_image_choices( $form ),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify the list of available forms displayed in the Form block.
|
||||
*
|
||||
* @since 2.4.23
|
||||
*
|
||||
* @param array $forms A collection of active forms on site.
|
||||
*/
|
||||
return apply_filters( 'gform_block_form_forms', $forms );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Config data.
|
||||
*
|
||||
* @return array[]
|
||||
*/
|
||||
public function data() {
|
||||
$attributes = apply_filters( 'gform_form_block_attributes', $this->attributes );
|
||||
|
||||
$orbital_default = GFSettings::is_orbital_default();
|
||||
|
||||
return array(
|
||||
'block_editor' => array(
|
||||
'gravityforms/form' => array(
|
||||
'data' => array(
|
||||
'attributes' => $attributes,
|
||||
'adminURL' => admin_url( 'admin.php' ),
|
||||
'forms' => $this->get_forms(),
|
||||
'preview' => GFCommon::get_base_url() . '/images/gf_block_preview.svg',
|
||||
'orbitalDefault' => $orbital_default,
|
||||
'block_docs_url' => 'https://docs.gravityforms.com/gravity-forms-gutenberg-block/',
|
||||
'styles' => array(
|
||||
'defaults' => \GFForms::get_service_container()->get( \Gravity_Forms\Gravity_Forms\Form_Display\GF_Form_Display_Service_Provider::BLOCK_STYLES_DEFAULTS ),
|
||||
),
|
||||
),
|
||||
'i18n' => array(
|
||||
'accent' => esc_html__( 'Accent', 'gravityforms' ),
|
||||
'advanced' => esc_html__( 'Advanced', 'gravityforms' ),
|
||||
'ajax' => esc_html__( 'AJAX', 'gravityforms' ),
|
||||
'appearance' => esc_html__( 'Appearance', 'gravityforms' ),
|
||||
'background' => esc_html__( 'Background', 'gravityforms' ),
|
||||
'border' => esc_html__( 'Border', 'gravityforms' ),
|
||||
'border_radius' => esc_html__( 'Border Radius', 'gravityforms' ),
|
||||
'button_styles' => esc_html__( 'Button Styles', 'gravityforms' ),
|
||||
'cancel' => esc_html__( 'Cancel', 'gravityforms' ),
|
||||
'card' => esc_html__( 'Card', 'gravityforms' ),
|
||||
'circle' => esc_html__( 'Circle', 'gravityforms' ),
|
||||
'close' => esc_html__( 'Close', 'gravityforms' ),
|
||||
'colors' => esc_html__( 'Colors', 'gravityforms' ),
|
||||
'copy_and_paste_not_available' => esc_html__( 'Copy / Paste Not Available', 'gravityforms' ),
|
||||
'copy_and_paste_requires_secure_connection' => esc_html__( 'Copy and paste functionality requires a secure connection. Reload this page using an HTTPS URL and try again.', 'gravityforms' ),
|
||||
'copy_form_styles' => esc_html__( 'Copy Form Styles', 'gravityforms' ),
|
||||
'custom_colors' => esc_html__( 'Custom Colors', 'gravityforms' ),
|
||||
'default_colors' => esc_html__( 'Default Colors', 'gravityforms' ),
|
||||
'description_styles' => esc_html__( 'Description Styles', 'gravityforms' ),
|
||||
'edit_form' => esc_html__( 'Edit Form', 'gravityforms' ),
|
||||
'field_values' => esc_html__( 'Field Values', 'gravityforms' ),
|
||||
'font_size' => esc_html__( 'Font Size', 'gravityforms' ),
|
||||
'form' => esc_html__( 'Form', 'gravityforms' ),
|
||||
'form_id' => esc_html__( 'Form ID: %s', 'gravityforms' ),
|
||||
'form_settings' => esc_html__( 'Form Settings', 'gravityforms' ),
|
||||
'form_styles' => esc_html__( 'Form Styles', 'gravityforms' ),
|
||||
'form_theme' => esc_html__( 'Form Theme', 'gravityforms' ),
|
||||
'form_style_options_not_available' => esc_html__( 'Form style options are not available for forms that use %1$slegacy mode%2$s.', 'gravityforms' ),
|
||||
'gravity_forms' => esc_html__( 'Gravity Forms', 'gravityforms' ),
|
||||
'gravity_forms_25_theme' => esc_html__( 'Gravity Forms 2.5 Theme', 'gravityforms' ),
|
||||
'image_choice_styles' => esc_html__( 'Image Choice Styles', 'gravityforms' ),
|
||||
'inherit_from_default' => esc_html__( 'Inherit from default (%s)', 'gravityforms' ),
|
||||
'input_styles' => esc_html__( 'Input Styles', 'gravityforms' ),
|
||||
'insert_gform_block_title' => esc_html__( 'Add Block To Page', 'gravityforms' ),
|
||||
'insert_gform_block_content' => esc_html__( 'Click or drag the Gravity Forms Block into the page to insert the form you selected. %1$sLearn More.%2$s', 'gravityforms' ),
|
||||
'in_pixels' => esc_html__( 'In pixels.', 'gravityforms' ),
|
||||
'invalid_form_styles' => esc_html__( 'Invalid Form Styles', 'gravityforms' ),
|
||||
'learn_more_orbital' => esc_html__( 'Learn more about configuring your form to use Orbital.', 'gravityforms' ),
|
||||
'label_styles' => esc_html__( 'Label Styles', 'gravityforms' ),
|
||||
'large' => esc_html__( 'Large', 'gravityforms' ),
|
||||
'medium' => esc_html__( 'Medium', 'gravityforms' ),
|
||||
'no_card' => esc_html__( 'No Card', 'gravityforms' ),
|
||||
'ok' => esc_html__( 'OK', 'gravityforms' ),
|
||||
'orbital_theme' => esc_html__( 'Orbital Theme', 'gravityforms' ),
|
||||
'paste_form_styles' => esc_html__( 'Paste Form Styles', 'gravityforms' ),
|
||||
'paste_not_available' => esc_html__( 'Paste Not Available', 'gravityforms' ),
|
||||
'please_ensure_correct_format' => esc_html__( 'Please ensure the form styles you are trying to paste are in the correct format.', 'gravityforms' ),
|
||||
'preview' => esc_html__( 'Preview', 'gravityforms' ),
|
||||
'reset_defaults' => esc_html__( 'Reset Defaults', 'gravityforms' ),
|
||||
'restore_defaults' => esc_html__( 'Restore Defaults', 'gravityforms' ),
|
||||
'restore_default_styles' => esc_html__( 'Restore Default Styles', 'gravityforms' ),
|
||||
'select_a_form' => esc_html__( 'Select a Form', 'gravityforms' ),
|
||||
'select_and_display_form' => esc_html__( 'Select and display one of your forms.', 'gravityforms' ),
|
||||
'show_form_description' => esc_html__( 'Show Form Description', 'gravityforms' ),
|
||||
'show_form_title' => esc_html__( 'Show Form Title', 'gravityforms' ),
|
||||
'size' => esc_html__( 'Size', 'gravityforms' ),
|
||||
'small' => esc_html__( 'Small', 'gravityforms' ),
|
||||
'style' => esc_html__( 'Style', 'gravityforms' ),
|
||||
'square' => esc_html__( 'Square', 'gravityforms' ),
|
||||
'tabindex' => esc_html__( 'Tabindex', 'gravityforms' ),
|
||||
'text' => esc_html__( 'Text', 'gravityforms' ),
|
||||
'theme_colors' => esc_html__( 'Theme Colors', 'gravityforms' ),
|
||||
'the_accent_color_is_used' => esc_html__( 'The accent color is used for aspects such as checkmarks and dropdown choices.', 'gravityforms' ),
|
||||
'the_background_color_is_used' => esc_html__( 'The background color is used for various form elements, such as buttons and progress bars.', 'gravityforms' ),
|
||||
'the_selected_form_deleted' => esc_html__( 'The selected form has been deleted or trashed. Please select a new form.', 'gravityforms' ),
|
||||
'this_will_restore_defaults' => esc_html__( 'This will restore your form styles back to their default values and cannot be undone. Are you sure you want to continue?', 'gravityforms' ),
|
||||
'you_must_have_one_form' => esc_html__( 'You must have at least one form to use the block.', 'gravityforms' ),
|
||||
'your_browser_no_permission_to_paste' => __( 'Your browser does not have permission to paste from the clipboard. <p>Please navigate to <strong>about:config</strong> and change the preference <strong>dom.events.asyncClipboard.readText</strong> to <strong>true</strong>.', 'gravityforms' ),
|
||||
'external_link_opens_in_new_tab' => esc_html__( '(opens in a new tab)', 'gravityforms' ),
|
||||
),
|
||||
),
|
||||
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
+167
@@ -0,0 +1,167 @@
|
||||
<?php
|
||||
/**
|
||||
* Background processor for bulk entry actions.
|
||||
*
|
||||
* @package Gravity_Forms\Gravity_Forms\Bulk_Actions
|
||||
* @since 2.10.3
|
||||
*/
|
||||
|
||||
namespace Gravity_Forms\Gravity_Forms\Bulk_Actions;
|
||||
|
||||
use Gravity_Forms\Gravity_Forms\Async\GF_Background_Process;
|
||||
use GFCommon;
|
||||
use GFFormsModel;
|
||||
|
||||
if ( ! class_exists( 'GFForms' ) ) {
|
||||
die();
|
||||
}
|
||||
|
||||
if ( ! class_exists( 'Gravity_Forms\Gravity_Forms\Async\GF_Background_Process' ) ) {
|
||||
require_once GF_PLUGIN_DIR_PATH . 'includes/async/class-gf-background-process.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Class GF_Entry_Bulk_Action_Processor
|
||||
*
|
||||
* Handles background processing of bulk entry actions.
|
||||
*
|
||||
* @since 2.10.2
|
||||
*/
|
||||
class GF_Entry_Bulk_Action_Processor extends GF_Background_Process {
|
||||
|
||||
protected $action = 'gf_entry_bulk_action';
|
||||
|
||||
private static $instance = null;
|
||||
|
||||
public static function get_instance() {
|
||||
if ( self::$instance === null ) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
protected function task( $item ) {
|
||||
$entry_id = rgar( $item, 'entry_id' );
|
||||
$form_id = rgar( $item, 'form_id' );
|
||||
$bulk_action = rgar( $item, 'bulk_action' );
|
||||
|
||||
if ( empty( $bulk_action ) ) {
|
||||
GFCommon::log_debug( __METHOD__ . '(): Skipping task with missing bulk_action.' );
|
||||
$this->increment_processed();
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( $bulk_action === 'delete_form' ) {
|
||||
return $this->process_delete_form( $form_id );
|
||||
}
|
||||
|
||||
if ( empty( $entry_id ) ) {
|
||||
GFCommon::log_debug( __METHOD__ . '(): Skipping task with missing entry_id.' );
|
||||
$this->increment_processed();
|
||||
return false;
|
||||
}
|
||||
|
||||
$entry = \GFAPI::get_entry( $entry_id );
|
||||
|
||||
if ( is_wp_error( $entry ) ) {
|
||||
GFCommon::log_debug( __METHOD__ . sprintf( '(): Entry #%d not found, skipping.', $entry_id ) );
|
||||
$this->increment_processed();
|
||||
return false;
|
||||
}
|
||||
|
||||
switch ( $bulk_action ) {
|
||||
case 'delete':
|
||||
GFFormsModel::delete_entry( $entry_id );
|
||||
GFCommon::log_debug( __METHOD__ . sprintf( '(): Deleted entry #%d.', $entry_id ) );
|
||||
break;
|
||||
|
||||
case 'trash':
|
||||
\GFAPI::update_entry_property( $entry_id, 'status', 'trash' );
|
||||
GFCommon::log_debug( __METHOD__ . sprintf( '(): Trashed entry #%d.', $entry_id ) );
|
||||
break;
|
||||
|
||||
case 'restore':
|
||||
\GFAPI::update_entry_property( $entry_id, 'status', 'active' );
|
||||
GFCommon::log_debug( __METHOD__ . sprintf( '(): Restored entry #%d.', $entry_id ) );
|
||||
break;
|
||||
|
||||
case 'spam':
|
||||
\GFAPI::update_entry_property( $entry_id, 'status', 'spam' );
|
||||
GFCommon::log_debug( __METHOD__ . sprintf( '(): Marked entry #%d as spam.', $entry_id ) );
|
||||
break;
|
||||
|
||||
case 'unspam':
|
||||
\GFAPI::update_entry_property( $entry_id, 'status', 'active' );
|
||||
GFCommon::log_debug( __METHOD__ . sprintf( '(): Unmarked entry #%d as spam.', $entry_id ) );
|
||||
break;
|
||||
|
||||
case 'mark_read':
|
||||
\GFAPI::update_entry_property( $entry_id, 'is_read', 1 );
|
||||
GFCommon::log_debug( __METHOD__ . sprintf( '(): Marked entry #%d as read.', $entry_id ) );
|
||||
break;
|
||||
|
||||
case 'mark_unread':
|
||||
\GFAPI::update_entry_property( $entry_id, 'is_read', 0 );
|
||||
GFCommon::log_debug( __METHOD__ . sprintf( '(): Marked entry #%d as unread.', $entry_id ) );
|
||||
break;
|
||||
|
||||
default:
|
||||
GFCommon::log_debug( __METHOD__ . sprintf( '(): Unknown action "%s" for entry #%d.', $bulk_action, $entry_id ) );
|
||||
break;
|
||||
}
|
||||
|
||||
$this->increment_processed();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function process_delete_form( $form_id ) {
|
||||
if ( empty( $form_id ) ) {
|
||||
GFCommon::log_debug( __METHOD__ . '(): Skipping task with missing form_id.' );
|
||||
$this->increment_processed();
|
||||
return false;
|
||||
}
|
||||
|
||||
$form = \GFAPI::get_form( $form_id );
|
||||
|
||||
if ( ! $form ) {
|
||||
GFCommon::log_debug( __METHOD__ . sprintf( '(): Form #%d not found, skipping.', $form_id ) );
|
||||
$this->increment_processed();
|
||||
return false;
|
||||
}
|
||||
|
||||
GFFormsModel::delete_form( $form_id );
|
||||
GFCommon::log_debug( __METHOD__ . sprintf( '(): Permanently deleted form #%d.', $form_id ) );
|
||||
|
||||
$this->increment_processed();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function increment_processed() {
|
||||
$status = get_option( 'gf_bulk_action_status', array() );
|
||||
|
||||
if ( ! empty( $status ) && rgar( $status, 'status' ) === 'processing' ) {
|
||||
$status['processed'] = rgar( $status, 'processed', 0 ) + 1;
|
||||
update_option( 'gf_bulk_action_status', $status );
|
||||
}
|
||||
}
|
||||
|
||||
protected function complete() {
|
||||
parent::complete();
|
||||
|
||||
$status = get_option( 'gf_bulk_action_status', array() );
|
||||
|
||||
if ( ! empty( $status ) ) {
|
||||
$status['status'] = 'completed';
|
||||
$status['completed_at'] = time();
|
||||
update_option( 'gf_bulk_action_status', $status );
|
||||
}
|
||||
|
||||
GFCommon::log_debug( __METHOD__ . '(): Bulk action process completed.' );
|
||||
}
|
||||
|
||||
protected function get_action_for_log() {
|
||||
return ' for bulk entry action';
|
||||
}
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
/**
|
||||
* AJAX endpoint for cancelling bulk actions.
|
||||
*
|
||||
* @package Gravity_Forms\Gravity_Forms\Bulk_Actions\Endpoints
|
||||
* @since 2.10.3
|
||||
*/
|
||||
|
||||
namespace Gravity_Forms\Gravity_Forms\Bulk_Actions\Endpoints;
|
||||
|
||||
use GFCommon;
|
||||
use Gravity_Forms\Gravity_Forms\Bulk_Actions\GF_Entry_Bulk_Action_Processor;
|
||||
|
||||
if ( ! class_exists( 'GFForms' ) ) {
|
||||
die();
|
||||
}
|
||||
|
||||
/**
|
||||
* Class GF_Bulk_Action_Endpoint_Cancel
|
||||
*
|
||||
* Handles AJAX requests to cancel bulk actions.
|
||||
*
|
||||
* @since 2.10.2
|
||||
*/
|
||||
class GF_Bulk_Action_Endpoint_Cancel {
|
||||
|
||||
const ACTION_NAME = 'gf_bulk_action_cancel';
|
||||
|
||||
public function handle() {
|
||||
check_ajax_referer( 'gf_bulk_action', 'nonce' );
|
||||
|
||||
if ( ! GFCommon::current_user_can_any( 'gravityforms_delete_entries' ) ) {
|
||||
wp_send_json_error( array( 'message' => __( 'Permission denied.', 'gravityforms' ) ) );
|
||||
}
|
||||
|
||||
$status = get_option( 'gf_bulk_action_status', array() );
|
||||
|
||||
if ( empty( $status ) || rgar( $status, 'status' ) !== 'processing' ) {
|
||||
wp_send_json_error( array( 'message' => __( 'No active bulk operation to cancel.', 'gravityforms' ) ) );
|
||||
}
|
||||
|
||||
$bulk_action = rgar( $status, 'bulk_action' );
|
||||
|
||||
require_once GF_PLUGIN_DIR_PATH . 'includes/bulk-actions/class-gf-entry-bulk-action-processor.php';
|
||||
$processor = GF_Entry_Bulk_Action_Processor::get_instance();
|
||||
$processor->cancel();
|
||||
$processor->clear_queue();
|
||||
|
||||
delete_option( 'gf_bulk_action_status' );
|
||||
|
||||
GFCommon::log_debug( __METHOD__ . sprintf( '(): Cancelled bulk %s operation.', $bulk_action ) );
|
||||
|
||||
wp_send_json_success( array(
|
||||
'message' => __( 'Bulk operation cancelled.', 'gravityforms' ),
|
||||
'processed' => rgar( $status, 'processed', 0 ),
|
||||
'total' => rgar( $status, 'total', 0 ),
|
||||
) );
|
||||
}
|
||||
}
|
||||
+343
@@ -0,0 +1,343 @@
|
||||
<?php
|
||||
/**
|
||||
* AJAX endpoint for starting bulk actions.
|
||||
*
|
||||
* @package Gravity_Forms\Gravity_Forms\Bulk_Actions\Endpoints
|
||||
* @since 2.10.3
|
||||
*/
|
||||
|
||||
namespace Gravity_Forms\Gravity_Forms\Bulk_Actions\Endpoints;
|
||||
|
||||
use GFCommon;
|
||||
use GFAPI;
|
||||
use Gravity_Forms\Gravity_Forms\Bulk_Actions\GF_Entry_Bulk_Action_Processor;
|
||||
|
||||
if ( ! class_exists( 'GFForms' ) ) {
|
||||
die();
|
||||
}
|
||||
|
||||
/**
|
||||
* Class GF_Bulk_Action_Endpoint_Start
|
||||
*
|
||||
* Handles AJAX requests to start bulk actions.
|
||||
*
|
||||
* @since 2.10.2
|
||||
*/
|
||||
class GF_Bulk_Action_Endpoint_Start {
|
||||
|
||||
const ACTION_NAME = 'gf_start_bulk_action';
|
||||
|
||||
const BACKGROUND_THRESHOLD = 100;
|
||||
|
||||
const BACKGROUND_ACTIONS_ENTRY = array( 'delete', 'trash', 'spam', 'restore', 'unspam', 'mark_read', 'mark_unread' );
|
||||
|
||||
const BACKGROUND_ACTIONS_FORM = array( 'delete_entries', 'delete' );
|
||||
|
||||
const BACKGROUND_ACTIONS = array( 'delete', 'trash', 'spam', 'restore', 'unspam', 'delete_entries', 'mark_read', 'mark_unread' );
|
||||
|
||||
/**
|
||||
* Gets the background processing threshold.
|
||||
*
|
||||
* @since 2.10.2
|
||||
*
|
||||
* @return int The threshold for background processing.
|
||||
*/
|
||||
public static function get_background_threshold() {
|
||||
/**
|
||||
* Filters the threshold for triggering background processing of bulk actions.
|
||||
*
|
||||
* When the number of entries exceeds this threshold, bulk actions will be
|
||||
* processed in the background instead of synchronously.
|
||||
*
|
||||
* @since 2.10.2
|
||||
*
|
||||
* @param int $threshold The threshold value. Default 100.
|
||||
*/
|
||||
return apply_filters( 'gform_bulk_action_threshold', self::BACKGROUND_THRESHOLD );
|
||||
}
|
||||
|
||||
public function handle() {
|
||||
check_ajax_referer( 'gf_bulk_action', 'nonce' );
|
||||
|
||||
if ( ! GFCommon::current_user_can_any( 'gravityforms_delete_entries' ) ) {
|
||||
wp_send_json_error( array( 'message' => __( 'Permission denied.', 'gravityforms' ) ) );
|
||||
}
|
||||
|
||||
$bulk_action = sanitize_text_field( rgpost( 'bulk_action' ) );
|
||||
$action_type = sanitize_text_field( rgpost( 'action_type' ) );
|
||||
$origin_page = sanitize_text_field( rgpost( 'origin_page' ) );
|
||||
|
||||
if ( ! in_array( $bulk_action, self::BACKGROUND_ACTIONS, true ) ) {
|
||||
wp_send_json_error( array( 'message' => __( 'Invalid action.', 'gravityforms' ) ) );
|
||||
}
|
||||
|
||||
$status = get_option( 'gf_bulk_action_status', array() );
|
||||
if ( ! empty( $status ) && rgar( $status, 'status' ) === 'processing' ) {
|
||||
wp_send_json_error( array( 'message' => __( 'A bulk operation is already in progress. Please wait for it to complete.', 'gravityforms' ) ) );
|
||||
}
|
||||
|
||||
if ( $action_type === 'form' ) {
|
||||
$this->handle_form_action( $bulk_action, $origin_page );
|
||||
} else {
|
||||
$this->handle_entry_action( $bulk_action, $origin_page );
|
||||
}
|
||||
}
|
||||
|
||||
private function handle_entry_action( $bulk_action, $origin_page ) {
|
||||
$form_id = absint( rgpost( 'form_id' ) );
|
||||
$entry_ids = rgpost( 'entry_ids' );
|
||||
$select_all = rgpost( 'select_all' ) === 'true';
|
||||
|
||||
if ( empty( $form_id ) ) {
|
||||
wp_send_json_error( array( 'message' => __( 'Invalid form ID.', 'gravityforms' ) ) );
|
||||
}
|
||||
|
||||
if ( $select_all ) {
|
||||
$search_criteria = rgpost( 'search_criteria' );
|
||||
if ( is_string( $search_criteria ) ) {
|
||||
$search_criteria = json_decode( stripslashes( $search_criteria ), true );
|
||||
}
|
||||
$entry_ids = GFAPI::get_entry_ids( $form_id, $search_criteria );
|
||||
} elseif ( is_string( $entry_ids ) ) {
|
||||
$entry_ids = json_decode( stripslashes( $entry_ids ), true );
|
||||
}
|
||||
|
||||
if ( empty( $entry_ids ) || ! is_array( $entry_ids ) ) {
|
||||
wp_send_json_error( array( 'message' => __( 'No entries selected.', 'gravityforms' ) ) );
|
||||
}
|
||||
|
||||
$total = count( $entry_ids );
|
||||
|
||||
$status = array(
|
||||
'form_id' => $form_id,
|
||||
'bulk_action' => $bulk_action,
|
||||
'action_type' => 'entry',
|
||||
'total' => $total,
|
||||
'processed' => 0,
|
||||
'status' => 'processing',
|
||||
'started_at' => time(),
|
||||
'completed_at' => null,
|
||||
'origin_url' => 'admin.php?page=gf_entries&id=' . $form_id,
|
||||
'origin_page' => $origin_page ?: 'entry_list',
|
||||
);
|
||||
update_option( 'gf_bulk_action_status', $status );
|
||||
|
||||
require_once GF_PLUGIN_DIR_PATH . 'includes/bulk-actions/class-gf-entry-bulk-action-processor.php';
|
||||
$processor = GF_Entry_Bulk_Action_Processor::get_instance();
|
||||
$this->reset_processor_if_cancelled( $processor );
|
||||
|
||||
$chunks = array_chunk( $entry_ids, self::get_background_threshold() );
|
||||
|
||||
foreach ( $chunks as $chunk ) {
|
||||
foreach ( $chunk as $entry_id ) {
|
||||
$processor->push_to_queue( array(
|
||||
'entry_id' => absint( $entry_id ),
|
||||
'form_id' => $form_id,
|
||||
'bulk_action' => $bulk_action,
|
||||
) );
|
||||
}
|
||||
$processor->save();
|
||||
}
|
||||
|
||||
$processor->dispatch();
|
||||
|
||||
GFCommon::log_debug( __METHOD__ . sprintf( '(): Started bulk %s for form #%d with %d entries.', $bulk_action, $form_id, $total ) );
|
||||
|
||||
wp_send_json_success( array(
|
||||
'total' => $total,
|
||||
'bulk_action' => $bulk_action,
|
||||
'action_type' => 'entry',
|
||||
) );
|
||||
}
|
||||
|
||||
private function handle_form_action( $bulk_action, $origin_page ) {
|
||||
$form_ids = rgpost( 'form_ids' );
|
||||
|
||||
if ( is_string( $form_ids ) ) {
|
||||
$form_ids = json_decode( stripslashes( $form_ids ), true );
|
||||
}
|
||||
|
||||
if ( empty( $form_ids ) || ! is_array( $form_ids ) ) {
|
||||
wp_send_json_error( array( 'message' => __( 'No forms selected.', 'gravityforms' ) ) );
|
||||
}
|
||||
|
||||
switch ( $bulk_action ) {
|
||||
case 'delete_entries':
|
||||
$this->handle_delete_entries_action( $form_ids, $origin_page );
|
||||
break;
|
||||
|
||||
case 'delete':
|
||||
$this->handle_delete_forms_action( $form_ids, $origin_page );
|
||||
break;
|
||||
|
||||
default:
|
||||
wp_send_json_error( array( 'message' => __( 'Invalid action.', 'gravityforms' ) ) );
|
||||
}
|
||||
}
|
||||
|
||||
private function handle_delete_entries_action( $form_ids, $origin_page ) {
|
||||
$all_entry_ids = array();
|
||||
|
||||
foreach ( $form_ids as $form_id ) {
|
||||
$entry_ids = GFAPI::get_entry_ids( absint( $form_id ) );
|
||||
if ( ! empty( $entry_ids ) ) {
|
||||
foreach ( $entry_ids as $entry_id ) {
|
||||
$all_entry_ids[] = array(
|
||||
'entry_id' => absint( $entry_id ),
|
||||
'form_id' => absint( $form_id ),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( empty( $all_entry_ids ) ) {
|
||||
wp_send_json_error( array( 'message' => __( 'No entries found in the selected forms.', 'gravityforms' ) ) );
|
||||
}
|
||||
|
||||
$total = count( $all_entry_ids );
|
||||
|
||||
if ( $total <= self::get_background_threshold() ) {
|
||||
wp_send_json_success( array(
|
||||
'below_threshold' => true,
|
||||
'total' => $total,
|
||||
) );
|
||||
}
|
||||
|
||||
$status = array(
|
||||
'form_id' => null,
|
||||
'form_ids' => $form_ids,
|
||||
'bulk_action' => 'delete_entries',
|
||||
'action_type' => 'form',
|
||||
'total' => $total,
|
||||
'processed' => 0,
|
||||
'status' => 'processing',
|
||||
'started_at' => time(),
|
||||
'completed_at' => null,
|
||||
'origin_url' => 'admin.php?page=gf_edit_forms',
|
||||
'origin_page' => $origin_page ?: 'form_list',
|
||||
);
|
||||
update_option( 'gf_bulk_action_status', $status );
|
||||
|
||||
require_once GF_PLUGIN_DIR_PATH . 'includes/bulk-actions/class-gf-entry-bulk-action-processor.php';
|
||||
$processor = GF_Entry_Bulk_Action_Processor::get_instance();
|
||||
$this->reset_processor_if_cancelled( $processor );
|
||||
|
||||
$chunks = array_chunk( $all_entry_ids, self::get_background_threshold() );
|
||||
|
||||
foreach ( $chunks as $chunk ) {
|
||||
foreach ( $chunk as $entry_data ) {
|
||||
$processor->push_to_queue( array(
|
||||
'entry_id' => $entry_data['entry_id'],
|
||||
'form_id' => $entry_data['form_id'],
|
||||
'bulk_action' => 'delete',
|
||||
) );
|
||||
}
|
||||
$processor->save();
|
||||
}
|
||||
|
||||
$processor->dispatch();
|
||||
|
||||
GFCommon::log_debug( __METHOD__ . sprintf( '(): Started bulk delete_entries for %d forms (%d entries).', count( $form_ids ), $total ) );
|
||||
|
||||
wp_send_json_success( array(
|
||||
'total' => $total,
|
||||
'bulk_action' => 'delete_entries',
|
||||
'action_type' => 'form',
|
||||
) );
|
||||
}
|
||||
|
||||
private function handle_delete_forms_action( $form_ids, $origin_page ) {
|
||||
$all_entry_ids = array();
|
||||
$form_ids_int = array();
|
||||
|
||||
foreach ( $form_ids as $form_id ) {
|
||||
$form_id_int = absint( $form_id );
|
||||
$form_ids_int[] = $form_id_int;
|
||||
$entry_ids = GFAPI::get_entry_ids( $form_id_int );
|
||||
|
||||
if ( ! empty( $entry_ids ) ) {
|
||||
foreach ( $entry_ids as $entry_id ) {
|
||||
$all_entry_ids[] = array(
|
||||
'entry_id' => absint( $entry_id ),
|
||||
'form_id' => $form_id_int,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$entry_count = count( $all_entry_ids );
|
||||
$form_count = count( $form_ids_int );
|
||||
$total = $entry_count + $form_count;
|
||||
|
||||
if ( $entry_count <= self::get_background_threshold() ) {
|
||||
wp_send_json_success( array(
|
||||
'below_threshold' => true,
|
||||
'total' => $entry_count,
|
||||
) );
|
||||
}
|
||||
|
||||
$status = array(
|
||||
'form_id' => null,
|
||||
'form_ids' => $form_ids_int,
|
||||
'bulk_action' => 'delete',
|
||||
'action_type' => 'form',
|
||||
'total' => $total,
|
||||
'processed' => 0,
|
||||
'status' => 'processing',
|
||||
'started_at' => time(),
|
||||
'completed_at' => null,
|
||||
'origin_url' => 'admin.php?page=gf_edit_forms',
|
||||
'origin_page' => $origin_page ?: 'form_list',
|
||||
);
|
||||
update_option( 'gf_bulk_action_status', $status );
|
||||
|
||||
require_once GF_PLUGIN_DIR_PATH . 'includes/bulk-actions/class-gf-entry-bulk-action-processor.php';
|
||||
$processor = GF_Entry_Bulk_Action_Processor::get_instance();
|
||||
$this->reset_processor_if_cancelled( $processor );
|
||||
|
||||
$chunks = array_chunk( $all_entry_ids, self::get_background_threshold() );
|
||||
|
||||
foreach ( $chunks as $chunk ) {
|
||||
foreach ( $chunk as $entry_data ) {
|
||||
$processor->push_to_queue( array(
|
||||
'entry_id' => $entry_data['entry_id'],
|
||||
'form_id' => $entry_data['form_id'],
|
||||
'bulk_action' => 'delete',
|
||||
) );
|
||||
}
|
||||
$processor->save();
|
||||
}
|
||||
|
||||
foreach ( $form_ids_int as $form_id ) {
|
||||
$processor->push_to_queue( array(
|
||||
'form_id' => $form_id,
|
||||
'bulk_action' => 'delete_form',
|
||||
) );
|
||||
}
|
||||
$processor->save();
|
||||
|
||||
$processor->dispatch();
|
||||
|
||||
GFCommon::log_debug( __METHOD__ . sprintf( '(): Started bulk form delete for %d forms (%d entries).', $form_count, $entry_count ) );
|
||||
|
||||
wp_send_json_success( array(
|
||||
'total' => $total,
|
||||
'bulk_action' => 'delete',
|
||||
'action_type' => 'form',
|
||||
) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the processor if it was previously cancelled.
|
||||
*
|
||||
* @since 2.10.2
|
||||
*
|
||||
* @param GF_Entry_Bulk_Action_Processor $processor The processor instance.
|
||||
*/
|
||||
private function reset_processor_if_cancelled( $processor ) {
|
||||
if ( $processor->is_cancelled() ) {
|
||||
$processor->clear_queue();
|
||||
$processor->resume( false );
|
||||
}
|
||||
}
|
||||
}
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
/**
|
||||
* AJAX endpoint for checking bulk action status.
|
||||
*
|
||||
* @package Gravity_Forms\Gravity_Forms\Bulk_Actions\Endpoints
|
||||
* @since 2.10.3
|
||||
*/
|
||||
|
||||
namespace Gravity_Forms\Gravity_Forms\Bulk_Actions\Endpoints;
|
||||
|
||||
use GFCommon;
|
||||
use Gravity_Forms\Gravity_Forms\Bulk_Actions\GF_Entry_Bulk_Action_Processor;
|
||||
|
||||
if ( ! class_exists( 'GFForms' ) ) {
|
||||
die();
|
||||
}
|
||||
|
||||
/**
|
||||
* Class GF_Bulk_Action_Endpoint_Status
|
||||
*
|
||||
* Handles AJAX requests to check bulk action progress.
|
||||
*
|
||||
* @since 2.10.2
|
||||
*/
|
||||
class GF_Bulk_Action_Endpoint_Status {
|
||||
|
||||
const ACTION_NAME = 'gf_bulk_action_status';
|
||||
|
||||
const STUCK_TIMEOUT = 1800; // 30 minutes
|
||||
|
||||
public function handle() {
|
||||
check_ajax_referer( 'gf_bulk_action', 'nonce' );
|
||||
|
||||
if ( ! GFCommon::current_user_can_any( 'gravityforms_delete_entries' ) ) {
|
||||
wp_send_json_error( array( 'message' => __( 'Permission denied.', 'gravityforms' ) ) );
|
||||
}
|
||||
|
||||
$status = get_option( 'gf_bulk_action_status', array() );
|
||||
|
||||
require_once GF_PLUGIN_DIR_PATH . 'includes/bulk-actions/class-gf-entry-bulk-action-processor.php';
|
||||
$processor = GF_Entry_Bulk_Action_Processor::get_instance();
|
||||
$has_queue = $processor->is_queued();
|
||||
|
||||
if ( $this->is_stuck( $status, $processor ) ) {
|
||||
$processor->clear_queue();
|
||||
delete_option( 'gf_bulk_action_status' );
|
||||
wp_send_json_success( array( 'active' => false ) );
|
||||
}
|
||||
|
||||
if ( empty( $status ) ) {
|
||||
if ( $has_queue ) {
|
||||
$processor->clear_queue();
|
||||
}
|
||||
|
||||
wp_send_json_success( array(
|
||||
'active' => false,
|
||||
) );
|
||||
}
|
||||
|
||||
$is_processing = rgar( $status, 'status' ) === 'processing';
|
||||
|
||||
wp_send_json_success( array(
|
||||
'active' => $is_processing || $has_queue,
|
||||
'pending' => $has_queue && ! $processor->is_processing(),
|
||||
'cancelled' => rgar( $status, 'status' ) === 'cancelled',
|
||||
'form_id' => rgar( $status, 'form_id' ),
|
||||
'bulk_action' => rgar( $status, 'bulk_action' ),
|
||||
'action_type' => rgar( $status, 'action_type', 'entry' ),
|
||||
'total' => rgar( $status, 'total', 0 ),
|
||||
'processed' => rgar( $status, 'processed', 0 ),
|
||||
'status' => rgar( $status, 'status' ),
|
||||
'origin_url' => rgar( $status, 'origin_url' ),
|
||||
'origin_page' => rgar( $status, 'origin_page' ),
|
||||
) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a bulk action is stuck.
|
||||
*
|
||||
* @since 2.10.2
|
||||
*
|
||||
* @param array $status The bulk action status.
|
||||
* @param GF_Entry_Bulk_Action_Processor $processor The processor instance.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function is_stuck( $status, $processor ) {
|
||||
if ( empty( $status ) || rgar( $status, 'status' ) !== 'processing' ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( $processor->is_processing() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$started_at = rgar( $status, 'started_at', 0 );
|
||||
$elapsed = time() - $started_at;
|
||||
|
||||
return $elapsed > self::STUCK_TIMEOUT;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
/**
|
||||
* Background Upgrader
|
||||
*
|
||||
* Uses https://github.com/A5hleyRich/wp-background-processing to handle DB
|
||||
* updates in the background.
|
||||
*
|
||||
* @class GF_Background_Upgrader
|
||||
* @version 2.3
|
||||
* @category Class
|
||||
* @author Rocketgenius
|
||||
*/
|
||||
|
||||
use Gravity_Forms\Gravity_Forms\Async\GF_Background_Process;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
if ( ! class_exists( 'Gravity_Forms\Gravity_Forms\Async\GF_Background_Process' ) ) {
|
||||
require_once GF_PLUGIN_DIR_PATH . 'includes/async/class-gf-background-process.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* GF_Background_Upgrader Class.
|
||||
*/
|
||||
class GF_Background_Upgrader extends GF_Background_Process {
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $action = 'gf_upgrader';
|
||||
|
||||
/**
|
||||
* Returns the data for the background upgrader.
|
||||
*
|
||||
* @since 2.3
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_data() {
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the queue empty for all blogs?
|
||||
*
|
||||
* @since 2.3
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_queue_empty() {
|
||||
return parent::is_queue_empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the updater running?
|
||||
* @return boolean
|
||||
*/
|
||||
public function is_updating() {
|
||||
return false === $this->is_queue_empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Task
|
||||
*
|
||||
* Override this method to perform any actions required on each
|
||||
* queue item. Return the modified item for further processing
|
||||
* in the next pass through. Or, return false to remove the
|
||||
* item from the queue.
|
||||
*
|
||||
* @param string $callback Update callback function
|
||||
* @return mixed
|
||||
*/
|
||||
protected function task( $callback ) {
|
||||
if ( ! defined( 'GF_UPGRADING' ) ) {
|
||||
define( 'GF_UPGRADING', true );
|
||||
}
|
||||
|
||||
if ( is_callable( $callback ) ) {
|
||||
GFCommon::log_debug( sprintf( '%s(): Running callback: %s', __METHOD__, print_r( $callback, 1 ) ) );
|
||||
$needs_more_time = call_user_func( $callback );
|
||||
if ( $needs_more_time ) {
|
||||
GFCommon::log_debug( sprintf( '%s(): Callback needs another run: %s', __METHOD__, print_r( $callback, 1 ) ) );
|
||||
return $callback;
|
||||
} else {
|
||||
GFCommon::log_debug( sprintf( '%s(): Finished callback: %s', __METHOD__, print_r( $callback, 1 ) ) );
|
||||
}
|
||||
} else {
|
||||
GFCommon::log_debug( sprintf( '%s(): Could not find callback: %s', __METHOD__, print_r( $callback, 1 ) ) );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete
|
||||
*
|
||||
* Override if applicable, but ensure that the below actions are
|
||||
* performed, or, call parent::complete().
|
||||
*/
|
||||
protected function complete() {
|
||||
parent::complete();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,280 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Handles download requests for files stored by File Upload fields.
|
||||
*
|
||||
* Class GF_Download
|
||||
*/
|
||||
class GF_Download {
|
||||
|
||||
/**
|
||||
* If the request is for a Gravity Forms file download then validate and deliver.
|
||||
*
|
||||
* @since 2.0
|
||||
*/
|
||||
public static function maybe_process() {
|
||||
if ( isset( $_GET['gf-download'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
|
||||
$file = $_GET['gf-download']; // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash
|
||||
$form_id = rgget( 'form-id' );
|
||||
$field_id = rgget( 'field-id' );
|
||||
|
||||
if ( empty( $file ) || empty( $form_id ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$hash = rgget( 'hash' );
|
||||
$entry_id = rgget( 'entry-id' );
|
||||
|
||||
GFCommon::log_debug( __METHOD__ . "(): Starting file download process. file: {$file}, hash: {$hash}." );
|
||||
|
||||
$file_validation = self::validate_file_path( $file );
|
||||
if ( is_wp_error( $file_validation ) ) {
|
||||
GFCommon::log_debug( __METHOD__ . sprintf( '(): Not downloading file (%s): %s', $file_validation->get_error_code(), $file ) );
|
||||
self::die_401();
|
||||
}
|
||||
|
||||
$permission_granted = self::validate_download( $form_id, $field_id, $file, $hash, $entry_id );
|
||||
|
||||
if ( has_filter( 'gform_permission_granted_pre_download' ) ) {
|
||||
GFCommon::log_debug( __METHOD__ . '(): Executing functions hooked to gform_permission_granted_pre_download.' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow custom logic to be used to determine if the file can be accessed.
|
||||
*
|
||||
* @since 2.4.3.2
|
||||
*
|
||||
* @param bool $permission_granted Indicates if access to the file has been granted. Default is the result of the hash validation.
|
||||
* @param string $form_id The ID of the form used to upload the requested file.
|
||||
* @param string $field_id The ID of the field used to upload the requested file.
|
||||
*/
|
||||
$permission_granted = apply_filters( 'gform_permission_granted_pre_download', $permission_granted, $form_id, $field_id );
|
||||
|
||||
if ( $permission_granted ) {
|
||||
GFCommon::log_debug( __METHOD__ . '(): Download validated. Proceeding.' );
|
||||
self::deliver( $form_id, $file, $entry_id );
|
||||
} else {
|
||||
GFCommon::log_debug( __METHOD__ . '(): Download validation failed. Aborting with 401.' );
|
||||
self::die_401();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the hash for the download.
|
||||
*
|
||||
* @since 2.0
|
||||
* @since 2.9.29 Added the $entry_id param.
|
||||
*
|
||||
* @param string $form_id The form ID from the query string.
|
||||
* @param string $field_id The field ID from the query string.
|
||||
* @param string $file The file name from the query string.
|
||||
* @param string $hash The hash from the query string.
|
||||
* @param string $entry_id The entry ID from the query string.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private static function validate_download( $form_id, $field_id, $file, $hash, $entry_id ) {
|
||||
if ( empty( $hash ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( has_filter( 'gform_require_login_pre_download' ) ) {
|
||||
GFCommon::log_debug( __METHOD__ . '(): Executing functions hooked to gform_require_login_pre_download.' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows login to be required to access the file.
|
||||
*
|
||||
* @since 2.2.3.16
|
||||
*
|
||||
* @param bool $require_login Does the user need to be logged in to access the file? Default false.
|
||||
* @param string $form_id The ID of the form used to upload the requested file.
|
||||
* @param string $field_id The ID of the field used to upload the requested file.
|
||||
*/
|
||||
$require_login = apply_filters( 'gform_require_login_pre_download', false, $form_id, $field_id );
|
||||
|
||||
if ( $require_login && ! is_user_logged_in() ) {
|
||||
GFCommon::log_debug( __METHOD__ . '(): Login required to access file.' );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$hash_check = GFCommon::generate_download_hash( $form_id, $field_id, $file, $entry_id );
|
||||
$valid = hash_equals( $hash, $hash_check );
|
||||
|
||||
if ( ! $valid ) {
|
||||
GFCommon::log_debug( __METHOD__ . '(): Hash failed validation.' );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the file.
|
||||
*
|
||||
* @since 2.0
|
||||
* @since 2.9.29 Added the $entry_id param.
|
||||
*
|
||||
* @param string $form_id The form ID from the query string.
|
||||
* @param string $file The file name from the query string.
|
||||
* @param string $entry_id The entry ID from the query string.
|
||||
*/
|
||||
private static function deliver( $form_id, $file, $entry_id ) {
|
||||
if ( $entry_id && ! GFAPI::entry_exists( $entry_id ) ) {
|
||||
GFCommon::log_debug( __METHOD__ . sprintf( '(): Entry ID %d has been deleted. Aborting with 404', $entry_id ) );
|
||||
self::die_404();
|
||||
}
|
||||
|
||||
$path = GFFormsModel::get_upload_path( $form_id );
|
||||
$file_path = trailingslashit( $path ) . $file;
|
||||
|
||||
GFCommon::log_debug( __METHOD__ . "(): Checking if file exists: {$file_path}." );
|
||||
|
||||
if ( file_exists( $file_path ) ) {
|
||||
GFCommon::log_debug( __METHOD__ . '(): File exists. Starting delivery.' );
|
||||
|
||||
$content_type = self::get_content_type( $file_path );
|
||||
$content_disposition = rgget( 'dl' ) ? 'attachment' : 'inline';
|
||||
|
||||
/**
|
||||
* Allows the nocache_headers() to be overridden.
|
||||
*
|
||||
* @since 2.9.15
|
||||
*
|
||||
* @param bool $enable If the nocache_headers() should be called. Default true.
|
||||
* @param string $form_id The ID of the form used to download files.
|
||||
* @param string $file The file to be downloaded.
|
||||
*/
|
||||
if ( apply_filters( 'gform_enable_download_nocache_headers', true, $form_id, $file ) ) {
|
||||
nocache_headers();
|
||||
}
|
||||
|
||||
header( 'X-Robots-Tag: noindex', true );
|
||||
header( 'Content-Type: ' . $content_type );
|
||||
header( 'Content-Description: File Transfer' );
|
||||
header( 'Content-Disposition: ' . $content_disposition . '; filename="' . wp_basename( $file ) . '"' );
|
||||
header( 'Content-Transfer-Encoding: binary' );
|
||||
// Clear buffer AND turn off output buffering before starting delivery of files requested for download to prevent third-parties to corrupt the file content.
|
||||
if ( ob_get_contents() ) {
|
||||
ob_end_clean();
|
||||
}
|
||||
self::readfile_chunked( $file_path );
|
||||
die();
|
||||
} else {
|
||||
GFCommon::log_debug( __METHOD__ . '(): File does not exist. Aborting with 404.' );
|
||||
self::die_404();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the appropriate mime type for the file extension.
|
||||
*
|
||||
* @param $file_path
|
||||
*
|
||||
* @return mixed|null|string
|
||||
*/
|
||||
private static function get_content_type( $file_path ) {
|
||||
$info = wp_check_filetype( $file_path );
|
||||
$type = rgar( $info, 'type' );
|
||||
|
||||
return $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads file in chunks so big downloads are possible without changing PHP.INI
|
||||
* See https://github.com/bcit-ci/CodeIgniter/wiki/Download-helper-for-large-files
|
||||
*
|
||||
* @access public
|
||||
* @param string $file The file
|
||||
* @param boolean $retbytes Return the bytes of file
|
||||
* @return bool|string If string, $status || $cnt
|
||||
*/
|
||||
private static function readfile_chunked( $file, $retbytes = true ) {
|
||||
|
||||
$chunksize = 1024 * 1024;
|
||||
$buffer = '';
|
||||
$cnt = 0;
|
||||
$handle = @fopen( $file, 'r' );
|
||||
|
||||
if ( $size = @filesize( $file ) ) {
|
||||
header( 'Content-Length: ' . $size );
|
||||
}
|
||||
|
||||
if ( false === $handle ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
while ( ! @feof( $handle ) ) {
|
||||
$buffer = @fread( $handle, $chunksize );
|
||||
echo $buffer; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
|
||||
if ( $retbytes ) {
|
||||
$cnt += strlen( $buffer );
|
||||
}
|
||||
}
|
||||
|
||||
$status = @fclose( $handle );
|
||||
|
||||
if ( $retbytes && $status ) {
|
||||
return $cnt;
|
||||
}
|
||||
|
||||
return $status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ends the request with a 404 (Not Found) HTTP status code. Loads the 404 template if it exists.
|
||||
*/
|
||||
private static function die_404() {
|
||||
global $wp_query;
|
||||
status_header( 404 );
|
||||
$wp_query->set_404();
|
||||
$template_path = get_404_template();
|
||||
if ( file_exists( $template_path ) ) {
|
||||
require_once( $template_path );
|
||||
}
|
||||
die();
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a file path from a download request for security concerns.
|
||||
*
|
||||
* Checks for null bytes and directory traversal characters (including encoded variants).
|
||||
*
|
||||
* @since 2.10.2
|
||||
*
|
||||
* @param string $file The file path to validate.
|
||||
*
|
||||
* @return true|WP_Error True if the file path is safe, WP_Error otherwise.
|
||||
*/
|
||||
public static function validate_file_path( $file ) {
|
||||
if ( empty( $file ) ) {
|
||||
return new WP_Error( 'empty_file', __( 'The file path is empty.', 'gravityforms' ) );
|
||||
}
|
||||
|
||||
// Null byte injection check.
|
||||
if ( str_contains( $file, '%00' ) || str_contains( $file, "\0" ) ) {
|
||||
return new WP_Error( 'null_byte', __( 'The file path contains a null byte.', 'gravityforms' ) );
|
||||
}
|
||||
|
||||
// Directory traversal check on decoded path to catch encoded variants.
|
||||
$decoded_file = rawurldecode( rawurldecode( rawurldecode( $file ) ) );
|
||||
if ( str_contains( $decoded_file, '..' ) ) {
|
||||
return new WP_Error( 'directory_traversal', __( 'The file path contains directory traversal characters.', 'gravityforms' ) );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ends the request with a 401 (Unauthorized) HTTP status code.
|
||||
*/
|
||||
private static function die_401() {
|
||||
status_header( 401 );
|
||||
die();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,351 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Adds integration with osDXP.
|
||||
*
|
||||
* @since 2.4.15
|
||||
*/
|
||||
class GF_OSDXP {
|
||||
|
||||
/**
|
||||
* @var GF_OSDXP
|
||||
*/
|
||||
private static $instance;
|
||||
|
||||
/**
|
||||
* @var array The array of page slugs.
|
||||
*/
|
||||
private $slugs = array();
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @since 2.4.15
|
||||
*/
|
||||
private function __construct() {
|
||||
$this->slugs = [
|
||||
'gf_settings' => esc_html__( 'Settings', 'gravityforms' ),
|
||||
'gf_export' => esc_html__( 'Import/Export', 'gravityforms' ),
|
||||
'gf_addons' => esc_html__( 'Add-Ons', 'gravityforms' ),
|
||||
'gf_help' => esc_html__( 'Help', 'gravityforms' ),
|
||||
'gf_system_status' => esc_html__( 'System Status', 'gravityforms' ),
|
||||
];
|
||||
add_filter( 'osdxp_license_key_gravityforms', array( $this, 'license_key' ) );
|
||||
add_filter( 'osdxp_dashboard_license_submit_response', array( $this, 'process_license_key_submit' ), 10, 3 );
|
||||
add_filter( 'osdxp_dashboard_license_deletion_response', array(
|
||||
$this,
|
||||
'process_license_key_deletion',
|
||||
), 10, 2 );
|
||||
add_filter( 'osdxp_add_module_settings_page', array( $this, 'settings_page' ) );
|
||||
add_filter( 'parent_file', array( $this, 'settings_page_highlight' ) );
|
||||
add_action( 'in_admin_header', array( $this, 'nav_tabs' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a class instance.
|
||||
*
|
||||
* @since 2.4.15
|
||||
*
|
||||
* @return GF_OSDXP instance of GF_OSDXP.
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if ( ! self::$instance ) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the license key display.
|
||||
*
|
||||
* @since 2.4.15
|
||||
*
|
||||
* @param $license_key
|
||||
*
|
||||
* @return string|null Return a string or null to display the text box.
|
||||
*/
|
||||
public static function license_key( $license_key ) {
|
||||
$version_info = GFCommon::get_version_info( false );
|
||||
$key = GFCommon::get_key();
|
||||
|
||||
if ( ! rgempty( 'is_error', $version_info ) ) {
|
||||
return esc_attr__( 'There was an error while validating your license key. Gravity Forms will continue to work, but automatic upgrades will not be available. Please contact support to resolve this issue.', 'gravityforms' );
|
||||
} elseif ( rgar( $version_info, 'is_valid_key' ) ) {
|
||||
return esc_html__( 'Valid', 'gravityforms' );
|
||||
|
||||
} elseif ( ! empty( $key ) ) {
|
||||
return esc_html__( 'Invalid or Expired.', 'gravityforms' );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Process license key submit.
|
||||
*
|
||||
* Callback for the 'osdxp_dashboard_license_submit_response' filter.
|
||||
*
|
||||
* @since 2.4.15
|
||||
*
|
||||
* @param array $response Response.
|
||||
* @param string $plugin_slug Plugin slug.
|
||||
* @param string $license_key License key.
|
||||
*
|
||||
* @return array Response array.
|
||||
*/
|
||||
public static function process_license_key_submit( $response, $plugin_slug, $license_key ) {
|
||||
|
||||
if ( 'gravityforms' !== $plugin_slug ) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
if ( ! is_array( $response ) ) {
|
||||
$response = array();
|
||||
}
|
||||
|
||||
// Sanitize license key.
|
||||
$license_key = sanitize_text_field( $license_key );
|
||||
|
||||
GFFormsModel::save_key( $license_key );
|
||||
|
||||
// Updating message because key could have been changed.
|
||||
GFCommon::cache_remote_message();
|
||||
|
||||
// Re-caching version info.
|
||||
$version_info = GFCommon::get_version_info( false );
|
||||
|
||||
$error_message = '';
|
||||
|
||||
$key = GFCommon::get_key();
|
||||
|
||||
if ( ! rgempty( 'is_error', $version_info ) ) {
|
||||
$error_message = esc_attr__( 'There was an error while validating your license key. Gravity Forms will continue to work, but automatic upgrades will not be available. Please contact support to resolve this issue.', 'gravityforms' );
|
||||
} elseif ( rgar( $version_info, 'is_valid_key' ) ) {
|
||||
|
||||
update_option( 'gform_pending_installation', 0 );
|
||||
if ( ! get_option( 'rg_gforms_currency' ) ) {
|
||||
update_option( 'rg_gforms_currency', 'USD' );
|
||||
}
|
||||
|
||||
$success_message = esc_html__( 'Valid Key : Your license key has been successfully validated.', 'gravityforms' );
|
||||
|
||||
if ( empty( $response['success_messages'] ) || ! is_array( $response['success_messages'] ) ) {
|
||||
$response['success_messages'] = array();
|
||||
}
|
||||
|
||||
$response['success_messages'][] = $success_message;
|
||||
} elseif ( rgar( $version_info, 'reason' ) == 'requires_enterprise_license' ) {
|
||||
$error_message = esc_html__( 'Invalid Key - an Enterprise license is required.', 'gravityforms' );
|
||||
} elseif ( ! empty( $key ) ) {
|
||||
$error_message = esc_html__( 'Invalid or Expired Key - Please make sure you have entered the correct value and that your key is not expired.', 'gravityforms' );
|
||||
}
|
||||
|
||||
if ( ! empty( $error_message ) ) {
|
||||
|
||||
if ( empty( $response['error_messages'] ) || ! is_array( $response['error_messages'] ) ) {
|
||||
$response['error_messages'] = array();
|
||||
}
|
||||
|
||||
$response['error_messages'][] = $error_message;
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the license key deletion.
|
||||
*
|
||||
* @since 2.4.15
|
||||
*
|
||||
* @param $response
|
||||
* @param $plugin_slug
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function process_license_key_deletion( $response, $plugin_slug ) {
|
||||
|
||||
if ( $plugin_slug != 'gravityforms' ) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
if ( ! is_array( $response ) ) {
|
||||
$response = array();
|
||||
}
|
||||
|
||||
GFFormsModel::save_key( '' );
|
||||
|
||||
$response['success'] = 1;
|
||||
|
||||
if ( empty( $response['success_messages'] ) || ! is_array( $response['success_messages'] ) ) {
|
||||
$response['success_messages'] = array();
|
||||
}
|
||||
|
||||
$response['success_messages'][] = esc_html__( 'License successfully removed.', 'gravityforms' );
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers plugin pages with osDXP.
|
||||
*
|
||||
* @since 2.4.15
|
||||
*
|
||||
* @param $pages
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function settings_page( $pages ) {
|
||||
|
||||
/**
|
||||
* Setup.
|
||||
*/
|
||||
$has_full_access = current_user_can( 'gform_full_access' );
|
||||
$min_cap = GFCommon::current_user_can_which( GFCommon::all_caps() );
|
||||
if ( empty( $min_cap ) ) {
|
||||
$min_cap = 'gform_full_access';
|
||||
}
|
||||
|
||||
$parent_menu = array(
|
||||
'name' => 'gf_edit_forms',
|
||||
'callback' => array('GFForms', 'forms')
|
||||
);
|
||||
|
||||
/**
|
||||
* Remove Classic WP-Admin menu items.
|
||||
*/
|
||||
global $admin_page_hooks;
|
||||
static $removed = false;
|
||||
if ( ! $removed && is_array( $admin_page_hooks ) ) {
|
||||
$removed = true;
|
||||
remove_menu_page( $parent_menu['name'] );
|
||||
remove_submenu_page( $parent_menu['name'], 'gf_settings' );
|
||||
remove_submenu_page( $parent_menu['name'], 'gf_export' );
|
||||
remove_submenu_page( $parent_menu['name'], 'gf_addons' );
|
||||
remove_submenu_page( $parent_menu['name'], 'gf_system_status' );
|
||||
remove_submenu_page( $parent_menu['name'], 'gf_help' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add osDXP specific pages.
|
||||
*/
|
||||
// Top-level action page.
|
||||
$pages[] = array(
|
||||
'function' => $parent_menu['callback'],
|
||||
'type' => 'menu',
|
||||
'menu_slug' => $parent_menu['name'],
|
||||
'page_title' => esc_html__( 'Forms', 'gravityforms' ),
|
||||
'menu_title' => esc_html__( 'Forms', 'gravityforms' ),
|
||||
'capability' => $has_full_access ? 'gform_full_access' : $min_cap,
|
||||
'icon_url' => GFForms::get_admin_icon_b64( '#FFF' ),
|
||||
);
|
||||
// Settings page.
|
||||
$pages[] = array(
|
||||
'function' => array( 'GFForms', 'settings_page' ),
|
||||
'menu_slug' => 'gf_settings',
|
||||
'page_title' => esc_html__( 'Form Settings', 'gravityforms' ),
|
||||
'menu_title' => esc_html__( 'Form Settings', 'gravityforms' ),
|
||||
'capability' => $has_full_access ? 'gform_full_access' : 'gravityforms_view_settings',
|
||||
);
|
||||
// Export page.
|
||||
$pages[] = array(
|
||||
'function' => array( 'GFForms', 'export_page' ),
|
||||
'menu_slug' => 'gf_export',
|
||||
'type' => 'endpoint',
|
||||
'page_title' => esc_html__( 'Import/Export', 'gravityforms' ),
|
||||
'menu_title' => esc_html__( 'Import/Export', 'gravityforms' ),
|
||||
'capability' => $has_full_access ? 'gform_full_access' : ( current_user_can( 'gravityforms_export_entries' ) ? 'gravityforms_export_entries' : 'gravityforms_edit_forms' ),
|
||||
);
|
||||
if ( current_user_can( 'install_plugins' ) ) {
|
||||
// Add-ons page.
|
||||
$pages[] = array(
|
||||
'function' => array( 'GFForms', 'addons_page' ),
|
||||
'menu_slug' => 'gf_addons',
|
||||
'type' => 'endpoint',
|
||||
'page_title' => esc_html__( 'Add-Ons', 'gravityforms' ),
|
||||
'menu_title' => esc_html__( 'Add-Ons', 'gravityforms' ),
|
||||
'capability' => $has_full_access ? 'gform_full_access' : 'gravityforms_view_addons',
|
||||
);
|
||||
}
|
||||
// System status page.
|
||||
$pages[] = array(
|
||||
'function' => array( 'GFForms', 'system_status' ),
|
||||
'menu_slug' => 'gf_system_status',
|
||||
'type' => 'endpoint',
|
||||
'page_title' => esc_html__( 'System Status', 'gravityforms' ),
|
||||
'menu_title' => esc_html__( 'System Status', 'gravityforms' ),
|
||||
'capability' => $has_full_access ? 'gform_full_access' : 'gravityforms_system_status',
|
||||
);
|
||||
// Help page.
|
||||
$pages[] = array(
|
||||
'function' => array( 'GFForms', 'help_page' ),
|
||||
'menu_slug' => 'gf_help',
|
||||
'type' => 'endpoint',
|
||||
'page_title' => esc_html__( 'Help', 'gravityforms' ),
|
||||
'menu_title' => esc_html__( 'Help', 'gravityforms' ),
|
||||
'capability' => $has_full_access ? 'gform_full_access' : $min_cap,
|
||||
);
|
||||
|
||||
return $pages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to check if a page slug is a setting/misc page.
|
||||
*
|
||||
* @since 2.4.15
|
||||
*
|
||||
* @param $plugin_page
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function is_setting_page( $plugin_page ) {
|
||||
return array_key_exists( $plugin_page, $this->slugs );
|
||||
}
|
||||
|
||||
/**
|
||||
* Highlights appropriate menu item for misc pages.
|
||||
*
|
||||
* @since 2.4.15
|
||||
*
|
||||
* @param $file
|
||||
*
|
||||
* @return $file
|
||||
*/
|
||||
public function settings_page_highlight( $file ) {
|
||||
global $plugin_page, $submenu_file;
|
||||
|
||||
if ( $this->is_setting_page( $plugin_page ) ) {
|
||||
$file = 'dxp-module-settings';
|
||||
$submenu_file = 'gf_settings';
|
||||
}
|
||||
|
||||
return $file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs Nav Tabs for settings&misc pages.
|
||||
*
|
||||
* @since 2.4.15
|
||||
*/
|
||||
public function nav_tabs() {
|
||||
global $plugin_page;
|
||||
|
||||
if ( $this->is_setting_page( $plugin_page ) ) {
|
||||
?>
|
||||
<ul class="osdxp-nav-tabs">
|
||||
<?php
|
||||
foreach ( $this->slugs as $path => $name ) {
|
||||
?>
|
||||
<li <?php echo ( $path === $plugin_page ) ? 'class="active"' : '' ?>>
|
||||
<a href="<?php echo esc_url_raw( admin_url( 'admin.php?page=' . $path ) ); ?>">
|
||||
<?php echo esc_html( $name ); ?>
|
||||
</a>
|
||||
</li>
|
||||
<?php
|
||||
} ?>
|
||||
</ul>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GF_OSDXP::get_instance();
|
||||
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
namespace Gravity_Forms\Gravity_Forms;
|
||||
|
||||
/**
|
||||
* Class GF_Service_Container
|
||||
*
|
||||
* A simple Service Container used to collect and organize Services used by the application and its modules.
|
||||
*
|
||||
* @since 2.5
|
||||
*
|
||||
* @package Gravity_Forms\Gravity_Forms
|
||||
*/
|
||||
class GF_Service_Container {
|
||||
|
||||
private $services = array();
|
||||
private $providers = array();
|
||||
|
||||
/**
|
||||
* Add a service to the container.
|
||||
*
|
||||
* @since 2.5
|
||||
*
|
||||
* @param string $name The service Name
|
||||
* @param mixed $service The service to add
|
||||
*/
|
||||
public function add( $name, $service, $defer = false ) {
|
||||
if ( empty( $name ) ) {
|
||||
$name = get_class( $service );
|
||||
}
|
||||
|
||||
if ( ! $defer && is_callable( $service ) ) {
|
||||
$service = $service();
|
||||
}
|
||||
|
||||
$this->services[ $name ] = $service;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a service from the container.
|
||||
*
|
||||
* @since 2.5
|
||||
*
|
||||
* @param string $name The service name.
|
||||
*/
|
||||
public function remove( $name ) {
|
||||
unset( $this->services[ $name ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a service from the container by name.
|
||||
*
|
||||
* @since 2.5
|
||||
*
|
||||
* @param string $name The service name.
|
||||
*
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function get( $name ) {
|
||||
if ( ! isset( $this->services[ $name ] ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( is_callable( $this->services[ $name ] ) ) {
|
||||
$called = $this->services[ $name ]();
|
||||
$this->services[ $name ] = $called;
|
||||
}
|
||||
|
||||
return $this->services[ $name ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a service provider to the container and register each of its services.
|
||||
*
|
||||
* @since 2.5
|
||||
*
|
||||
* @param GF_Service_Provider $provider
|
||||
*/
|
||||
public function add_provider( GF_Service_Provider $provider ) {
|
||||
$provider_name = get_class( $provider );
|
||||
|
||||
// Only add providers a single time.
|
||||
if ( isset( $this->providers[ $provider_name ] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->providers[ $provider_name ] = $provider;
|
||||
|
||||
$provider->set_container( $this );
|
||||
$provider->register( $this );
|
||||
$provider->init( $this );
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
namespace Gravity_Forms\Gravity_Forms;
|
||||
|
||||
use Gravity_Forms\Gravity_Forms\Config\GF_App_Config;
|
||||
use Gravity_Forms\Gravity_Forms\Config\GF_Config_Service_Provider;
|
||||
|
||||
/**
|
||||
* Class GF_Service_Provider
|
||||
*
|
||||
* An abstraction which provides a contract for defining Service Providers. Service Providers facilitate
|
||||
* organizing Services into discreet modules, as opposed to having to register each service in a single location.
|
||||
*
|
||||
* @since 2.5
|
||||
*
|
||||
* @package Gravity_Forms\Gravity_Forms
|
||||
*/
|
||||
abstract class GF_Service_Provider {
|
||||
|
||||
/**
|
||||
* @var GF_Service_Container $container
|
||||
*/
|
||||
protected $container;
|
||||
|
||||
public function set_container( GF_Service_Container $container ) {
|
||||
$this->container = $container;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register new services to the Service Container.
|
||||
*
|
||||
* @param GF_Service_Container $container
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
abstract public function register( GF_Service_Container $container );
|
||||
|
||||
/**
|
||||
* Noop by default - used to initialize hooks and filters for the given module.
|
||||
*/
|
||||
public function init( GF_Service_Container $container ) {}
|
||||
|
||||
//----------------------------------------
|
||||
//---------- App Registration ------------
|
||||
//----------------------------------------
|
||||
|
||||
/**
|
||||
* Register a JS app with the given arguments.
|
||||
*
|
||||
* @since 2.7.1
|
||||
*
|
||||
* @param array $args
|
||||
*/
|
||||
public function register_app( $args ) {
|
||||
$config = new GF_App_Config( $this->container->get( GF_Config_Service_Provider::DATA_PARSER ) );
|
||||
$config->set_data( $args );
|
||||
|
||||
$this->container->get( GF_Config_Service_Provider::CONFIG_COLLECTION )->add_config( $config );
|
||||
|
||||
$should_display = is_callable( $args['enqueue'] ) ? call_user_func( $args['enqueue'] ) : $args['enqueue'];
|
||||
|
||||
if ( ! $should_display ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! empty( $args['css'] ) ) {
|
||||
$this->enqueue_app_css( $args );
|
||||
}
|
||||
|
||||
if ( ! empty( $args['root_element'] ) ) {
|
||||
$this->add_root_element( $args['root_element'] );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue the CSS assets for the app.
|
||||
*
|
||||
* @since 2.7.1
|
||||
*
|
||||
* @param $args
|
||||
*/
|
||||
protected function enqueue_app_css( $args ) {
|
||||
$css_asset = $args['css'];
|
||||
|
||||
add_action( 'wp_enqueue_scripts', function () use ( $css_asset ) {
|
||||
call_user_func_array( 'wp_enqueue_style', $css_asset );
|
||||
} );
|
||||
|
||||
add_action( 'admin_enqueue_scripts', function () use ( $css_asset ) {
|
||||
call_user_func_array( 'wp_enqueue_style', $css_asset );
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the root element to the footer output for bootstrapping.
|
||||
*
|
||||
* @since 2.7.1
|
||||
*
|
||||
* @param string $root
|
||||
*/
|
||||
protected function add_root_element( $root ) {
|
||||
add_action( 'admin_footer', function() use ( $root ) {
|
||||
echo '<div data-js="' . esc_attr( $root ) . '"></div>';
|
||||
}, 10, 0 );
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,588 @@
|
||||
<?php
|
||||
|
||||
if ( ! class_exists( 'GFForms' ) ) {
|
||||
die();
|
||||
}
|
||||
|
||||
if ( ! defined( 'GRAVITY_API_URL' ) ) {
|
||||
define( 'GRAVITY_API_URL', 'https://gravityapi.com/wp-json/gravityapi/v1' );
|
||||
}
|
||||
|
||||
if ( ! class_exists( 'Gravity_Api' ) ) {
|
||||
|
||||
/**
|
||||
* Client-side API wrapper for interacting with the Gravity APIs.
|
||||
*
|
||||
* @package Gravity Forms
|
||||
* @subpackage Gravity_Api
|
||||
* @since 1.9
|
||||
* @access public
|
||||
*/
|
||||
class Gravity_Api {
|
||||
|
||||
private static $instance = null;
|
||||
|
||||
private static $raw_response = null;
|
||||
|
||||
public static function get_instance() {
|
||||
if ( null == self::$instance ) {
|
||||
self::$instance = new self;
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves site key and site secret key from remote API and stores them as WP options. Returns false if license key is invalid; otherwise, returns true.
|
||||
*
|
||||
* @since 2.3
|
||||
*
|
||||
* @param string $license_key License key to be registered.
|
||||
* @param boolean $is_md5 Specifies if $license_key provided is an MD5 or unhashed license key.
|
||||
*
|
||||
* @return bool|WP_Error
|
||||
*/
|
||||
public function register_current_site( $license_key, $is_md5 = false ) {
|
||||
|
||||
$body = array();
|
||||
$body['site_name'] = get_bloginfo( 'name' );
|
||||
$body['site_url'] = get_bloginfo( 'url' );
|
||||
|
||||
if ( $is_md5 ) {
|
||||
$body['license_key_md5'] = $license_key;
|
||||
} else {
|
||||
$body['license_key'] = $license_key;
|
||||
}
|
||||
|
||||
GFCommon::log_debug( __METHOD__ . '(): registering site' );
|
||||
|
||||
$result = $this->request( 'sites', $body, 'POST', array( 'headers' => $this->get_license_auth_header( $license_key ) ) );
|
||||
$result = $this->prepare_response_body( $result, true );
|
||||
|
||||
if ( is_wp_error( $result ) ) {
|
||||
GFCommon::log_error( __METHOD__ . '(): error registering site. ' . $result->get_error_message() );
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
update_option( 'gf_site_key', $result['key'] );
|
||||
update_option( 'gf_site_secret', $result['secret'] );
|
||||
|
||||
GFCommon::log_debug( __METHOD__ . '(): site registration successful. Site Key: ' . $result['key'] );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates license key for a site that has already been registered.
|
||||
*
|
||||
* @since 2.3
|
||||
* @since 2.5 Returns License Response on success.
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @param string $new_license_key_md5 Hash license key to be updated
|
||||
*
|
||||
* @return \Gravity_Forms\Gravity_Forms\License\GF_License_API_Response|WP_Error
|
||||
*/
|
||||
public function update_current_site( $new_license_key_md5 ) {
|
||||
|
||||
$site_key = $this->get_site_key();
|
||||
$site_secret = $this->get_site_secret();
|
||||
if ( empty( $site_key ) || empty( $site_secret ) ) {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$body = GFCommon::get_remote_post_params();
|
||||
$body['site_name'] = get_bloginfo( 'name' );
|
||||
$body['site_url'] = get_bloginfo( 'url' );
|
||||
$body['site_key'] = $site_key;
|
||||
$body['site_secret'] = $site_secret;
|
||||
$body['license_key_md5'] = $new_license_key_md5;
|
||||
|
||||
GFCommon::log_debug( __METHOD__ . '(): refreshing license info' );
|
||||
|
||||
$result = $this->request( 'sites/' . $site_key, $body, 'PUT', array( 'headers' => $this->get_site_auth_header( $site_key, $site_secret ) ) );
|
||||
$result = $this->prepare_response_body( $result, true );
|
||||
|
||||
if ( is_wp_error( $result ) ) {
|
||||
|
||||
GFCommon::log_debug( __METHOD__ . '(): error updating site registration. ' . print_r( $result, true ) );
|
||||
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/***
|
||||
* Removes a license key from a registered site. NOTE: It doesn't actually deregister the site.
|
||||
*
|
||||
* @deprecated Use gapi()->update_current_site('') instead.
|
||||
*
|
||||
* @return bool|WP_Error
|
||||
*/
|
||||
public function deregister_current_site() {
|
||||
|
||||
$site_key = $this->get_site_key();
|
||||
$site_secret = $this->get_site_secret();
|
||||
|
||||
if ( empty( $site_key ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
GFCommon::log_debug( __METHOD__ . '(): deregistering' );
|
||||
|
||||
$body = array(
|
||||
'license_key_md5' => '',
|
||||
);
|
||||
|
||||
$result = $this->request( 'sites/' . $site_key, $body, 'PUT', array( 'headers' => $this->get_site_auth_header( $site_key, $site_secret ) ) );
|
||||
$result = $this->prepare_response_body( $result, true );
|
||||
|
||||
if ( is_wp_error( $result ) ) {
|
||||
|
||||
GFCommon::log_debug( __METHOD__ . '(): error updating site registration. ' . print_r( $result, true ) );
|
||||
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the given license key to get its information from the API.
|
||||
*
|
||||
* @since 2.5
|
||||
*
|
||||
* @param string $key The license key.
|
||||
*
|
||||
* @return array|false|WP_Error
|
||||
*/
|
||||
public function check_license( $key ) {
|
||||
|
||||
GFCommon::log_debug( __METHOD__ . '(): getting site and license info' );
|
||||
|
||||
$params = array(
|
||||
'site_url' => get_option( 'home' ),
|
||||
'is_multisite' => is_multisite(),
|
||||
);
|
||||
|
||||
$resource = 'licenses/' . $key . '/check?' . build_query( $params );
|
||||
$result = $this->request( $resource, null );
|
||||
$result = $this->prepare_response_body( $result, true );
|
||||
|
||||
if ( is_wp_error( $result ) ) {
|
||||
|
||||
GFCommon::log_debug( __METHOD__ . '(): error getting site and license information. ' . $result->get_error_message() );
|
||||
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
$response = $result;
|
||||
|
||||
if ( rgar( $result, 'license' ) ) {
|
||||
$response = rgar( $result, 'license' );
|
||||
}
|
||||
|
||||
// Set the license object to the transient.
|
||||
set_transient( 'rg_gforms_license', $response, DAY_IN_SECONDS );
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get GF core and add-on family information.
|
||||
*
|
||||
* @since 2.5
|
||||
*
|
||||
* @return false|array
|
||||
*/
|
||||
public function get_plugins_info() {
|
||||
$version_info = $this->get_version_info();
|
||||
|
||||
if ( empty( $version_info['offerings'] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $version_info['offerings'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get version information from the Gravity Manager API.
|
||||
*
|
||||
* @since 2.5
|
||||
*
|
||||
* @param false $cache
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function get_version_info( $cache = false ) {
|
||||
|
||||
$version_info = null;
|
||||
|
||||
if ( $cache ) {
|
||||
$cached_info = get_option( 'gform_version_info' );
|
||||
|
||||
// Checking cache expiration
|
||||
$cache_duration = DAY_IN_SECONDS; // 24 hours.
|
||||
$cache_timestamp = $cached_info && isset( $cached_info['timestamp'] ) ? $cached_info['timestamp'] : 0;
|
||||
|
||||
// Is cache expired? If not, set $version_info to the cached data.
|
||||
if ( $cache_timestamp + $cache_duration >= time() ) {
|
||||
$version_info = $cached_info;
|
||||
}
|
||||
}
|
||||
|
||||
if ( is_wp_error( $version_info ) || isset( $version_info['headers'] ) ) {
|
||||
// Legacy ( < 2.1.1.14 ) version info contained the whole raw response.
|
||||
$version_info = null;
|
||||
}
|
||||
|
||||
// If we reach this point with a $version_info array, it's from cache, and we can return it.
|
||||
if ( $version_info ) {
|
||||
return $version_info;
|
||||
}
|
||||
|
||||
//Getting version number
|
||||
$options = array(
|
||||
'method' => 'POST',
|
||||
'timeout' => 20,
|
||||
);
|
||||
|
||||
$options['headers'] = array(
|
||||
'Content-Type' => 'application/x-www-form-urlencoded; charset=' . get_option( 'blog_charset' ),
|
||||
'User-Agent' => 'WordPress/' . get_bloginfo( 'version' ),
|
||||
);
|
||||
|
||||
$options['body'] = GFCommon::get_remote_post_params();
|
||||
$options['timeout'] = 15;
|
||||
|
||||
$nocache = $cache ? '' : 'nocache=1'; //disabling server side caching
|
||||
|
||||
// Check if API is currently blocked due to previous 500 errors.
|
||||
$api_blocked = get_transient( 'gf_api_version_blocked' );
|
||||
if ( $api_blocked ) {
|
||||
GFCommon::log_debug( __METHOD__ . '(): API calls to version.php are temporarily blocked due to previous server errors.' );
|
||||
// Use a WP_Error to indicate API is temporarily blocked
|
||||
self::$raw_response = new WP_Error( 'api_blocked', 'API temporarily blocked due to previous server errors' );
|
||||
} else {
|
||||
// Store the raw_response for this page load. This will keep us from hitting the api multiple times per pageload.
|
||||
if ( is_null( self::$raw_response ) ) {
|
||||
self::$raw_response = GFCommon::post_to_manager( 'version.php', $nocache, $options );
|
||||
}
|
||||
}
|
||||
|
||||
$raw_response = self::$raw_response;;
|
||||
$version_info = array(
|
||||
'is_valid_key' => '1',
|
||||
'version' => '',
|
||||
'url' => '',
|
||||
'is_error' => '1',
|
||||
);
|
||||
|
||||
if ( is_wp_error( $raw_response ) || rgars( $raw_response, 'response/code' ) != 200 ) {
|
||||
// Check if this is a 5xx server error and set a transient to block future requests for 30 minutes.
|
||||
$response_code = rgars( $raw_response, 'response/code' );
|
||||
if ( $response_code >= 500 && $response_code < 600 ) {
|
||||
set_transient( 'gf_api_version_blocked', true, 30 * MINUTE_IN_SECONDS );
|
||||
GFCommon::log_error( __METHOD__ . "(): {$response_code} error received from version.php API. Blocking further calls for 30 minutes." );
|
||||
}
|
||||
|
||||
$version_info['timestamp'] = time();
|
||||
|
||||
return $version_info;
|
||||
}
|
||||
|
||||
$decoded = json_decode( $raw_response['body'], true );
|
||||
|
||||
if ( empty( $decoded ) ) {
|
||||
$version_info['timestamp'] = time();
|
||||
|
||||
return $version_info;
|
||||
}
|
||||
|
||||
$decoded['timestamp'] = time();
|
||||
|
||||
// Caching response.
|
||||
update_option( 'gform_version_info', $decoded, false ); //caching version info
|
||||
|
||||
return $decoded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the usage data (call version.php in Gravity Manager). We will replace it once we have statistics API endpoints.
|
||||
*
|
||||
* @since 2.5
|
||||
*/
|
||||
public function update_site_data() {
|
||||
|
||||
// Whenever we update the plugins info, we call the versions.php to update usage data.
|
||||
$options = array( 'method' => 'POST' );
|
||||
$options['headers'] = array(
|
||||
'Content-Type' => 'application/x-www-form-urlencoded; charset=' . get_option( 'blog_charset' ),
|
||||
'User-Agent' => 'WordPress/' . get_bloginfo( 'version' ),
|
||||
'Referer' => get_bloginfo( 'url' ),
|
||||
);
|
||||
$options['body'] = GFCommon::get_remote_post_params();
|
||||
// Set the version to 3 which lightens the burden of version.php, it won't return anything to us anymore.
|
||||
$options['body']['version'] = '3';
|
||||
$options['timeout'] = 15;
|
||||
|
||||
$nocache = 'nocache=1'; //disabling server side caching
|
||||
|
||||
// Check if API is currently blocked due to previous 500 errors.
|
||||
$api_blocked = get_transient( 'gf_api_version_blocked' );
|
||||
if ( $api_blocked ) {
|
||||
GFCommon::log_debug( __METHOD__ . '(): API calls to version.php are temporarily blocked due to previous server errors.' );
|
||||
return;
|
||||
}
|
||||
|
||||
$response = GFCommon::post_to_manager( 'version.php', $nocache, $options );
|
||||
|
||||
// Check if this is a 5xx server error
|
||||
$response_code = rgars( $response, 'response/code' );
|
||||
if ( $response_code >= 500 && $response_code < 600 ) {
|
||||
set_transient( 'gf_api_version_blocked', true, 30 * MINUTE_IN_SECONDS );
|
||||
GFCommon::log_error( __METHOD__ . "(): {$response_code} error received from version.php API. Blocking further calls for 30 minutes." );
|
||||
}
|
||||
}
|
||||
|
||||
public function send_email_to_hubspot( $email ) {
|
||||
GFCommon::log_debug( __METHOD__ . '(): Sending installation wizard to hubspot.' );
|
||||
|
||||
$body = array(
|
||||
'email' => $email,
|
||||
);
|
||||
|
||||
$result = $this->request( 'emails/installation/add-to-list', $body, 'POST', array( 'headers' => $this->get_license_info_header( $site_secret ) ) );
|
||||
$result = $this->prepare_response_body( $result, true );
|
||||
|
||||
if ( is_wp_error( $result ) ) {
|
||||
GFCommon::log_debug( __METHOD__ . '(): error sending installation wizard to hubspot. ' . print_r( $result, true ) );
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// # HELPERS
|
||||
|
||||
/**
|
||||
* @return false|mixed|void
|
||||
*/
|
||||
public function get_key() {
|
||||
return GFCommon::get_key();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $site_key
|
||||
* @param $site_secret
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
private function get_site_auth_header( $site_key, $site_secret ) {
|
||||
|
||||
$auth = base64_encode( "{$site_key}:{$site_secret}" );
|
||||
|
||||
return array( 'Authorization' => 'GravityAPI ' . $auth );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $site_secret
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
private function get_license_info_header( $site_secret ) {
|
||||
$auth = base64_encode( "gravityforms.com:{$site_secret}" );
|
||||
|
||||
return array( 'Authorization' => 'GravityAPI ' . $auth );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $license_key_md5
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
private function get_license_auth_header( $license_key_md5 ) {
|
||||
|
||||
$auth = base64_encode( "license:{$license_key_md5}" );
|
||||
|
||||
return array( 'Authorization' => 'GravityAPI ' . $auth );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare response body.
|
||||
*
|
||||
* @since unknown
|
||||
* @since 2.5 Support a WP_Error being returned.
|
||||
* @since 2.5 Allow results to be returned as array with second param.
|
||||
*
|
||||
* @param WP_Error|WP_REST_Response $raw_response The API response.
|
||||
* @param bool $as_array Whether to return the response as an array or object.
|
||||
*
|
||||
* @return array|object|WP_Error
|
||||
*/
|
||||
public function prepare_response_body( $raw_response, $as_array = false ) {
|
||||
|
||||
if ( is_wp_error( $raw_response ) ) {
|
||||
return $raw_response;
|
||||
}
|
||||
|
||||
$response_body = json_decode( wp_remote_retrieve_body( $raw_response ), $as_array );
|
||||
$response_code = wp_remote_retrieve_response_code( $raw_response );
|
||||
$response_message = wp_remote_retrieve_response_message( $raw_response );
|
||||
|
||||
if ( $response_code > 200 ) {
|
||||
|
||||
// If a WP_Error was returned in the body.
|
||||
if ( rgar( $response_body, 'code' ) ) {
|
||||
|
||||
// Restore the WP_Error.
|
||||
$error = new WP_Error( $response_body['code'], $response_body['message'], $response_body['data'] );
|
||||
} else {
|
||||
$error_code = $response_code == 429 ? 'http_request_blocked' : 'http_request_failed';
|
||||
$error = new WP_Error( $error_code, 'Error from server: ' . $response_message );
|
||||
}
|
||||
|
||||
return $error;
|
||||
|
||||
}
|
||||
|
||||
return $response_body;
|
||||
}
|
||||
|
||||
/**
|
||||
* Purge the site credentials.
|
||||
*
|
||||
* @since unknown
|
||||
* @since 2.5 Added the deletion of the gf_site_registered option.
|
||||
*/
|
||||
public function purge_site_credentials() {
|
||||
|
||||
delete_option( 'gf_site_key' );
|
||||
delete_option( 'gf_site_secret' );
|
||||
delete_option( 'gf_site_registered' );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Making API requests.
|
||||
*
|
||||
* @since unknown
|
||||
* @since 2.5 Purge the registration data on site if certain errors matched.
|
||||
*
|
||||
* @param string $resource The API route.
|
||||
* @param array $body The request body.
|
||||
* @param string $method The method.
|
||||
* @param array $options The options.
|
||||
*
|
||||
* @return array|WP_Error
|
||||
*/
|
||||
public function request( $resource, $body, $method = 'POST', $options = array() ) {
|
||||
$body['timestamp'] = time();
|
||||
|
||||
// set default options
|
||||
$options = wp_parse_args( $options, array(
|
||||
'method' => $method,
|
||||
'timeout' => 10,
|
||||
'body' => in_array( $method, array( 'GET', 'DELETE' ) ) ? null : json_encode( $body ),
|
||||
'headers' => array(),
|
||||
'sslverify' => true,
|
||||
) );
|
||||
|
||||
// set default header options
|
||||
$options['headers'] = wp_parse_args( $options['headers'], array(
|
||||
'Content-Type' => 'application/json; charset=' . get_option( 'blog_charset' ),
|
||||
'User-Agent' => 'WordPress/' . get_bloginfo( 'version' ),
|
||||
'Referer' => get_bloginfo( 'url' ),
|
||||
) );
|
||||
|
||||
// WP docs say method should be uppercase
|
||||
$options['method'] = strtoupper( $options['method'] );
|
||||
|
||||
$request_url = $this->get_gravity_api_url() . $resource;
|
||||
|
||||
return wp_remote_request( $request_url, $options );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return false|mixed|void
|
||||
*/
|
||||
public function get_site_key() {
|
||||
|
||||
if ( defined( 'GRAVITY_API_SITE_KEY' ) ) {
|
||||
return GRAVITY_API_SITE_KEY;
|
||||
}
|
||||
|
||||
$site_key = get_option( 'gf_site_key' );
|
||||
if ( empty( $site_key ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $site_key;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return false|mixed|void
|
||||
*/
|
||||
public function get_site_secret() {
|
||||
if ( defined( 'GRAVITY_API_SITE_SECRET' ) ) {
|
||||
return GRAVITY_API_SITE_SECRET;
|
||||
}
|
||||
$site_secret = get_option( 'gf_site_secret' );
|
||||
if ( empty( $site_secret ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $site_secret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function get_gravity_api_url() {
|
||||
return trailingslashit( GRAVITY_API_URL );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the site has the gf_site_key and gf_site_secret options.
|
||||
*
|
||||
* @since unknown
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_site_registered() {
|
||||
return $this->get_site_key() && $this->get_site_secret();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the site has the gf_site_key, gf_site_secret and also the gf_site_registered options.
|
||||
*
|
||||
* @since 2.5
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_legacy_registration() {
|
||||
|
||||
return $this->is_site_registered() && ! get_option( 'gf_site_registered' );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function gapi() {
|
||||
return Gravity_Api::get_instance();
|
||||
}
|
||||
|
||||
gapi();
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,483 @@
|
||||
<?php
|
||||
|
||||
namespace Gravity_Forms\Gravity_Forms;
|
||||
|
||||
use GFCommon;
|
||||
|
||||
/**
|
||||
* Allows to download translations from TranslationsPress
|
||||
* This is a modified version of the library available at https://github.com/WP-Translations/t15s-registry
|
||||
* This version aims to be compatible with PHP 5.2, and supports only plugins.
|
||||
*
|
||||
* @since 2.5
|
||||
*/
|
||||
class TranslationsPress_Updater {
|
||||
|
||||
const T15S_TRANSIENT_KEY = 't15s-registry-gforms';
|
||||
const T15S_API_URL = 'https://packages.translationspress.com/rocketgenius/packages.json';
|
||||
|
||||
/**
|
||||
* The plugin slug.
|
||||
*
|
||||
* @since 2.5
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $slug = '';
|
||||
|
||||
/**
|
||||
* The locales installed during the current request.
|
||||
*
|
||||
* @since 2.5.6
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $installed = array();
|
||||
|
||||
/**
|
||||
* Cached TranslationsPress data for all Rocketgenius plugins.
|
||||
*
|
||||
* @since 2.5.6
|
||||
*
|
||||
* @var null|object
|
||||
*/
|
||||
private static $all_translations;
|
||||
|
||||
/**
|
||||
* The current instances of this class with the slugs as the keys.
|
||||
*
|
||||
* @since 2.5.6
|
||||
*
|
||||
* @var TranslationsPress_Updater[]
|
||||
*/
|
||||
private static $_instances = array();
|
||||
|
||||
/**
|
||||
* Returns an instance of this class for the given slug.
|
||||
*
|
||||
* @since 2.5.6
|
||||
*
|
||||
* @param string $slug The plugin slug.
|
||||
*
|
||||
* @return TranslationsPress_Updater
|
||||
*/
|
||||
public static function get_instance( $slug ) {
|
||||
if ( empty( self::$_instances[ $slug ] ) ) {
|
||||
self::$_instances[ $slug ] = new self( $slug );
|
||||
}
|
||||
|
||||
return self::$_instances[ $slug ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new project to load translations for.
|
||||
*
|
||||
* @since 2.5
|
||||
*
|
||||
* @param string $slug The plugin slug.
|
||||
* @param string $deprecated Not used. Previously, the locale to be installed.
|
||||
*/
|
||||
public function __construct( $slug, $deprecated = '' ) {
|
||||
$this->slug = $slug;
|
||||
|
||||
if ( 'gravityforms' === $slug ) {
|
||||
// Translations data for all Rocketgenius plugins is stored together so we only need to add this hook once.
|
||||
add_action( 'delete_site_transient_update_plugins', array( __CLASS__, 'refresh_all_translations' ) );
|
||||
}
|
||||
|
||||
add_action( 'gform_post_install', array( $this, 'install' ), 10, 0 );
|
||||
add_action( 'gform_post_upgrade', array( $this, 'install' ), 10, 0 );
|
||||
add_action( 'upgrader_process_complete', array( $this, 'upgrader_process_complete' ), 10, 2 );
|
||||
|
||||
add_filter( 'translations_api', array( $this, 'translations_api' ), 10, 3 );
|
||||
add_filter( 'pre_set_site_transient_update_plugins', array( $this, 'site_transient_update_plugins' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Short-circuits translations API requests for private projects.
|
||||
*
|
||||
* @since 2.5
|
||||
*
|
||||
* @param bool|array $result The result object. Default false.
|
||||
* @param string $requested_type The type of translations being requested.
|
||||
* @param object $args Translation API arguments.
|
||||
*
|
||||
* @return bool|array
|
||||
*/
|
||||
public function translations_api( $result, $requested_type, $args ) {
|
||||
if ( 'plugins' !== $requested_type || $this->slug !== $args['slug'] ) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
return $this->get_plugin_translations();
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the translations transients to include the current plugin.
|
||||
*
|
||||
* @see wp_get_translation_updates()
|
||||
*
|
||||
* @since 2.5
|
||||
*
|
||||
* @param mixed $value The transient value.
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public function site_transient_update_plugins( $value ) {
|
||||
if ( ! $value ) {
|
||||
$value = new \stdClass();
|
||||
}
|
||||
|
||||
if ( ! isset( $value->translations ) ) {
|
||||
$value->translations = array();
|
||||
}
|
||||
|
||||
$translations = $this->get_plugin_translations();
|
||||
|
||||
if ( empty( $translations['translations'] ) ) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
foreach ( $translations['translations'] as $translation ) {
|
||||
if ( ! $this->should_install( $translation ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$translation['type'] = 'plugin';
|
||||
$translation['slug'] = $this->slug;
|
||||
|
||||
$value->translations[] = $translation;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the TranslationsPress data for the current plugin.
|
||||
*
|
||||
* @since 2.5
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function get_plugin_translations() {
|
||||
self::set_all_translations();
|
||||
|
||||
return (array) rgar( self::$all_translations->projects, $this->slug );
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the cached TranslationsPress data, if expired.
|
||||
*
|
||||
* @since 2.5.6
|
||||
*/
|
||||
public static function refresh_all_translations() {
|
||||
static $done;
|
||||
|
||||
if ( $done ) {
|
||||
return;
|
||||
}
|
||||
|
||||
self::$all_translations = null;
|
||||
self::set_all_translations();
|
||||
$done = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the cached TranslationsPress data needs refreshing.
|
||||
*
|
||||
* @since 2.5.6
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private static function is_transient_expired() {
|
||||
$cache_lifespan = 12 * HOUR_IN_SECONDS;
|
||||
|
||||
return ! isset( self::$all_translations->_last_checked ) || ( time() - self::$all_translations->_last_checked ) > $cache_lifespan;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the translations data from the TranslationsPress API.
|
||||
*
|
||||
* @since 2.5.6
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private static function get_remote_translations_data() {
|
||||
$result = json_decode( wp_remote_retrieve_body( wp_remote_get( self::T15S_API_URL, array( 'timeout' => 3 ) ) ), true );
|
||||
|
||||
return is_array( $result ) ? $result : array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Caches the TranslationsPress data, if not already cached.
|
||||
*
|
||||
* @since 2.5.6
|
||||
*/
|
||||
private static function set_all_translations() {
|
||||
if ( is_object( self::$all_translations ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
self::$all_translations = get_site_transient( self::T15S_TRANSIENT_KEY );
|
||||
if ( is_object( self::$all_translations ) && ! self::is_transient_expired() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
self::$all_translations = new \stdClass();
|
||||
self::$all_translations->projects = self::get_remote_translations_data();
|
||||
self::$all_translations->_last_checked = time();
|
||||
set_site_transient( self::T15S_TRANSIENT_KEY, self::$all_translations );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the translations for a given project.
|
||||
*
|
||||
* @since 2.5
|
||||
*
|
||||
* @deprecated 2.5.6
|
||||
*
|
||||
* @param string $url Full GlotPress API URL for the project.
|
||||
*
|
||||
* @return array Translation data.
|
||||
*/
|
||||
public static function get_translations( $url ) {
|
||||
_deprecated_function( __METHOD__, '2.5.6', '\Gravity_Forms\Gravity_Forms\TranslationsPress_Updater::get_plugin_translations' );
|
||||
self::set_all_translations();
|
||||
|
||||
return self::$all_translations->projects;
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads and installs the translations for the specified plugin.
|
||||
*
|
||||
* @since 2.5
|
||||
*
|
||||
* @param string $slug The plugin slug.
|
||||
* @param string $locale The locale when the site locale is changed or an empty string to install all the user available locales.
|
||||
*/
|
||||
public static function download_package( $slug, $locale = '' ) {
|
||||
self::get_instance( $slug )->install( $locale );
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers translation installation, if required.
|
||||
*
|
||||
* @since 2.5.6
|
||||
*
|
||||
* @param string $locale The locale when the site locale is changed or an empty string to install all the user available locales.
|
||||
*/
|
||||
public function install( $locale = '' ) {
|
||||
if ( $locale && in_array( $locale, $this->installed ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$translations = $this->get_plugin_translations();
|
||||
|
||||
if ( empty( $translations['translations'] ) ) {
|
||||
GFCommon::log_error( __METHOD__ . sprintf( '(): Aborting; No translations list for %s.', $this->slug ) );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ( $translations['translations'] as $translation ) {
|
||||
if ( ! $this->should_install( $translation, $locale ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->install_translation( $translation );
|
||||
|
||||
if ( $locale ) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads and installs the given translation.
|
||||
*
|
||||
* @since 2.5.6
|
||||
*
|
||||
* @param array $translation The translation data.
|
||||
*/
|
||||
private function install_translation( $translation ) {
|
||||
global $wp_filesystem;
|
||||
|
||||
if ( ! $wp_filesystem ) {
|
||||
require_once ABSPATH . '/wp-admin/includes/admin.php';
|
||||
|
||||
// Same approach as in WP Core: https://github.com/WordPress/wordpress-develop/blob/6.2/src/wp-includes/rest-api/endpoints/class-wp-rest-plugins-controller.php#L853-L869.
|
||||
ob_start();
|
||||
$filesystem_credentials_available = request_filesystem_credentials( self_admin_url() );
|
||||
ob_end_clean();
|
||||
|
||||
if ( ! $filesystem_credentials_available ) {
|
||||
GFCommon::log_error( __METHOD__ . '(): Aborting; filesystem credentials required.' );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! \WP_Filesystem() ) {
|
||||
GFCommon::log_error( __METHOD__ . '(): Aborting; unable to init WP_Filesystem.' );
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$lang_dir = $this->get_path();
|
||||
if ( ! $wp_filesystem->is_dir( $lang_dir ) ) {
|
||||
$wp_filesystem->mkdir( $lang_dir, FS_CHMOD_DIR );
|
||||
}
|
||||
|
||||
GFCommon::log_debug( __METHOD__ . '(): Downloading: ' . $translation['package'] );
|
||||
$temp_file = download_url( $translation['package'] );
|
||||
|
||||
if ( is_wp_error( $temp_file ) ) {
|
||||
GFCommon::log_error( __METHOD__ . '(): Error downloading package. Code: ' . $temp_file->get_error_code() . '; Message: ' . $temp_file->get_error_message() );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$zip_path = $lang_dir . $this->slug . '-' . $translation['language'] . '.zip';
|
||||
$copy_result = $wp_filesystem->copy( $temp_file, $zip_path, true, FS_CHMOD_FILE );
|
||||
$wp_filesystem->delete( $temp_file );
|
||||
|
||||
if ( ! $copy_result ) {
|
||||
GFCommon::log_error( __METHOD__ . '(): Unable to move package to: ' . $lang_dir );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$result = unzip_file( $zip_path, $lang_dir );
|
||||
@unlink( $zip_path );
|
||||
|
||||
if ( is_wp_error( $result ) ) {
|
||||
GFCommon::log_error( __METHOD__ . '(): Error extracting package. Code: ' . $result->get_error_code() . '; Message: ' . $result->get_error_message() );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
GFCommon::log_debug( __METHOD__ . sprintf( '(): Installed %s translation for %s.', $translation['language'], $this->slug ) );
|
||||
$this->installed[] = $translation['language'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs which locales WordPress installs translations for.
|
||||
*
|
||||
* @since 2.5.6
|
||||
*
|
||||
* @param object $upgrader_object WP_Upgrader Instance.
|
||||
* @param array $hook_extra Item update data.
|
||||
*/
|
||||
public function upgrader_process_complete( $upgrader_object, $hook_extra ) {
|
||||
if ( rgar( $hook_extra, 'type' ) !== 'translation' || empty( $hook_extra['translations'] ) || empty( $upgrader_object->result ) || is_wp_error( $upgrader_object->result ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$locales = array();
|
||||
|
||||
foreach ( $hook_extra['translations'] as $translation ) {
|
||||
if ( rgar( $translation, 'type' ) !== 'plugin' || rgar( $translation, 'slug' ) !== $this->slug ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$locales[] = $translation['language'];
|
||||
}
|
||||
|
||||
if ( empty( $locales ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->installed = $locales;
|
||||
GFCommon::log_debug( __METHOD__ . sprintf( '(): WordPress installed %s translation(s) for %s.', implode( ', ', $locales ), $this->slug ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of locales the site has installed.
|
||||
*
|
||||
* @since 2.5.6
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function get_available_languages() {
|
||||
static $languages = array();
|
||||
|
||||
if ( empty( $languages ) ) {
|
||||
$languages = get_available_languages();
|
||||
}
|
||||
|
||||
return $languages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the header data from the installed translations for the current plugin.
|
||||
*
|
||||
* @since 2.5.6
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function get_installed_translations_data() {
|
||||
static $data = array();
|
||||
|
||||
if ( isset( $data[ $this->slug ] ) ) {
|
||||
return $data[ $this->slug ];
|
||||
}
|
||||
|
||||
$data[ $this->slug ] = array();
|
||||
$translations = GFCommon::get_installed_translations( $this->slug, true );
|
||||
|
||||
foreach ( $translations as $locale => $mo_file ) {
|
||||
$po_file = str_replace( '.mo', '.po', $mo_file );
|
||||
if ( ! file_exists( $po_file ) ) {
|
||||
continue;
|
||||
}
|
||||
$data[ $this->slug ][ $locale ] = wp_get_pomo_file_data( $po_file );
|
||||
}
|
||||
|
||||
return $data[ $this->slug ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path to where plugin translations are stored.
|
||||
*
|
||||
* @since 2.5.6
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function get_path() {
|
||||
return WP_LANG_DIR . '/plugins/';
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a translation should be installed.
|
||||
*
|
||||
* @since 2.5.6
|
||||
*
|
||||
* @param array $translation The translation data.
|
||||
* @param string $locale The locale when the site locale is changed or an empty string to check all the user available locales.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function should_install( $translation, $locale = '' ) {
|
||||
if ( ( $locale && $locale !== $translation['language'] ) || ! in_array( $translation['language'], $this->get_available_languages() ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( empty( $translation['updated'] ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$installed = $this->get_installed_translations_data();
|
||||
|
||||
if ( ! isset( $installed[ $translation['language'] ] ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$local = date_create( $installed[ $translation['language'] ]['PO-Revision-Date'] );
|
||||
$remote = date_create( $translation['updated'] );
|
||||
|
||||
return $remote > $local;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
TranslationsPress_Updater::get_instance( 'gravityforms' );
|
||||
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
namespace Gravity_Forms\Gravity_Forms\Config;
|
||||
|
||||
use Gravity_Forms\Gravity_Forms\Config\GF_Config_Collection;
|
||||
use Gravity_Forms\Gravity_Forms\Config\GF_Config;
|
||||
use Gravity_Forms\Gravity_Forms\Config\GF_Configurator;
|
||||
|
||||
/**
|
||||
* Config items for app registration, including helper methods.
|
||||
*
|
||||
* @since 2.7.1
|
||||
*/
|
||||
class GF_App_Config extends GF_Config {
|
||||
|
||||
/**
|
||||
* The name of the app, in slug form.
|
||||
*
|
||||
* @var string $app_name
|
||||
*/
|
||||
private $app_name;
|
||||
|
||||
/**
|
||||
* The condition for enqueuing the app data.
|
||||
*
|
||||
* @var bool|callable $display_condition
|
||||
*/
|
||||
private $display_condition;
|
||||
|
||||
/**
|
||||
* The CSS assets to enqueue.
|
||||
*
|
||||
* @var array $css_asset
|
||||
*/
|
||||
private $css_asset;
|
||||
|
||||
/**
|
||||
* The relative path to the chunk in JS.
|
||||
*
|
||||
* @var string $chunk
|
||||
*/
|
||||
private $chunk;
|
||||
|
||||
/**
|
||||
* The root element to use to load the app.
|
||||
*
|
||||
* @var string $root_element
|
||||
*/
|
||||
private $root_element;
|
||||
|
||||
/**
|
||||
* Set the data for this app config.
|
||||
*
|
||||
* @since 2.7.1
|
||||
*
|
||||
* @param $data
|
||||
*/
|
||||
public function set_data( $data ) {
|
||||
$this->name = $data['object_name'];
|
||||
$this->script_to_localize = $data['script_name'];
|
||||
$this->app_name = $data['app_name'];
|
||||
|
||||
$this->display_condition = $data['enqueue'];
|
||||
$this->chunk = $data['chunk'];
|
||||
$this->root_element = $data['root_element'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether we should enqueue this data.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @return bool|mixed
|
||||
*/
|
||||
public function should_enqueue() {
|
||||
return is_admin();
|
||||
}
|
||||
|
||||
/**
|
||||
* Config data.
|
||||
*
|
||||
* @return array[]
|
||||
*/
|
||||
public function data() {
|
||||
return array(
|
||||
'apps' => array(
|
||||
$this->app_name => array(
|
||||
'should_display' => is_callable( $this->display_condition ) ? call_user_func( $this->display_condition ) : $this->display_condition,
|
||||
'chunk_path' => $this->chunk,
|
||||
'root_element' => $this->root_element,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,224 @@
|
||||
<?php
|
||||
|
||||
namespace Gravity_Forms\Gravity_Forms\Config;
|
||||
|
||||
use Gravity_Forms\Gravity_Forms\Config\GF_Config;
|
||||
use GFCommon;
|
||||
|
||||
/**
|
||||
* Collection to hold GF_Config items and provide their structured data when needed.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @package Gravity_Forms\Gravity_Forms\Config
|
||||
*/
|
||||
class GF_Config_Collection {
|
||||
|
||||
/**
|
||||
* @var GF_Config[] $configs
|
||||
*/
|
||||
private $configs = array();
|
||||
|
||||
/**
|
||||
* Add a config to the collection.
|
||||
*
|
||||
* @param GF_Config $config
|
||||
*/
|
||||
public function add_config( GF_Config $config ) {
|
||||
$this->configs[] = $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle outputting the config data.
|
||||
*
|
||||
* If $localize is true, data is actually localized via `wp_localize_script`, otherwise
|
||||
* data is simply returned as an array.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @param bool $localize Whether to localize the data, or simply return it.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function handle( $localize = true, $args = null ) {
|
||||
|
||||
$scripts = $this->get_configs_by_script();
|
||||
$data_to_localize = array();
|
||||
|
||||
foreach ( $scripts as $script => $items ) {
|
||||
$item_data = $this->localize_data_for_script( $script, $items, $localize, $args );
|
||||
$data_to_localize = array_merge( $data_to_localize, $item_data );
|
||||
}
|
||||
|
||||
return $data_to_localize;
|
||||
}
|
||||
|
||||
public function handle_ajax() {
|
||||
|
||||
// Check nonce.
|
||||
$nonce_result = check_ajax_referer( 'gform_config_ajax', 'gform_ajax_nonce', false );
|
||||
|
||||
if ( ! $nonce_result ) {
|
||||
GFCommon::send_json_error( esc_html__( 'Unable to verify nonce. Please refresh the page and try again.', 'gravityforms' ) );
|
||||
}
|
||||
|
||||
$args = json_decode( rgpost( 'args' ), true );
|
||||
$config_path = wp_strip_all_tags( rgpost( 'config_path' ) );
|
||||
$query_string = rgpost( 'query_string' );
|
||||
|
||||
// Making the query string available for use with form filters.
|
||||
if ( ! empty( $query_string ) && is_string( $query_string ) ) {
|
||||
parse_str( $query_string, $query );
|
||||
unset( $query['gf_page'] ); // Removing so it doesn't conflict with gf_ajax_page=preview.
|
||||
$_GET = array_merge( $_GET, $query );
|
||||
}
|
||||
|
||||
$configs = $this->get_configs_by_path( $config_path, $args );
|
||||
|
||||
if ( ! $configs ) {
|
||||
GFCommon::send_json_error( sprintf( esc_html__( 'Unable to find config: %s', 'gravityforms' ), esc_html( $config_path ) ) );
|
||||
}
|
||||
|
||||
$data = $this->get_merged_data_for_object( $configs, $args );
|
||||
GFCommon::send_json_success( $data );
|
||||
}
|
||||
/**
|
||||
* Localize the data for the given script.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @param string $script The script to localize.
|
||||
* @param array $items The associative array of configs to process for this script.
|
||||
* @param bool $localize Whether to actually localize the data, or simply return it.
|
||||
* @param array $args The arguments to pass to the config objects.
|
||||
*
|
||||
* @return array Returns the localized data.
|
||||
*/
|
||||
private function localize_data_for_script( $script, $items, $localize = true, $args = null ) {
|
||||
$data = array();
|
||||
|
||||
foreach ( $items as $name => $configs ) {
|
||||
|
||||
$localized_data = $this->get_merged_data_for_object( $configs, $args );
|
||||
|
||||
/**
|
||||
* Allows users to filter the data localized for a given script/resource.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @param array $localized_data The current localize data
|
||||
* @param string $script The script being localized
|
||||
* @param array $configs An array of $configs being applied to this script
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
$localized_data = apply_filters( 'gform_localized_script_data_' . $name, $localized_data, $script, $configs );
|
||||
|
||||
$data[ $name ] = $localized_data;
|
||||
|
||||
if ( $localize ) {
|
||||
wp_localize_script( $script, $name, $localized_data );
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the merged data object for the applicable configs. Will process each config by its
|
||||
* $priority property, overriding or merging values as needed.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @param GF_Config[] $configs
|
||||
*/
|
||||
private function get_merged_data_for_object( $configs, $args ) {
|
||||
// Squash warnings for PHP < 7.0 when running tests.
|
||||
@usort( $configs, array( $this, 'sort_by_priority' ) );
|
||||
|
||||
$data = array();
|
||||
|
||||
foreach ( $configs as $config ) {
|
||||
|
||||
$config->set_args( $args );
|
||||
|
||||
// Config is set to overwrite data - simply return its value without attempting to merge.
|
||||
if ( $config->should_overwrite() ) {
|
||||
$data = $config->get_data();
|
||||
continue;
|
||||
}
|
||||
|
||||
// Config should be merged - loop through each key and attempt to recursively merge the values.
|
||||
foreach ( $config->get_data() as $key => $value ) {
|
||||
$existing = isset( $data[ $key ] ) ? $data[ $key ] : null;
|
||||
|
||||
if ( is_null( $existing ) || ! is_array( $existing ) || ! is_array( $value ) ) {
|
||||
$data[ $key ] = $value;
|
||||
continue;
|
||||
}
|
||||
|
||||
$data[ $key ] = array_merge_recursive( $existing, $value );
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the appropriate configs, organized by the script they belong to.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function get_configs_by_script() {
|
||||
$data_to_localize = array();
|
||||
|
||||
foreach ( $this->configs as $config ) {
|
||||
if ( ( ! defined( 'GFORMS_DOING_MOCK' ) || ! GFORMS_DOING_MOCK ) && ! $config->should_enqueue() ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$data_to_localize[ $config->script_to_localize() ][ $config->name() ][] = $config;
|
||||
}
|
||||
|
||||
return $data_to_localize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a list of configs that match the specified config path.
|
||||
*
|
||||
* @since 2.9.0
|
||||
*
|
||||
* @param string $config_path The path of the config to be returned.
|
||||
* @param array $args The arguments to pass to the config objects.
|
||||
*
|
||||
* @return array Returns an array of configs that are associated with the specified config path.
|
||||
*/
|
||||
private function get_configs_by_path( $config_path, $args )
|
||||
{
|
||||
$configs = array();
|
||||
foreach ( $this->configs as $config ) {
|
||||
if ( $config->enable_ajax( $config_path, $args ) ) {
|
||||
$configs[] = $config;
|
||||
}
|
||||
}
|
||||
return $configs;
|
||||
}
|
||||
|
||||
/**
|
||||
* usort() callback to sort the configs by their $priority.
|
||||
*
|
||||
* @param GF_Config $a
|
||||
* @param GF_Config $b
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function sort_by_priority( GF_Config $a, GF_Config $b ) {
|
||||
if ( $a->priority() === $b->priority() ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return $a->priority() < $b->priority() ? - 1 : 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
namespace Gravity_Forms\Gravity_Forms\Config;
|
||||
|
||||
/**
|
||||
* Parses a given data array to return either Live or Mock values, depending on the
|
||||
* environment and context.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @package Gravity_Forms\Gravity_Forms\Config
|
||||
*/
|
||||
class GF_Config_Data_Parser {
|
||||
|
||||
/**
|
||||
* Parse the given $data array and get the correct values for the context.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @param $data
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function parse( $data ) {
|
||||
$return = array();
|
||||
|
||||
foreach( $data as $key => $value ) {
|
||||
$return[ $key ] = $this->get_correct_value( $value );
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loop through each array key and get the correct value. Is called recursively for
|
||||
* nested arrays.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return array|mixed
|
||||
*/
|
||||
private function get_correct_value( $value ) {
|
||||
|
||||
// Value isn't array - we've reached the final level for this branch.
|
||||
if ( ! is_array( $value ) ) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
// Value is an array with our defined value and default keys. Return either live or mock data.
|
||||
if ( array_key_exists( 'default', $value ) && array_key_exists( 'value', $value ) ) {
|
||||
return $this->is_mock() ? $value['default'] : $value['value'];
|
||||
}
|
||||
|
||||
$data = array();
|
||||
|
||||
// Value is an array - recursively call this method to dig into each level and return the correct value.
|
||||
foreach( $value as $key => $value ) {
|
||||
$data[ $key ] = $this->get_correct_value( $value );
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the current environmental context is a Mock context.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function is_mock() {
|
||||
return defined( 'GFORMS_DOING_MOCK' ) && GFORMS_DOING_MOCK;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,245 @@
|
||||
<?php
|
||||
|
||||
namespace Gravity_Forms\Gravity_Forms\Config;
|
||||
|
||||
use Gravity_Forms\Gravity_Forms\Config\Items\GF_Config_Admin;
|
||||
use Gravity_Forms\Gravity_Forms\Config\Items\GF_Config_Block_Editor;
|
||||
use Gravity_Forms\Gravity_Forms\Config\Items\GF_Config_Global;
|
||||
use Gravity_Forms\Gravity_Forms\Config\Items\GF_Config_I18n;
|
||||
use Gravity_Forms\Gravity_Forms\Config\Items\GF_Config_Legacy_Check;
|
||||
use Gravity_Forms\Gravity_Forms\Config\Items\GF_Config_Legacy_Check_Multi;
|
||||
use Gravity_Forms\Gravity_Forms\Config\Items\GF_Config_Multifile;
|
||||
|
||||
use Gravity_Forms\Gravity_Forms\GF_Service_Container;
|
||||
use Gravity_Forms\Gravity_Forms\GF_Service_Provider;
|
||||
|
||||
/**
|
||||
* Class GF_Config_Service_Provider
|
||||
*
|
||||
* Service provider for the Config Service.
|
||||
*
|
||||
* @package Gravity_Forms\Gravity_Forms\Config
|
||||
*/
|
||||
class GF_Config_Service_Provider extends GF_Service_Provider {
|
||||
|
||||
// Organizational services
|
||||
const CONFIG_COLLECTION = 'config_collection';
|
||||
const DATA_PARSER = 'data_parser';
|
||||
|
||||
// Config services
|
||||
const I18N_CONFIG = 'i18n_config';
|
||||
const ADMIN_CONFIG = 'admin_config';
|
||||
const LEGACY_CONFIG = 'legacy_config';
|
||||
const LEGACY_MULTI_CONFIG = 'legacy_multi_config';
|
||||
const MULTIFILE_CONFIG = 'multifile_config';
|
||||
const GLOBAL_CONFIG = 'global_config';
|
||||
|
||||
/**
|
||||
* Array mapping config class names to their container ID.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $configs = array(
|
||||
self::I18N_CONFIG => GF_Config_I18n::class,
|
||||
self::ADMIN_CONFIG => GF_Config_Admin::class,
|
||||
self::LEGACY_CONFIG => GF_Config_Legacy_Check::class,
|
||||
self::LEGACY_MULTI_CONFIG => GF_Config_Legacy_Check_Multi::class,
|
||||
self::MULTIFILE_CONFIG => GF_Config_Multifile::class,
|
||||
);
|
||||
|
||||
/**
|
||||
* Register services to the container.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @param GF_Service_Container $container
|
||||
*/
|
||||
public function register( GF_Service_Container $container ) {
|
||||
|
||||
// Include required files.
|
||||
require_once( plugin_dir_path( __FILE__ ) . 'class-gf-config-collection.php' );
|
||||
require_once( plugin_dir_path( __FILE__ ) . 'class-gf-config.php' );
|
||||
require_once( plugin_dir_path( __FILE__ ) . 'class-gf-config-data-parser.php' );
|
||||
require_once( plugin_dir_path( __FILE__ ) . 'class-gf-app-config.php' );
|
||||
require_once( plugin_dir_path( __FILE__ ) . 'items/class-gf-config-global.php' );
|
||||
|
||||
// Add to container
|
||||
$container->add( self::CONFIG_COLLECTION, function () {
|
||||
return new GF_Config_Collection();
|
||||
} );
|
||||
|
||||
$container->add( self::DATA_PARSER, function () {
|
||||
return new GF_Config_Data_Parser();
|
||||
} );
|
||||
|
||||
$container->add( self::GLOBAL_CONFIG, function () {
|
||||
return new GF_Config_Global();
|
||||
} );
|
||||
|
||||
// Add configs to container.
|
||||
$this->register_config_items( $container );
|
||||
$this->register_configs_to_collection( $container );
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the config has been localized.
|
||||
*
|
||||
* @since 2.9.0
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private static $is_localized = false;
|
||||
|
||||
/**
|
||||
* Initiailize any actions or hooks.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @param GF_Service_Container $container
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init( GF_Service_Container $container ) {
|
||||
|
||||
// Need to pass $this to callbacks; save as variable.
|
||||
$self = $this;
|
||||
|
||||
add_action( 'wp_enqueue_scripts', function () use ( $container ) {
|
||||
// Only localize during wp_enqueue_scripts if none of the other more specific events have been fired.
|
||||
if ( ! self::$is_localized ) {
|
||||
$container->get( self::CONFIG_COLLECTION )->handle();
|
||||
}
|
||||
}, 9999 );
|
||||
|
||||
add_action( 'admin_enqueue_scripts', function () use ( $container ) {
|
||||
// Only localize during admin_enqueue_scripts if none of the other more specific events have been fired.
|
||||
if ( ! self::$is_localized ) {
|
||||
$container->get( self::CONFIG_COLLECTION )->handle();
|
||||
}
|
||||
}, 9999 );
|
||||
|
||||
add_action( 'gform_output_config', function ( $form_ids = null ) use ( $container ) {
|
||||
$container->get( self::CONFIG_COLLECTION )->handle( true, $form_ids );
|
||||
self::$is_localized = true;
|
||||
} );
|
||||
|
||||
add_action( 'gform_post_enqueue_scripts', function ( $found_forms, $found_blocks, $post ) use ( $container ) {
|
||||
$form_ids = array_column( $found_forms, 'formId' );
|
||||
$container->get( self::CONFIG_COLLECTION )->handle( true, array( 'form_ids' => $form_ids ) );
|
||||
self::$is_localized = true;
|
||||
}, 10, 3);
|
||||
|
||||
add_action( 'gform_preview_init', function ( $form_id ) use ( $container ) {
|
||||
$form_ids = array( $form_id );
|
||||
$container->get( self::CONFIG_COLLECTION )->handle( true, array( 'form_ids' => $form_ids ) );
|
||||
self::$is_localized = true;
|
||||
}, 10, 2);
|
||||
|
||||
add_action('wp_ajax_gform_get_config', function () use ( $container ) {
|
||||
$container->get( self::CONFIG_COLLECTION )->handle_ajax();
|
||||
});
|
||||
|
||||
add_action('wp_ajax_nopriv_gform_get_config', function () use ( $container ) {
|
||||
$container->get( self::CONFIG_COLLECTION )->handle_ajax();
|
||||
});
|
||||
|
||||
add_action( 'rest_api_init', function () use ( $container, $self ) {
|
||||
// check if we are in a test environment, if so register the mock data endpoint.
|
||||
if ( defined( 'GF_SCRIPT_DEBUG' ) && GF_SCRIPT_DEBUG ) {
|
||||
register_rest_route( 'gravityforms/v2', '/tests/mock-data', array( // nosemgrep audit.php.wp.security.rest-route.permission-callback.return-true
|
||||
'methods' => 'GET',
|
||||
'callback' => array( $self, 'config_mocks_endpoint' ),
|
||||
'permission_callback' => function () {
|
||||
return true;
|
||||
},
|
||||
) );
|
||||
}
|
||||
} );
|
||||
|
||||
// Add global config data to admin and theme.
|
||||
add_filter( 'gform_localized_script_data_gform_admin_config', function ( $data ) use ( $self ) {
|
||||
return $self->add_global_config_data( $data );
|
||||
} );
|
||||
|
||||
add_filter( 'gform_localized_script_data_gform_theme_config', function ( $data ) use ( $self ) {
|
||||
return $self->add_global_config_data( $data );
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* For each config defined in $configs, instantiate and add to container.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @param GF_Service_Container $container
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function register_config_items( GF_Service_Container $container ) {
|
||||
require_once( plugin_dir_path( __FILE__ ) . '/items/class-gf-config-i18n.php' );
|
||||
require_once( plugin_dir_path( __FILE__ ) . '/items/class-gf-config-admin.php' );
|
||||
require_once( plugin_dir_path( __FILE__ ) . '/items/class-gf-config-legacy-check.php' );
|
||||
require_once( plugin_dir_path( __FILE__ ) . '/items/class-gf-config-legacy-check-multi.php' );
|
||||
require_once( plugin_dir_path( __FILE__ ) . '/items/class-gf-config-multifile.php' );
|
||||
|
||||
$parser = $container->get( self::DATA_PARSER );
|
||||
|
||||
foreach ( $this->configs as $name => $class ) {
|
||||
$container->add( $name, function () use ( $class, $parser ) {
|
||||
return new $class( $parser );
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register each config defined in $configs to the GF_Config_Collection.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @param GF_Service_Container $container
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register_configs_to_collection( GF_Service_Container $container ) {
|
||||
$collection = $container->get( self::CONFIG_COLLECTION );
|
||||
|
||||
foreach ( $this->configs as $name => $config ) {
|
||||
$config_class = $container->get( $name );
|
||||
$collection->add_config( $config_class );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for the Config Mocks REST endpoint.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function config_mocks_endpoint() {
|
||||
define( 'GFORMS_DOING_MOCK', true );
|
||||
$container = \GFForms::get_service_container();
|
||||
$data = $container->get( self::CONFIG_COLLECTION )->handle( false );
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add global data to both admin and theme configs so that it is available everywhere
|
||||
* within the system.
|
||||
*
|
||||
* @since 2.7
|
||||
*
|
||||
* @param $data
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function add_global_config_data( $data ) {
|
||||
$container = \GFForms::get_service_container();
|
||||
$global = $container->get( self::GLOBAL_CONFIG )->data();
|
||||
|
||||
return array_merge( $data, $global );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,297 @@
|
||||
<?php
|
||||
|
||||
namespace Gravity_Forms\Gravity_Forms\Config;
|
||||
|
||||
/**
|
||||
* Base class for providing advanced functionality when localizing Config Data
|
||||
* for usage in Javascript.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @package Gravity_Forms\Gravity_Forms\Config
|
||||
*/
|
||||
abstract class GF_Config {
|
||||
|
||||
/**
|
||||
* The Data Parser
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @var GF_Config_Data_Parser
|
||||
*/
|
||||
protected $parser;
|
||||
|
||||
/**
|
||||
* The data for this config object.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $data;
|
||||
|
||||
/**
|
||||
* The object name for this config.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* The ID of the script to localize the data to.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $script_to_localize;
|
||||
|
||||
/**
|
||||
* The priority of this config - can be used to control the order in
|
||||
* which configs are processed in the Collection.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $priority = 0;
|
||||
|
||||
/**
|
||||
* Whether the config should enqueue it's data. Can also be handled by overriding the
|
||||
* ::should_enqueue() method.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $should_enqueue = true;
|
||||
|
||||
/**
|
||||
* Whether this config should overwrite previous values in the object.
|
||||
*
|
||||
* If set to "true", the object will be overwritten by the values provided here.
|
||||
* If set to "false", the object will have its values merged with those defined here, recursively.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $overwrite = false;
|
||||
|
||||
/**
|
||||
* An args array. Use in the data() method to retrieve data specific to the specified args. For example, form specific configs will have an array of form ids specified in args.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $args = array();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param GF_Config_Data_Parser $parser
|
||||
*/
|
||||
public function __construct( GF_Config_Data_Parser $parser ) {
|
||||
$this->parser = $parser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to handle defining the data array for this config.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
abstract protected function data();
|
||||
|
||||
/**
|
||||
* Override this method to add enable ajax loading for a specific config path.
|
||||
* To enable loading data() via ajax, check if $config_path is one of the paths that are provided by the config. If so, return true.
|
||||
*
|
||||
* Example:
|
||||
* public function enable_ajax( $config_path, $args ) {
|
||||
* return str_starts_with( $config_path, 'gform_theme_config/common/form/product_meta' );
|
||||
* }
|
||||
*
|
||||
* @since 2.9.0
|
||||
*
|
||||
* @param string $config_path The full path to the config item when stored in the browser's window object, for example: "gform_theme_config/common/form/product_meta"
|
||||
* @param array $args The args used to load the config data. This will be empty for generic config items. For form specific items will be in the format: array( 'form_ids' => array(123,222) ).
|
||||
*
|
||||
* @return bool Return true to load the config data associated with the provided $config_path. Return false otherwise.
|
||||
*/
|
||||
public function enable_ajax( $config_path, $args ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the config should enqueue its data. If should_enqueue() is a method,
|
||||
* call it and return the result. If not, simply return the (boolean) value of the property.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function should_enqueue() {
|
||||
if ( is_callable( $this->should_enqueue ) ) {
|
||||
return call_user_func( $this->should_enqueue );
|
||||
}
|
||||
|
||||
return $this->should_enqueue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the data for the config, passing it through a filter.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_data() {
|
||||
if ( ( ! defined( 'GFORMS_DOING_MOCK' ) || ! GFORMS_DOING_MOCK ) && ! $this->should_enqueue() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows developers to modify the raw config data being sent to the Config Parser. Useful for
|
||||
* adding in custom default/mock values for a given entry in the data, as well as modifying
|
||||
* things like callbacks for dynamic data before it's parsed and localized.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @param array $data
|
||||
* @param string $script_to_localize
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
$data = apply_filters( 'gform_config_data_' . $this->name(), $this->data(), $this->script_to_localize() );
|
||||
|
||||
return $this->parser->parse( $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the config's object.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function name() {
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the $priority for the config.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function priority() {
|
||||
return $this->priority;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the script to localize.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function script_to_localize() {
|
||||
return $this->script_to_localize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get whether the config should override previous values.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function should_overwrite() {
|
||||
return $this->overwrite;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the $form_ids arrays.
|
||||
*
|
||||
* @since 2.9.0
|
||||
*
|
||||
* @param array $args Args array to be set
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function set_args( $args ) {
|
||||
$this->args = $args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a sanitized array of form ids from the args.
|
||||
*
|
||||
* @since 2.9.31
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_form_ids_arg() {
|
||||
$form_ids = rgar( $this->args, 'form_ids' );
|
||||
if ( empty( $form_ids ) || ! is_array( $form_ids ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
return array_filter( array_map( 'absint', $form_ids ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the config data against a hash to ensure it has not been tampered with.
|
||||
* This method is called via AJAX, initiated by the gform.config.isValid() JS method.
|
||||
*
|
||||
* @since 2.9.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function validate_ajax() {
|
||||
|
||||
// Check nonce
|
||||
$nonce_result = check_ajax_referer( 'gform_config_ajax', 'gform_ajax_nonce', false );
|
||||
|
||||
if ( ! $nonce_result ) {
|
||||
wp_send_json_error( esc_html__( 'Unable to verify nonce. Please refresh the page and try again.', 'gravityforms' ) );
|
||||
}
|
||||
|
||||
$config = json_decode( rgpost( 'config' ), true );
|
||||
$hash = rgar( $config, 'hash' );
|
||||
if ( ! $hash ) {
|
||||
wp_send_json_error( esc_html__( 'Invalid config.', 'gravityforms' ) );
|
||||
}
|
||||
|
||||
// Remove hash from config before validating.
|
||||
unset( $config['hash'] );
|
||||
|
||||
// Compare hash to config.
|
||||
if ( $hash !== self::hash( $config ) ) {
|
||||
wp_send_json_error( esc_html__( 'Config validation failed. Hash does not match', 'gravityforms' ) );
|
||||
}
|
||||
|
||||
// Send success response.
|
||||
wp_send_json_success();
|
||||
}
|
||||
|
||||
/**
|
||||
* Hashes the config data.
|
||||
*
|
||||
* @since 2.9.0
|
||||
*
|
||||
* @param array $config
|
||||
*
|
||||
* @return string Returns the hash of the config data.
|
||||
*/
|
||||
public static function hash( $config ) {
|
||||
return wp_hash( json_encode( $config ) );
|
||||
}
|
||||
}
|
||||
|
||||
// AJAX hash validation for config data.
|
||||
add_action('wp_ajax_gform_validate_config', array( 'Gravity_Forms\Gravity_Forms\Config\GF_Config', 'validate_ajax' ) );
|
||||
add_action('wp_ajax_nopriv_gform_validate_config', array( 'Gravity_Forms\Gravity_Forms\Config\GF_Config', 'validate_ajax' ) );
|
||||
@@ -0,0 +1,188 @@
|
||||
<?php
|
||||
|
||||
namespace Gravity_Forms\Gravity_Forms\Config\Items;
|
||||
|
||||
use Gravity_Forms\Gravity_Forms\Config\GF_Config_Collection;
|
||||
use Gravity_Forms\Gravity_Forms\Config\GF_Config;
|
||||
use Gravity_Forms\Gravity_Forms\Config\GF_Configurator;
|
||||
|
||||
/**
|
||||
* Config items for Admin I18N
|
||||
*
|
||||
* @since 2.6
|
||||
*/
|
||||
class GF_Config_Admin extends GF_Config {
|
||||
|
||||
protected $name = 'gform_admin_config';
|
||||
protected $script_to_localize = 'gform_gravityforms_admin_vendors';
|
||||
|
||||
/**
|
||||
* Whether we should enqueue this data.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @return bool|mixed
|
||||
*/
|
||||
public function should_enqueue() {
|
||||
return is_admin();
|
||||
}
|
||||
|
||||
/**
|
||||
* Config data.
|
||||
*
|
||||
* @return array[]
|
||||
*/
|
||||
public function data() {
|
||||
$data = array(
|
||||
'data' => array(
|
||||
'is_block_editor' => \GFCommon::is_block_editor_page(),
|
||||
'gf_page' => \GFForms::get_page(),
|
||||
),
|
||||
'i18n' => array(
|
||||
'form_admin' => array(
|
||||
'toggle_feed_inactive' => esc_html__( 'Inactive', 'gravityforms' ),
|
||||
'toggle_feed_active' => esc_html__( 'Active', 'gravityforms' ),
|
||||
),
|
||||
'shortcode_ui' => array(
|
||||
'edit_form' => esc_html__( 'Edit Form', 'gravityforms' ),
|
||||
'insert_form' => esc_html__( 'Insert Form', 'gravityforms' ),
|
||||
),
|
||||
'dialog' => array(
|
||||
'cancel' => esc_html__( 'Cancel', 'gravityforms' ),
|
||||
'close' => esc_html__( 'Close', 'gravityforms' ),
|
||||
'ok' => esc_html__( 'OK', 'gravityforms' ),
|
||||
),
|
||||
),
|
||||
'bulk_actions' => $this->get_bulk_action_config(),
|
||||
);
|
||||
|
||||
$data = $this->add_entry_list_data( $data );
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
private function get_bulk_action_config() {
|
||||
return array(
|
||||
'nonce' => wp_create_nonce( 'gf_bulk_action' ),
|
||||
'threshold' => \Gravity_Forms\Gravity_Forms\Bulk_Actions\Endpoints\GF_Bulk_Action_Endpoint_Start::get_background_threshold(),
|
||||
'backgroundActions' => \Gravity_Forms\Gravity_Forms\Bulk_Actions\Endpoints\GF_Bulk_Action_Endpoint_Start::BACKGROUND_ACTIONS,
|
||||
'pollInterval' => 1500,
|
||||
'labels' => array(
|
||||
'delete' => array(
|
||||
'title' => esc_html__( 'Bulk Delete', 'gravityforms' ),
|
||||
'starting' => esc_html__( 'Starting bulk delete...', 'gravityforms' ),
|
||||
'progress' => esc_html__( 'Deleting Gravity Forms entries...', 'gravityforms' ),
|
||||
'complete' => esc_html__( 'Completed deleting entries!', 'gravityforms' ),
|
||||
'error' => esc_html__( 'Error starting bulk delete.', 'gravityforms' ),
|
||||
),
|
||||
'trash' => array(
|
||||
'title' => esc_html__( 'Bulk Trash', 'gravityforms' ),
|
||||
'starting' => esc_html__( 'Starting bulk trash...', 'gravityforms' ),
|
||||
'progress' => esc_html__( 'Trashing Gravity Forms entries...', 'gravityforms' ),
|
||||
'complete' => esc_html__( 'Completed moving entries to trash!', 'gravityforms' ),
|
||||
'error' => esc_html__( 'Error starting bulk trash.', 'gravityforms' ),
|
||||
),
|
||||
'spam' => array(
|
||||
'title' => esc_html__( 'Bulk Mark as Spam', 'gravityforms' ),
|
||||
'starting' => esc_html__( 'Starting bulk spam marking...', 'gravityforms' ),
|
||||
'progress' => esc_html__( 'Marking Gravity Forms entries as spam...', 'gravityforms' ),
|
||||
'complete' => esc_html__( 'Completed marking entries as spam!', 'gravityforms' ),
|
||||
'error' => esc_html__( 'Error starting bulk spam.', 'gravityforms' ),
|
||||
),
|
||||
'restore' => array(
|
||||
'title' => esc_html__( 'Bulk Restore', 'gravityforms' ),
|
||||
'starting' => esc_html__( 'Starting bulk restore...', 'gravityforms' ),
|
||||
'progress' => esc_html__( 'Restoring Gravity Forms entries...', 'gravityforms' ),
|
||||
'complete' => esc_html__( 'Completed restoring entries!', 'gravityforms' ),
|
||||
'error' => esc_html__( 'Error starting bulk restore.', 'gravityforms' ),
|
||||
),
|
||||
'unspam' => array(
|
||||
'title' => esc_html__( 'Bulk Unmark as Spam', 'gravityforms' ),
|
||||
'starting' => esc_html__( 'Starting bulk unspam...', 'gravityforms' ),
|
||||
'progress' => esc_html__( 'Restoring Gravity Forms entries from spam...', 'gravityforms' ),
|
||||
'complete' => esc_html__( 'Completed unmarking entries as spam!', 'gravityforms' ),
|
||||
'error' => esc_html__( 'Error starting bulk unspam.', 'gravityforms' ),
|
||||
),
|
||||
'delete_entries' => array(
|
||||
'title' => esc_html__( 'Bulk Delete Entries', 'gravityforms' ),
|
||||
'starting' => esc_html__( 'Starting bulk entry deletion...', 'gravityforms' ),
|
||||
'progress' => esc_html__( 'Deleting Gravity Forms entries...', 'gravityforms' ),
|
||||
'complete' => esc_html__( 'Completed deleting entries!', 'gravityforms' ),
|
||||
'error' => esc_html__( 'Error starting bulk entry deletion.', 'gravityforms' ),
|
||||
),
|
||||
'mark_read' => array(
|
||||
'title' => esc_html__( 'Bulk Mark as Read', 'gravityforms' ),
|
||||
'starting' => esc_html__( 'Starting bulk mark as read...', 'gravityforms' ),
|
||||
'progress' => esc_html__( 'Marking Gravity Forms entries as read...', 'gravityforms' ),
|
||||
'complete' => esc_html__( 'Completed marking entries as read!', 'gravityforms' ),
|
||||
'error' => esc_html__( 'Error starting bulk mark as read.', 'gravityforms' ),
|
||||
),
|
||||
'mark_unread' => array(
|
||||
'title' => esc_html__( 'Bulk Mark as Unread', 'gravityforms' ),
|
||||
'starting' => esc_html__( 'Starting bulk mark as unread...', 'gravityforms' ),
|
||||
'progress' => esc_html__( 'Marking Gravity Forms entries as unread...', 'gravityforms' ),
|
||||
'complete' => esc_html__( 'Completed marking entries as unread!', 'gravityforms' ),
|
||||
'error' => esc_html__( 'Error starting bulk mark as unread.', 'gravityforms' ),
|
||||
),
|
||||
),
|
||||
'messages' => array(
|
||||
'inProgress' => esc_html__( 'Bulk action in progress...', 'gravityforms' ),
|
||||
'continueInBackground' => esc_html__( 'Continue in Background', 'gravityforms' ),
|
||||
'cancel' => esc_html__( 'Cancel', 'gravityforms' ),
|
||||
'cancelling' => esc_html__( 'Cancelling...', 'gravityforms' ),
|
||||
'operationCancelled' => esc_html__( 'Operation cancelled.', 'gravityforms' ),
|
||||
'viewDetails' => esc_html__( 'View Details', 'gravityforms' ),
|
||||
'close' => esc_html__( 'Close', 'gravityforms' ),
|
||||
'processedCount' => esc_html__( '%1$d of %2$d items processed.', 'gravityforms' ),
|
||||
'goToPage' => esc_html__( 'Go to %s', 'gravityforms' ),
|
||||
),
|
||||
'pageLabels' => array(
|
||||
'entry_list' => esc_html__( 'Entries', 'gravityforms' ),
|
||||
'form_list' => esc_html__( 'Forms', 'gravityforms' ),
|
||||
),
|
||||
'entryActions' => \Gravity_Forms\Gravity_Forms\Bulk_Actions\Endpoints\GF_Bulk_Action_Endpoint_Start::BACKGROUND_ACTIONS_ENTRY,
|
||||
'formActions' => \Gravity_Forms\Gravity_Forms\Bulk_Actions\Endpoints\GF_Bulk_Action_Endpoint_Start::BACKGROUND_ACTIONS_FORM,
|
||||
);
|
||||
}
|
||||
|
||||
private function add_entry_list_data( $data ) {
|
||||
if ( \GFForms::get_page() !== 'entry_list' ) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
$form_id = absint( rgget( 'id' ) );
|
||||
if ( ! $form_id ) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
$data['form_id'] = $form_id;
|
||||
|
||||
$filter = rgget( 'filter' );
|
||||
$search = rgget( 's' );
|
||||
$field_id = rgget( 'field_id' ) === 'entry_id' ? 'id' : rgget( 'field_id' );
|
||||
$operator = rgget( 'operator' );
|
||||
$search_criteria = array();
|
||||
|
||||
if ( $filter === 'trash' || $filter === 'spam' ) {
|
||||
$search_criteria['status'] = $filter;
|
||||
} elseif ( $filter === 'star' ) {
|
||||
$search_criteria['field_filters'][] = array( 'key' => 'is_starred', 'value' => true );
|
||||
} elseif ( $filter === 'unread' ) {
|
||||
$search_criteria['field_filters'][] = array( 'key' => 'is_read', 'value' => false );
|
||||
} else {
|
||||
$search_criteria['status'] = 'active';
|
||||
}
|
||||
|
||||
if ( $search && $field_id ) {
|
||||
$search_criteria['field_filters'][] = array(
|
||||
'key' => $field_id,
|
||||
'value' => $search,
|
||||
'operator' => $operator ? $operator : 'contains',
|
||||
);
|
||||
}
|
||||
|
||||
$data['search_criteria'] = $search_criteria;
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace Gravity_Forms\Gravity_Forms\Config\Items;
|
||||
|
||||
/**
|
||||
* Acts as a container for any Global Config data we need to send to both
|
||||
* the admin and theme side of the ecosystem.
|
||||
*
|
||||
* @since 2.7
|
||||
*/
|
||||
class GF_Config_Global {
|
||||
|
||||
/**
|
||||
* The data to send to both configs.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function data() {
|
||||
return array(
|
||||
'hmr_dev' => defined( 'GF_ENABLE_HMR' ) && GF_ENABLE_HMR,
|
||||
'public_path' => trailingslashit( \GFCommon::get_base_url() ) . 'assets/js/dist/',
|
||||
'config_nonce' => wp_create_nonce( 'gform_config_ajax' ),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
namespace Gravity_Forms\Gravity_Forms\Config\Items;
|
||||
|
||||
use Gravity_Forms\Gravity_Forms\Config\GF_Config_Collection;
|
||||
use Gravity_Forms\Gravity_Forms\Config\GF_Config;
|
||||
use Gravity_Forms\Gravity_Forms\Config\GF_Configurator;
|
||||
|
||||
/**
|
||||
* Config items for Theme I18N
|
||||
*
|
||||
* @since 2.6
|
||||
*/
|
||||
class GF_Config_I18n extends GF_Config {
|
||||
|
||||
protected $name = 'gform_i18n';
|
||||
protected $script_to_localize = 'gform_gravityforms';
|
||||
|
||||
/**
|
||||
* Config data.
|
||||
*
|
||||
* @return array[]
|
||||
*/
|
||||
public function data() {
|
||||
return array(
|
||||
'datepicker' => array(
|
||||
'days' => array(
|
||||
'monday' => esc_html__( 'Mo', 'gravityforms' ),
|
||||
'tuesday' => esc_html__( 'Tu', 'gravityforms' ),
|
||||
'wednesday' => esc_html__( 'We', 'gravityforms' ),
|
||||
'thursday' => esc_html__( 'Th', 'gravityforms' ),
|
||||
'friday' => esc_html__( 'Fr', 'gravityforms' ),
|
||||
'saturday' => esc_html__( 'Sa', 'gravityforms' ),
|
||||
'sunday' => esc_html__( 'Su', 'gravityforms' ),
|
||||
),
|
||||
'months' => array(
|
||||
'january' => esc_html__( 'January', 'gravityforms' ),
|
||||
'february' => esc_html__( 'February', 'gravityforms' ),
|
||||
'march' => esc_html__( 'March', 'gravityforms' ),
|
||||
'april' => esc_html__( 'April', 'gravityforms' ),
|
||||
'may' => esc_html_x('May', 'Full month name', 'gravityforms'),
|
||||
'june' => esc_html__( 'June', 'gravityforms' ),
|
||||
'july' => esc_html__( 'July', 'gravityforms' ),
|
||||
'august' => esc_html__( 'August', 'gravityforms' ),
|
||||
'september' => esc_html__( 'September', 'gravityforms' ),
|
||||
'october' => esc_html__( 'October', 'gravityforms' ),
|
||||
'november' => esc_html__( 'November', 'gravityforms' ),
|
||||
'december' => esc_html__( 'December', 'gravityforms' ),
|
||||
),
|
||||
'firstDay' => array(
|
||||
'value' => absint( get_option( 'start_of_week' ) ),
|
||||
'default' => 1,
|
||||
),
|
||||
'iconText' => esc_html__( 'Select date', 'gravityforms' ),
|
||||
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Gravity_Forms\Gravity_Forms\Config\Items;
|
||||
|
||||
use Gravity_Forms\Gravity_Forms\Config\GF_Config;
|
||||
use Gravity_Forms\Gravity_Forms\Config\GF_Config_Collection;
|
||||
use Gravity_Forms\Gravity_Forms\Config\GF_Configurator;
|
||||
|
||||
/**
|
||||
* Config items for Multi Legacy Check (mostly just data from a filter).
|
||||
*
|
||||
* @since 2.6
|
||||
*/
|
||||
class GF_Config_Legacy_Check_Multi extends GF_Config {
|
||||
|
||||
protected $name = 'gf_legacy_multi';
|
||||
protected $script_to_localize = 'gform_gravityforms';
|
||||
|
||||
/**
|
||||
* Config data.
|
||||
*
|
||||
* @return array[]
|
||||
*/
|
||||
public function data() {
|
||||
/**
|
||||
* Allows users to filter the legacy checks for any form on the page.
|
||||
*
|
||||
* @since 2.5
|
||||
*
|
||||
* @param array
|
||||
*/
|
||||
return apply_filters( 'gform_gf_legacy_multi', array() );
|
||||
}
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace Gravity_Forms\Gravity_Forms\Config\Items;
|
||||
|
||||
use Gravity_Forms\Gravity_Forms\Config\GF_Config;
|
||||
use Gravity_Forms\Gravity_Forms\Config\GF_Config_Collection;
|
||||
use Gravity_Forms\Gravity_Forms\Config\GF_Configurator;
|
||||
|
||||
/**
|
||||
* Config items for Theme Legacy Checks.
|
||||
*
|
||||
* @since 2.6
|
||||
*/
|
||||
class GF_Config_Legacy_Check extends GF_Config {
|
||||
|
||||
protected $name = 'gf_legacy';
|
||||
protected $script_to_localize = 'gform_layout_editor';
|
||||
|
||||
/**
|
||||
* Determine if the config should enqueue its data.
|
||||
*
|
||||
* @since 2.7
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function should_enqueue() {
|
||||
return \GFCommon::is_form_editor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Config data.
|
||||
*
|
||||
* @return array[]
|
||||
*/
|
||||
public function data() {
|
||||
$form = \RGFormsModel::get_form_meta( rgget( 'id' ) );
|
||||
|
||||
return array(
|
||||
'is_legacy' => array(
|
||||
'value' => \GFCommon::is_legacy_markup_enabled( $form ),
|
||||
'default' => 0,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace Gravity_Forms\Gravity_Forms\Config\Items;
|
||||
|
||||
use Gravity_Forms\Gravity_Forms\Config\GF_Config;
|
||||
use Gravity_Forms\Gravity_Forms\Config\GF_Configurator;
|
||||
|
||||
/**
|
||||
* Config items for Multifile Strings
|
||||
*
|
||||
* @since 2.6
|
||||
*/
|
||||
class GF_Config_Multifile extends GF_Config {
|
||||
|
||||
protected $script_to_localize = 'gform_gravityforms';
|
||||
protected $name = 'gform_gravityforms';
|
||||
|
||||
/**
|
||||
* Config data.
|
||||
*
|
||||
* @return array[]
|
||||
*/
|
||||
public function data() {
|
||||
return array(
|
||||
'strings' => array(
|
||||
'invalid_file_extension' => wp_strip_all_tags( __( 'This type of file is not allowed. Must be one of the following: ', 'gravityforms' ) ),
|
||||
'file_uploaded' => wp_strip_all_tags( __( 'File uploaded', 'gravityforms' ) ),
|
||||
'delete_file' => wp_strip_all_tags( __( 'Delete this file', 'gravityforms' ) ),
|
||||
'in_progress' => wp_strip_all_tags( __( 'in progress', 'gravityforms' ) ),
|
||||
'file_exceeds_limit' => wp_strip_all_tags( __( 'File exceeds size limit', 'gravityforms' ) ),
|
||||
'illegal_extension' => wp_strip_all_tags( __( 'This type of file is not allowed.', 'gravityforms' ) ),
|
||||
'max_reached' => wp_strip_all_tags( __( 'Maximum number of files reached', 'gravityforms' ) ),
|
||||
'unknown_error' => wp_strip_all_tags( __( 'There was a problem while saving the file on the server', 'gravityforms' ) ),
|
||||
'currently_uploading' => wp_strip_all_tags( __( 'Please wait for the uploading to complete', 'gravityforms' ) ),
|
||||
'cancel' => wp_strip_all_tags( __( 'Cancel', 'gravityforms' ) ),
|
||||
'cancel_upload' => wp_strip_all_tags( __( 'Cancel this upload', 'gravityforms' ) ),
|
||||
'cancelled' => wp_strip_all_tags( __( 'Cancelled', 'gravityforms' ) ),
|
||||
'error' => wp_strip_all_tags( __( 'Error', 'gravityforms' ) ),
|
||||
'message' => wp_strip_all_tags( __( 'Message', 'gravityforms' ) ),
|
||||
),
|
||||
'vars' => array(
|
||||
'images_url' => \GFCommon::get_base_url() . '/images',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
/**
|
||||
* Handles logic for duplicate submission prevention services.
|
||||
*
|
||||
* @package Gravity_Forms\Gravity_Forms\Duplicate_Submissions
|
||||
*/
|
||||
|
||||
namespace Gravity_Forms\Gravity_Forms\Duplicate_Submissions;
|
||||
|
||||
/**
|
||||
* Class GF_Duplicate_Submissions_Handler
|
||||
*
|
||||
* @since 2.9.1
|
||||
*
|
||||
* Provides functionality for handling duplicate submissions while avoiding multiple
|
||||
* entries being submitted.
|
||||
*/
|
||||
class GF_Duplicate_Submissions_Handler {
|
||||
|
||||
/**
|
||||
* The URL parameter used for redirect protection in Safari.
|
||||
*/
|
||||
const SAFARI_REDIRECT_PARAM = 'gf_protect_submission';
|
||||
|
||||
/**
|
||||
* The base URL for this plugin
|
||||
*
|
||||
* @var string $base_url.
|
||||
*/
|
||||
private $base_url;
|
||||
|
||||
/**
|
||||
* GF_Duplicate_Submissions_Handler constructor.
|
||||
*
|
||||
* @param string $base_url The Base URL for this Plugin.
|
||||
*/
|
||||
public function __construct( $base_url ) {
|
||||
$this->base_url = $base_url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if duplicate submission protection is enabled. false otherwise.
|
||||
*
|
||||
* @returns bool $is_enabled true if duplicate protection is active/enabled. false otherwise
|
||||
*/
|
||||
public function is_enabled() {
|
||||
|
||||
$form_id = filter_input( INPUT_POST, 'gform_submit', FILTER_SANITIZE_NUMBER_INT );
|
||||
|
||||
if ( empty( $form_id ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows users to disable duplicate submissions protection, either globally
|
||||
* or on a form-by-form basis.
|
||||
*
|
||||
* @since 2.5.15
|
||||
*
|
||||
* @param bool Passes a false value by default.
|
||||
* @param int|string Passes the current form ID.
|
||||
*/
|
||||
$is_disabled = gf_apply_filters( array( 'gform_is_disabled_duplicate_submissions_protection', $form_id ), false, $form_id );
|
||||
|
||||
return ! $is_disabled;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue the JS file if this is a form submission configured for duplicate protection.
|
||||
*/
|
||||
public function maybe_enqueue_scripts() {
|
||||
|
||||
if ( $this->is_enabled() ) {
|
||||
$min = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG || rgget( 'gform_debug' ) ? '' : '.min';
|
||||
wp_enqueue_script( 'gform_duplicate_submissions', $this->base_url . "/js/duplicate-submissions{$min}.js", array(), true );
|
||||
wp_localize_script( 'gform_duplicate_submissions', 'gf_duplicate_submissions', $this->get_localized_script_data() );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the correct data to localize to the JS file.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function get_localized_script_data() {
|
||||
return array(
|
||||
'is_gf_submission' => (int) $this->is_valid_submission(),
|
||||
'safari_redirect_param' => self::SAFARI_REDIRECT_PARAM,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current submission exists, and is valid.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function is_valid_submission() {
|
||||
$form_id = filter_input( INPUT_POST, 'gform_submit', FILTER_SANITIZE_NUMBER_INT );
|
||||
|
||||
if ( empty( $form_id ) || ! class_exists( '\GFFormDisplay' ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$entry_id = rgars( \GFFormDisplay::$submission, $form_id . '/lead/id' );
|
||||
|
||||
if ( empty( $entry_id ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
\GFCommon::log_debug( __METHOD__ . sprintf( '(): form #%d. entry #%d.', $form_id, $entry_id ) );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirect to a $_GET request if we detect a dupe submission from Safari.
|
||||
*/
|
||||
public function maybe_handle_safari_redirect() {
|
||||
if ( rgget( self::SAFARI_REDIRECT_PARAM ) != '1' || ! $this->is_enabled() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the submission URL from the $_SERVER, and strip out our redirect param.
|
||||
$submission_url = filter_input( INPUT_SERVER, 'HTTP_REFERER', FILTER_SANITIZE_URL );
|
||||
$base_url = esc_url( remove_query_arg( self::SAFARI_REDIRECT_PARAM, $submission_url ) );
|
||||
|
||||
// Redirect to the form's page URL as a GET request.
|
||||
wp_safe_redirect( $base_url, 303 );
|
||||
exit;
|
||||
}
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
/**
|
||||
* Service Provider for Duplicate Submission Service
|
||||
*
|
||||
* @package Gravity_Forms\Gravity_Forms\Duplicate_Submissions
|
||||
*/
|
||||
|
||||
namespace Gravity_Forms\Gravity_Forms\Duplicate_Submissions;
|
||||
|
||||
use Gravity_Forms\Gravity_Forms\GF_Service_Container;
|
||||
use Gravity_Forms\Gravity_Forms\GF_Service_Provider;
|
||||
use Gravity_Forms\Gravity_Forms\Util\GF_Util_Service_Provider;
|
||||
|
||||
/**
|
||||
* Class GF_License_Service_Provider
|
||||
*
|
||||
* Service provider for the Duplicate Submission Service.
|
||||
*/
|
||||
class GF_Duplicate_Submissions_Service_Provider extends GF_Service_Provider {
|
||||
|
||||
const GF_DUPLICATE_SUBMISSION_HANDLER = 'gf_duplicate_submission_handler';
|
||||
|
||||
/**
|
||||
* Includes all related files and adds all containers.
|
||||
*
|
||||
* @param GF_Service_Container $container Container singleton object.
|
||||
*/
|
||||
public function register( GF_Service_Container $container ) {
|
||||
\GFForms::include_gravity_api();
|
||||
|
||||
require_once plugin_dir_path( __FILE__ ) . 'class-gf-duplicate-submissions-handler.php';
|
||||
|
||||
$container->add(
|
||||
self::GF_DUPLICATE_SUBMISSION_HANDLER,
|
||||
function () {
|
||||
return new GF_Duplicate_Submissions_Handler( \GFCommon::get_base_url() );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes service.
|
||||
*
|
||||
* @param GF_Service_Container $container Service Container.
|
||||
*/
|
||||
public function init( GF_Service_Container $container ) {
|
||||
parent::init( $container );
|
||||
|
||||
$duplicate_submission_handler = $container->get( self::GF_DUPLICATE_SUBMISSION_HANDLER );
|
||||
|
||||
add_action( 'gform_enqueue_scripts', array( $duplicate_submission_handler, 'maybe_enqueue_scripts' ) );
|
||||
add_action( 'wp_loaded', array( $duplicate_submission_handler, 'maybe_handle_safari_redirect' ), 8, 0 );
|
||||
}
|
||||
}
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
<?php
|
||||
|
||||
namespace Gravity_Forms\Gravity_Forms\Editor_Button;
|
||||
|
||||
use Gravity_Forms\Gravity_Forms\Config\GF_Config_Service_Provider;
|
||||
use Gravity_Forms\Gravity_Forms\Editor_Button\Config\GF_Editor_Config;
|
||||
use Gravity_Forms\Gravity_Forms\Editor_Button\Dom\GF_Editor_Button;
|
||||
use Gravity_Forms\Gravity_Forms\Editor_Button\Endpoints\GF_Editor_Save_Editor_Settings;
|
||||
use Gravity_Forms\Gravity_Forms\GF_Service_Container;
|
||||
use Gravity_Forms\Gravity_Forms\GF_Service_Provider;
|
||||
|
||||
/**
|
||||
* Class GF_Editor_Service_Provider
|
||||
*
|
||||
* Service provider for the Embed Form Service.
|
||||
*
|
||||
* @package Gravity_Forms\Gravity_Forms\Editor_Button;
|
||||
*/
|
||||
class GF_Editor_Service_Provider extends GF_Service_Provider {
|
||||
|
||||
// Configs
|
||||
const EDITOR_CONFIG = 'editor_config';
|
||||
const ENDPOINTS_CONFIG = 'editor_endpoints_config';
|
||||
|
||||
// DOM
|
||||
const DOM_EDITOR_BUTTON = 'dom_editor_button';
|
||||
|
||||
/**
|
||||
* Array mapping config class names to their container ID.
|
||||
*
|
||||
* @since 2.8
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $configs = array(
|
||||
self::EDITOR_CONFIG => GF_Editor_Config::class,
|
||||
);
|
||||
|
||||
/**
|
||||
* Register services to the container.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @param GF_Service_Container $container
|
||||
*/
|
||||
public function register( GF_Service_Container $container ) {
|
||||
// Configs
|
||||
require_once( plugin_dir_path( __FILE__ ) . '/config/class-gf-editor-config.php' );
|
||||
|
||||
// Dom
|
||||
require_once( plugin_dir_path( __FILE__ ) . '/dom/class-gf-editor-button.php' );
|
||||
|
||||
// Endpoints
|
||||
require_once( plugin_dir_path( __FILE__ ) . '/endpoints/class-gf-editor-save-editor-settings.php' );
|
||||
|
||||
$container->add( self::ENDPOINTS_CONFIG, function () {
|
||||
return new GF_Editor_Save_Editor_Settings();
|
||||
} );
|
||||
|
||||
$this->add_configs( $container );
|
||||
$this->dom( $container );
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize any actions or hooks.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @param GF_Service_Container $container
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init( GF_Service_Container $container ) {
|
||||
add_action( 'gform_after_toolbar_buttons', function () use ( $container ) {
|
||||
$container->get( self::DOM_EDITOR_BUTTON )->output_button();
|
||||
} );
|
||||
|
||||
add_action( 'wp_ajax_' . GF_Editor_Save_Editor_Settings::ACTION_NAME, function () use ( $container ) {
|
||||
$container->get( self::ENDPOINTS_CONFIG )->handle();
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* For each config defined in $configs, instantiate and add to container.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @param GF_Service_Container $container
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function add_configs( GF_Service_Container $container ) {
|
||||
foreach ( $this->configs as $name => $class ) {
|
||||
$container->add( $name, function () use ( $container, $class ) {
|
||||
return new $class( $container->get( GF_Config_Service_Provider::DATA_PARSER ) );
|
||||
} );
|
||||
|
||||
$container->get( GF_Config_Service_Provider::CONFIG_COLLECTION )->add_config( $container->get( $name ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register DOM-related services.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @param GF_Service_Container $container
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function dom( GF_Service_Container $container ) {
|
||||
$container->add( self::DOM_EDITOR_BUTTON, function() {
|
||||
return new GF_Editor_Button();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the compact view is enabled for the given form and user.
|
||||
*
|
||||
* @since 2.8
|
||||
*
|
||||
* @param int $user_id The user ID.
|
||||
* @param int $form_id The form ID.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_compact_view_enabled( $user_id, $form_id ) {
|
||||
$compact_view = get_user_meta( $user_id, 'gform_compact_view_' . $form_id, true );
|
||||
return $compact_view === 'enable';
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the field ID view in compact view is enabled for the given form and user.
|
||||
*
|
||||
* @since 2.8
|
||||
*
|
||||
* @param int $user_id The user ID.
|
||||
* @param int $form_id The form ID.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_field_id_enabled( $user_id, $form_id ) {
|
||||
$field_id = get_user_meta( $user_id, 'gform_compact_view_show_id_' . $form_id, true );
|
||||
return $field_id === 'enable';
|
||||
}
|
||||
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
namespace Gravity_Forms\Gravity_Forms\Editor_Button\Config;
|
||||
|
||||
use Gravity_Forms\Gravity_Forms\Config\GF_Config;
|
||||
use Gravity_Forms\Gravity_Forms\Editor_Button\GF_Editor_Service_Provider;
|
||||
use Gravity_Forms\Gravity_Forms\Editor_Button\Endpoints\GF_Editor_Save_Editor_Settings;
|
||||
|
||||
/**
|
||||
* Config items for the Editor Settings Button
|
||||
*
|
||||
* @since 2.8
|
||||
*/
|
||||
class GF_Editor_Config extends GF_Config {
|
||||
|
||||
protected $name = 'gform_admin_config';
|
||||
protected $script_to_localize = 'gform_gravityforms_admin_vendors';
|
||||
|
||||
/**
|
||||
* Determine if the config should enqueue its data.
|
||||
*
|
||||
* @since 2.8
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function should_enqueue() {
|
||||
return \GFCommon::is_form_editor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Config data.
|
||||
*
|
||||
* @return array[]
|
||||
*/
|
||||
public function data() {
|
||||
return array(
|
||||
'components' => array(
|
||||
'editor_button' => array(
|
||||
'i18n' => array(
|
||||
'title' => esc_html__( 'Editor Preferences', 'gravityforms' ),
|
||||
'closeButtonAriaLabel' => esc_html__( 'Close button', 'gravityforms' ),
|
||||
'description' => esc_html__( 'Change options related to the form editor.', 'gravityforms' ),
|
||||
'compactToggleLabel' => esc_html__( 'Compact View', 'gravityforms' ),
|
||||
'compactToggleText' => esc_html__( 'Simplify the preview of form fields for a more streamlined editing experience.', 'gravityforms' ),
|
||||
'idToggleLabel' => esc_html__( 'Show Field IDs', 'gravityforms' ),
|
||||
'idToggleText' => esc_html__( 'Show the ID of each field in Compact View.', 'gravityforms' ),
|
||||
),
|
||||
'endpoints' => $this->get_endpoints(),
|
||||
'compactViewEnabled' => GF_Editor_Service_Provider::is_compact_view_enabled( get_current_user_id(), rgget( 'id' ) ),
|
||||
'fieldIdEnabled' => GF_Editor_Service_Provider::is_field_id_enabled( get_current_user_id(), rgget( 'id' ) ),
|
||||
'form' => rgget( 'id' ),
|
||||
),
|
||||
'dropdown_menu' => array(
|
||||
'i18n' => array(
|
||||
'duplicateButtonLabel' => esc_html__( 'Duplicate', 'gravityforms' ),
|
||||
'deleteButtonLabel' => esc_html__( 'Delete', 'gravityforms' ),
|
||||
'dropdownButtonLabel' => esc_html__( 'Dropdown menu button', 'gravityforms' ),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the endpoints for saving the compact view settings.
|
||||
*
|
||||
* @since 2.8
|
||||
*
|
||||
* @return \array[][]
|
||||
*/
|
||||
private function get_endpoints() {
|
||||
return array(
|
||||
'save_editor_settings' => array(
|
||||
'action' => array(
|
||||
'value' => GF_Editor_Save_Editor_Settings::ACTION_NAME,
|
||||
'default' => 'mock_endpoint',
|
||||
),
|
||||
'nonce' => array(
|
||||
'value' => wp_create_nonce( GF_Editor_Save_Editor_Settings::ACTION_NAME ),
|
||||
'default' => 'nonce',
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace Gravity_Forms\Gravity_Forms\Editor_Button\Dom;
|
||||
|
||||
/**
|
||||
* Handle outputting the Embed Button in the UI.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @package Gravity_Forms\Gravity_Forms\Embed_Form\Dom
|
||||
*/
|
||||
class GF_Editor_Button {
|
||||
|
||||
/**
|
||||
* Output the HTML for the Embed Button.
|
||||
*/
|
||||
public function output_button() {
|
||||
?>
|
||||
<button
|
||||
data-js="editor-flyout-trigger"
|
||||
class="gform-button gform-button--icon-white gform-button--icon-editor"
|
||||
aria-label="<?php esc_attr_e( 'Open editor preferences', 'gravityforms' ); ?>"
|
||||
title="<?php esc_attr_e( 'Open editor preferences', 'gravityforms' ); ?>"
|
||||
>
|
||||
<i class="gform-icon gform-icon--cog gform-button__icon" aria-hidden="true"></i>
|
||||
</button>
|
||||
<?php
|
||||
}
|
||||
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace Gravity_Forms\Gravity_Forms\Editor_Button\Endpoints;
|
||||
|
||||
use Gravity_Forms\Gravity_Forms\Config\GF_Config;
|
||||
|
||||
/**
|
||||
* AJAX Endpoint for saving the compact view settings.
|
||||
*
|
||||
* @since 2.8
|
||||
*
|
||||
* @package Gravity_Forms\Gravity_Forms\Editor_Button\Endpoints
|
||||
*/
|
||||
class GF_Editor_Save_Editor_Settings {
|
||||
|
||||
// Strings
|
||||
const ACTION_NAME = 'gf_save_editor_settings';
|
||||
|
||||
/**
|
||||
* Handle the AJAX request.
|
||||
*
|
||||
* @since 2.8
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle() {
|
||||
check_ajax_referer( self::ACTION_NAME );
|
||||
|
||||
$form = intval( rgpost( 'form' ) );
|
||||
$user = get_current_user_id();
|
||||
$name = rgpost( 'name' );
|
||||
$value = rgpost( 'value' );
|
||||
$value = ( $value == 'enable' ) ? 'enable' : 'disable';
|
||||
|
||||
update_user_meta( $user, 'gform_' . $name . '_' . $form, $value );
|
||||
}
|
||||
|
||||
}
|
||||
+153
@@ -0,0 +1,153 @@
|
||||
<?php
|
||||
|
||||
namespace Gravity_Forms\Gravity_Forms\Embed_Form;
|
||||
|
||||
use Gravity_Forms\Gravity_Forms\Config\GF_Config_Service_Provider;
|
||||
use Gravity_Forms\Gravity_Forms\Embed_Form\Config\GF_Embed_Config;
|
||||
use Gravity_Forms\Gravity_Forms\Embed_Form\Config\GF_Embed_Config_I18N;
|
||||
use Gravity_Forms\Gravity_Forms\Embed_Form\Config\GF_Embed_Endpoints_Config;
|
||||
use Gravity_Forms\Gravity_Forms\Embed_Form\Dom\GF_Embed_Button;
|
||||
use Gravity_Forms\Gravity_Forms\Embed_Form\Endpoints\GF_Embed_Endpoint_Create_With_Block;
|
||||
use Gravity_Forms\Gravity_Forms\Embed_Form\Endpoints\GF_Embed_Endpoint_Get_Posts;
|
||||
use Gravity_Forms\Gravity_Forms\GF_Service_Container;
|
||||
use Gravity_Forms\Gravity_Forms\GF_Service_Provider;
|
||||
|
||||
/**
|
||||
* Class GF_Embed_Service_Provider
|
||||
*
|
||||
* Service provider for the Embed Form Service.
|
||||
*
|
||||
* @package Gravity_Forms\Gravity_Forms\Embed_Form;
|
||||
*/
|
||||
class GF_Embed_Service_Provider extends GF_Service_Provider {
|
||||
|
||||
// Configs
|
||||
const EMBED_CONFIG = 'embed_config';
|
||||
const EMBED_CONFIG_I18N = 'embed_config_i18n';
|
||||
const EMBED_CONFIG_ENDPOINTS = 'embed_config_endpoints';
|
||||
|
||||
// Endpoints
|
||||
const ENDPOINT_GET_POSTS = 'endpoint_get_posts';
|
||||
const ENDPOINT_CREATE_WITH_BLOCK = 'endpoint_create_with_block';
|
||||
|
||||
// DOM
|
||||
const DOM_EMBED_BUTTON = 'dom_embed_button';
|
||||
|
||||
// Strings
|
||||
const ADD_BLOCK_PARAM = 'gfAddBlock';
|
||||
|
||||
/**
|
||||
* Array mapping config class names to their container ID.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $configs = array(
|
||||
self::EMBED_CONFIG => GF_Embed_Config::class,
|
||||
self::EMBED_CONFIG_I18N => GF_Embed_Config_I18N::class,
|
||||
self::EMBED_CONFIG_ENDPOINTS => GF_Embed_Endpoints_Config::class,
|
||||
);
|
||||
|
||||
/**
|
||||
* Register services to the container.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @param GF_Service_Container $container
|
||||
*/
|
||||
public function register( GF_Service_Container $container ) {
|
||||
// Configs
|
||||
require_once( plugin_dir_path( __FILE__ ) . '/config/class-gf-embed-config.php' );
|
||||
require_once( plugin_dir_path( __FILE__ ) . '/config/class-gf-embed-config-i18n.php' );
|
||||
require_once( plugin_dir_path( __FILE__ ) . '/config/class-gf-embed-endpoints-config.php' );
|
||||
|
||||
// Endpoints
|
||||
require_once( plugin_dir_path( __FILE__ ) . '/endpoints/class-gf-embed-endpoint-get-posts.php' );
|
||||
require_once( plugin_dir_path( __FILE__ ) . '/endpoints/class-gf-embed-endpoint-create-with-block.php' );
|
||||
|
||||
// Dom
|
||||
require_once( plugin_dir_path( __FILE__ ) . '/dom/class-gf-embed-button.php' );
|
||||
|
||||
$this->add_configs( $container );
|
||||
$this->add_endpoints( $container );
|
||||
$this->dom( $container );
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiailize any actions or hooks.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @param GF_Service_Container $container
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init( GF_Service_Container $container ) {
|
||||
add_action( 'wp_ajax_' . GF_Embed_Endpoint_Get_Posts::ACTION_NAME, function () use ( $container ) {
|
||||
$container->get( self::ENDPOINT_GET_POSTS )->handle();
|
||||
} );
|
||||
|
||||
add_action( 'wp_ajax_' . GF_Embed_Endpoint_Create_With_Block::ACTION_NAME, function () use ( $container ) {
|
||||
$container->get( self::ENDPOINT_CREATE_WITH_BLOCK )->handle();
|
||||
} );
|
||||
|
||||
add_action( 'gform_before_toolbar_buttons', function () use ( $container ) {
|
||||
$container->get( self::DOM_EMBED_BUTTON )->output_button();
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* For each config defined in $configs, instantiate and add to container.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @param GF_Service_Container $container
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function add_configs( GF_Service_Container $container ) {
|
||||
foreach ( $this->configs as $name => $class ) {
|
||||
$container->add( $name, function () use ( $container, $class ) {
|
||||
return new $class( $container->get( GF_Config_Service_Provider::DATA_PARSER ) );
|
||||
} );
|
||||
|
||||
$container->get( GF_Config_Service_Provider::CONFIG_COLLECTION )->add_config( $container->get( $name ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register AJAX endpoints for the Embed UI.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @param GF_Service_Container $container
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function add_endpoints( GF_Service_Container $container ) {
|
||||
$container->add( self::ENDPOINT_GET_POSTS, function () use ( $container ) {
|
||||
return new GF_Embed_Endpoint_Get_Posts();
|
||||
} );
|
||||
|
||||
$container->add( self::ENDPOINT_CREATE_WITH_BLOCK, function () use ( $container ) {
|
||||
return new GF_Embed_Endpoint_Create_With_Block();
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Register DOM-related services.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @param GF_Service_Container $container
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function dom( GF_Service_Container $container ) {
|
||||
$container->add( self::DOM_EMBED_BUTTON, function() {
|
||||
return new GF_Embed_Button();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace Gravity_Forms\Gravity_Forms\Embed_Form\Config;
|
||||
|
||||
use Gravity_Forms\Gravity_Forms\Config\GF_Config;
|
||||
|
||||
/**
|
||||
* Config items for the Embed Form I18N
|
||||
*
|
||||
* @since 2.6
|
||||
*/
|
||||
class GF_Embed_Config_I18N extends GF_Config {
|
||||
|
||||
protected $name = 'gform_admin_config';
|
||||
protected $script_to_localize = 'gform_gravityforms_admin_vendors';
|
||||
|
||||
/**
|
||||
* Determine if the config should enqueue its data.
|
||||
*
|
||||
* @since 2.6.2
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function should_enqueue() {
|
||||
return \GFCommon::is_form_editor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Config data.
|
||||
*
|
||||
* @return array[]
|
||||
*/
|
||||
public function data() {
|
||||
return array(
|
||||
'components' => array(
|
||||
'embed_form' => array(
|
||||
'i18n' => array(
|
||||
'title' => esc_html__( 'Embed Form', 'gravityforms' ),
|
||||
'id' => esc_html__( 'Form ID: %s', 'gravityforms' ),
|
||||
'add_title' => esc_html__( 'Add to Existing Content', 'gravityforms' ),
|
||||
'add_post_type_choice_label' => esc_html__( '%1$sAdd to Existing Content:%2$s %3$s', 'gravityforms' ),
|
||||
'add_dropdown_placeholder' => esc_html__( 'Select a %s', 'gravityforms' ),
|
||||
'add_trigger_aria_text' => esc_html__( 'Select a post', 'gravityforms' ),
|
||||
'add_search_aria_text' => esc_html__( 'Search all %ss', 'gravityforms' ),
|
||||
'add_button_label' => esc_html__( 'Insert Form', 'gravityforms' ),
|
||||
'create_title' => esc_html__( 'Create New', 'gravityforms' ),
|
||||
'create_post_type_choice_label' => esc_html__( '%1$sCreate New:%2$s %3$s', 'gravityforms' ),
|
||||
'create_placeholder' => esc_html__( 'Enter %s Name', 'gravityforms' ),
|
||||
'create_button_label' => esc_html__( 'Create', 'gravityforms' ),
|
||||
'dialog_title' => esc_html__( 'Unsaved Changes', 'gravityforms' ),
|
||||
'dialog_content' => esc_html__( 'Oops! You have unsaved changes in the form, before you can continue with embedding it please save your changes.', 'gravityforms' ),
|
||||
'dialog_confirm_text' => esc_html__( 'Save Changes', 'gravityforms' ),
|
||||
'dialog_confirm_saving' => esc_html__( 'Saving', 'gravityforms' ),
|
||||
'dialog_cancel_text' => esc_html__( 'Cancel', 'gravityforms' ),
|
||||
'dialog_close_title' => esc_html__( 'Close this dialog and return to form editor.', 'gravityforms' ),
|
||||
'shortcode_title' => esc_html__( 'Not Using the Block Editor?', 'gravityforms' ),
|
||||
'shortcode_description' => esc_html__( 'Copy and paste the shortcode within your page builder.', 'gravityforms' ),
|
||||
'shortcode_button_label' => esc_html__( 'Copy Shortcode', 'gravityforms' ),
|
||||
'shortcode_button_copied' => esc_html__( 'Copied', 'gravityforms' ),
|
||||
'shortcode_helper' => esc_html__( '%1$sLearn more%2$s about the shortcode.', 'gravityforms' ),
|
||||
'shortcode_external_link_screen_reader_text' => esc_html__( 'Opens in a new tab', 'gravityforms' ),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
<?php
|
||||
|
||||
namespace Gravity_Forms\Gravity_Forms\Embed_Form\Config;
|
||||
|
||||
use Gravity_Forms\Gravity_Forms\Config\GF_Config;
|
||||
|
||||
/**
|
||||
* Config items for the Embed Form UI.
|
||||
*
|
||||
* @since 2.6
|
||||
*/
|
||||
class GF_Embed_Config extends GF_Config {
|
||||
|
||||
protected $name = 'gform_admin_config';
|
||||
protected $script_to_localize = 'gform_gravityforms_admin_vendors';
|
||||
|
||||
/**
|
||||
* Determine if the config should enqueue its data.
|
||||
*
|
||||
* @since 2.6.2
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function should_enqueue() {
|
||||
return \GFCommon::is_form_editor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Config data.
|
||||
*
|
||||
* @return array[]
|
||||
*/
|
||||
public function data() {
|
||||
return array(
|
||||
'components' => array(
|
||||
'embed_form' => array(
|
||||
'urls' => $this->get_urls(),
|
||||
'data' => array(
|
||||
'form_id' => array(
|
||||
'value' => rgget( 'id' ),
|
||||
'default' => 1,
|
||||
),
|
||||
'post_types' => array(
|
||||
'value' => $this->get_available_post_types(),
|
||||
'default' => $this->placeholder_post_types(),
|
||||
),
|
||||
'items' => array(
|
||||
'value' => $this->get_items_by_type(),
|
||||
'default' => $this->placeholder_items(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the various URLs for the Embed UI.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function get_urls() {
|
||||
$edit_link = '';
|
||||
|
||||
$post_type_object = get_post_type_object( 'page' );
|
||||
if ( ! empty( $post_type_object->_edit_link ) ) {
|
||||
$edit_link = admin_url( str_replace( '%d', '%1$s', $post_type_object->_edit_link ) . '&action=edit' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows the edit post link to be customized.
|
||||
*
|
||||
* @since 2.6.2
|
||||
*
|
||||
* @param string $link The edit link. Use %1$s as the placeholder for the ID.
|
||||
*/
|
||||
$edit_link = apply_filters( 'gform_embed_edit_post_link', $edit_link );
|
||||
|
||||
return [
|
||||
'edit_post' => [
|
||||
'value' => $edit_link,
|
||||
'default' => 'https://gravity.loc/wp-admin/post.php?post=%1$s&action=edit',
|
||||
],
|
||||
'shortcode_docs' => 'https://docs.gravityforms.com/shortcodes/',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Post Types data for the Embed UI.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function get_available_post_types() {
|
||||
$types = array(
|
||||
array(
|
||||
'slug' => 'page',
|
||||
'label' => get_post_type_object( 'page' )->labels->singular_name,
|
||||
),
|
||||
array(
|
||||
'slug' => 'post',
|
||||
'label' => get_post_type_object( 'post' )->labels->singular_name,
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
* Allows users to modify the post types sent as selectable options in the Embed UI.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @param array $types
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
return apply_filters( 'gform_embed_post_types', $types );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the items to localize for each post type.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function get_items_by_type() {
|
||||
$types = $this->get_available_post_types();
|
||||
$data = array();
|
||||
foreach ( $types as $type ) {
|
||||
$slug = $type['slug'];
|
||||
$label = $type['label'];
|
||||
|
||||
$items = get_posts( array( 'post_type' => $slug, 'posts_per_page' => 5 ) );
|
||||
array_walk( $items, function ( &$item ) {
|
||||
$item = array(
|
||||
'value' => $item->ID,
|
||||
'label' => $item->post_title,
|
||||
);
|
||||
} );
|
||||
|
||||
$data[ $slug ]['entries'] = $items;
|
||||
$data[ $slug ]['count'] = $this->get_total_posts_by_type( $slug );
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the totals for the given post type.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @param string $type - The Post Type to query for.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function get_total_posts_by_type( $type ) {
|
||||
$args = array(
|
||||
'post_type' => $type,
|
||||
'post_status' => 'publish',
|
||||
);
|
||||
|
||||
$query = new \WP_Query( $args );
|
||||
|
||||
return $query->found_posts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the placeholder post type values for use in Mocks.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function placeholder_post_types() {
|
||||
return array(
|
||||
array( 'slug' => 'page', 'label' => __( 'Page', 'gravityforms' ) ),
|
||||
array( 'slug' => 'post', 'label' => __( 'Post', 'gravityforms' ) ),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the placeholder post items for use in Mocks.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function placeholder_items() {
|
||||
return array(
|
||||
'post' => array(
|
||||
'count' => 2,
|
||||
'entries' => array(
|
||||
array(
|
||||
'value' => 1,
|
||||
'label' => 'Post One',
|
||||
),
|
||||
array(
|
||||
'value' => 2,
|
||||
'label' => 'Post Two',
|
||||
),
|
||||
),
|
||||
),
|
||||
'page' => array(
|
||||
'count' => 25,
|
||||
'entries' => array(
|
||||
array(
|
||||
'value' => 3,
|
||||
'label' => 'Page Three',
|
||||
),
|
||||
array(
|
||||
'value' => 4,
|
||||
'label' => 'Page Four',
|
||||
),
|
||||
array(
|
||||
'value' => 5,
|
||||
'label' => 'Page Five',
|
||||
),
|
||||
array(
|
||||
'value' => 6,
|
||||
'label' => 'Page Six',
|
||||
),
|
||||
array(
|
||||
'value' => 7,
|
||||
'label' => 'Page Seven',
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
namespace Gravity_Forms\Gravity_Forms\Embed_Form\Config;
|
||||
|
||||
use Gravity_Forms\Gravity_Forms\Config\GF_Config;
|
||||
use Gravity_Forms\Gravity_Forms\Embed_Form\Endpoints\GF_Embed_Endpoint_Create_With_Block;
|
||||
use Gravity_Forms\Gravity_Forms\Embed_Form\Endpoints\GF_Embed_Endpoint_Get_Posts;
|
||||
|
||||
/**
|
||||
* Config items for the Embed Forms REST Endpoints.
|
||||
*
|
||||
* @since 2.6
|
||||
*/
|
||||
class GF_Embed_Endpoints_Config extends GF_Config {
|
||||
|
||||
protected $script_to_localize = 'gform_gravityforms_admin_vendors';
|
||||
protected $name = 'gform_admin_config';
|
||||
protected $overwrite = false;
|
||||
|
||||
/**
|
||||
* Determine if the config should enqueue its data.
|
||||
*
|
||||
* @since 2.6.2
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function should_enqueue() {
|
||||
return \GFCommon::is_form_editor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Config data.
|
||||
*
|
||||
* @return array[]
|
||||
*/
|
||||
public function data() {
|
||||
return array(
|
||||
'components' => array(
|
||||
'embed_form' => array(
|
||||
'endpoints' => $this->get_endpoints(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the various endpoints for the Embed UI.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function get_endpoints() {
|
||||
return array(
|
||||
|
||||
// Endpoint to get posts for typeahead
|
||||
'get_posts' => array(
|
||||
'action' => array(
|
||||
'value' => 'gf_embed_query_posts',
|
||||
'default' => 'mock_endpoint',
|
||||
),
|
||||
'nonce' => array(
|
||||
'value' => wp_create_nonce( GF_Embed_Endpoint_Get_Posts::ACTION_NAME ),
|
||||
'default' => 'nonce',
|
||||
)
|
||||
),
|
||||
|
||||
// Endpoint to create a new page with our block inserted.
|
||||
'create_post_with_block' => array(
|
||||
'action' => array(
|
||||
'value' => GF_Embed_Endpoint_Create_With_Block::ACTION_NAME,
|
||||
'default' => 'mock_endpoint',
|
||||
),
|
||||
'nonce' => array(
|
||||
'value' => wp_create_nonce( GF_Embed_Endpoint_Create_With_Block::ACTION_NAME ),
|
||||
'default' => 'nonce',
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace Gravity_Forms\Gravity_Forms\Embed_Form\Dom;
|
||||
|
||||
/**
|
||||
* Handle outputting the Embed Button in the UI.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @package Gravity_Forms\Gravity_Forms\Embed_Form\Dom
|
||||
*/
|
||||
class GF_Embed_Button {
|
||||
|
||||
/**
|
||||
* Output the HTML for the Embed Button.
|
||||
*/
|
||||
public function output_button() {
|
||||
?>
|
||||
<button data-js="embed-flyout-trigger" class="gform-button gform-button--white gform-button--icon-leading">
|
||||
<i class="gform-button__icon gform-icon gform-icon--embed-alt" aria-hidden="true"></i>
|
||||
<?php esc_html_e( 'Embed', 'gravityforms' ); ?>
|
||||
</button>
|
||||
<?php
|
||||
}
|
||||
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace Gravity_Forms\Gravity_Forms\Embed_Form\Endpoints;
|
||||
|
||||
/**
|
||||
* AJAX Endpoint for creating a new post with a specific block already added.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @package Gravity_Forms\Gravity_Forms\Embed_Form\Endpoints
|
||||
*/
|
||||
class GF_Embed_Endpoint_Create_With_Block {
|
||||
|
||||
// Strings
|
||||
const ACTION_NAME = 'gf_embed_create_post_with_block';
|
||||
|
||||
// Request Params
|
||||
const PARAM_FORM_ID = 'form_id';
|
||||
const PARAM_POST_TYPE = 'post_type';
|
||||
const PARAM_POST_TITLE = 'post_title';
|
||||
|
||||
/**
|
||||
* Handle the AJAX request.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle() {
|
||||
check_ajax_referer( self::ACTION_NAME );
|
||||
|
||||
$post_type = rgpost( self::PARAM_POST_TYPE );
|
||||
$form_id = rgpost( self::PARAM_FORM_ID );
|
||||
$post_title = rgpost( self::PARAM_POST_TITLE );
|
||||
|
||||
if ( empty( $post_type ) || empty( $form_id ) ) {
|
||||
wp_send_json_error( 'Request must include a post_type and form_id.', 400 );
|
||||
}
|
||||
|
||||
$post_data = array(
|
||||
'post_title' => $post_title,
|
||||
'post_type' => $post_type,
|
||||
'post_content' => $this->get_content_for_form( $form_id ),
|
||||
);
|
||||
|
||||
$new_id = wp_insert_post( $post_data );
|
||||
|
||||
wp_send_json_success( array( 'ID' => $new_id ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the properly-formatted comment string for the block we're inserting.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @param $form_id
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function get_content_for_form( $form_id ) {
|
||||
$attrs = array(
|
||||
'formId' => $form_id
|
||||
);
|
||||
|
||||
return get_comment_delimited_block_content( 'gravityforms/form', $attrs, '' );
|
||||
}
|
||||
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace Gravity_Forms\Gravity_Forms\Embed_Form\Endpoints;
|
||||
|
||||
/**
|
||||
* AJAX Endpoint for getting posts based on search params.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @package Gravity_Forms\Gravity_Forms\Embed_Form\Endpoints
|
||||
*/
|
||||
class GF_Embed_Endpoint_Get_Posts {
|
||||
|
||||
// Strings
|
||||
const ACTION_NAME = 'gf_embed_query_posts';
|
||||
|
||||
// Parameters
|
||||
const PARAM_OFFSET = 'offset';
|
||||
const PARAM_COUNT = 'count';
|
||||
const PARAM_STATUS = 'status';
|
||||
const PARAM_SEARCH = 'search';
|
||||
const PARAM_POST_TYPE = 'post_type';
|
||||
|
||||
// Defaults
|
||||
const DEFAULT_OFFSET = 0;
|
||||
const DEFAULT_COUNT = 10;
|
||||
const DEFAULT_STATUS = 'publish';
|
||||
const DEFAULT_SEARCH = '';
|
||||
const DEFAULT_POST_TYPE = 'post';
|
||||
|
||||
/**
|
||||
* Handle the AJAX request.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle() {
|
||||
check_ajax_referer( self::ACTION_NAME );
|
||||
|
||||
$offset = rgpost( self::PARAM_OFFSET ) ? rgpost( self::PARAM_OFFSET ) : self::DEFAULT_OFFSET;
|
||||
$count = rgpost( self::PARAM_COUNT ) ? rgpost( self::PARAM_COUNT ) : self::DEFAULT_COUNT;
|
||||
$status = rgpost( self::PARAM_STATUS ) ? rgpost( self::PARAM_STATUS ) : self::DEFAULT_STATUS;
|
||||
$search = rgpost( self::PARAM_SEARCH ) ? rgpost( self::PARAM_SEARCH ) : self::DEFAULT_SEARCH;
|
||||
$post_type = rgpost( self::PARAM_POST_TYPE ) ? rgpost( self::PARAM_POST_TYPE ) : self::DEFAULT_POST_TYPE;
|
||||
|
||||
$args = array(
|
||||
'post_type' => $post_type,
|
||||
'post_status' => $status,
|
||||
'posts_per_page' => $count,
|
||||
'offset' => $offset,
|
||||
's' => $search,
|
||||
);
|
||||
|
||||
$query = new \WP_Query( $args );
|
||||
|
||||
$posts = $query->get_posts();
|
||||
|
||||
array_walk( $posts, function ( &$post ) {
|
||||
$post = array(
|
||||
'value' => $post->ID,
|
||||
'label' => $post->post_title,
|
||||
);
|
||||
} );
|
||||
|
||||
wp_send_json_success( $posts );
|
||||
}
|
||||
|
||||
}
|
||||
+224
@@ -0,0 +1,224 @@
|
||||
<?php
|
||||
/**
|
||||
* Handles logic for adjusting Gravity Forms to different environment configurations.
|
||||
*
|
||||
* @package Gravity_Forms\Gravity_Forms\Environment_Config
|
||||
*/
|
||||
|
||||
namespace Gravity_Forms\Gravity_Forms\Environment_Config;
|
||||
|
||||
/**
|
||||
* Class GF_Environment_Config_Handler
|
||||
*
|
||||
* @since 2.6.7
|
||||
*
|
||||
* Provides functionality for handling environment configuration options.
|
||||
*/
|
||||
class GF_Environment_Config_Handler {
|
||||
|
||||
/**
|
||||
* Provides caching service.
|
||||
*
|
||||
* @var \GFCache $cache
|
||||
*/
|
||||
protected $cache;
|
||||
|
||||
/**
|
||||
* GF_Environment_Config_Handler constructor.
|
||||
*
|
||||
* @since 2.6.7
|
||||
*
|
||||
* @param \GFCache $cache Provides caching service.
|
||||
*/
|
||||
public function __construct( $cache ) {
|
||||
|
||||
$this->cache = $cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an environment setting from wp_options.
|
||||
*
|
||||
* @since 2.6.7
|
||||
*
|
||||
* @param string $name The environment setting name. Don't include the "gf_env_" prefix.
|
||||
* @param mixed $default Default value to be returned if option is not set.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get_environment_setting( $name, $default = false ) {
|
||||
$option_name = "gf_env_{$name}";
|
||||
$setting = $this->cache->get( $option_name, $found );
|
||||
if ( ! $found ) {
|
||||
$setting = get_option( $option_name, $default );
|
||||
$this->cache->set( $option_name, $setting );
|
||||
}
|
||||
return $setting;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the license_key config value.
|
||||
*
|
||||
* @since 2.6.7
|
||||
*
|
||||
* @return string The license key config.
|
||||
*/
|
||||
public function get_license_key() {
|
||||
return $this->get_environment_setting( 'license_key', false );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the hide_license config value.
|
||||
*
|
||||
* @since 2.6.7
|
||||
*
|
||||
* @return bool Returns true if license is supposed to be hidden from the UI, false otherwise.
|
||||
*/
|
||||
public function get_hide_license() {
|
||||
return (bool) $this->get_environment_setting( 'hide_license', false );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the hide_background_updates config value.
|
||||
*
|
||||
* @since 2.6.9
|
||||
*
|
||||
* @return bool Returns true if the background updates setting is supposed to be hidden from the UI. Returns false otherwise.
|
||||
*/
|
||||
public function get_hide_background_updates() {
|
||||
return (bool) $this->get_environment_setting( 'hide_background_updates', false );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the hide_install_wizard config value.
|
||||
*
|
||||
* @since 2.6.7
|
||||
*
|
||||
* @return bool Returns true if install wizard is supposed to be hidden. Returns false otherwise.
|
||||
*/
|
||||
public function get_hide_install_wizard() {
|
||||
return (bool) $this->get_environment_setting( 'hide_setup_wizard', false );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the hide_update_message config value.
|
||||
*
|
||||
* @since 2.6.7
|
||||
*
|
||||
* @return bool Returns true if install wizard is supposed to be hidden. Returns false otherwise.
|
||||
*/
|
||||
public function get_hide_update_message() {
|
||||
return (bool) $this->get_environment_setting( 'hide_update_message', false );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the support_url config value.
|
||||
*
|
||||
* @since 2.6.7
|
||||
*
|
||||
* @return string The support link config value.
|
||||
*/
|
||||
public function get_support_url() {
|
||||
return $this->get_environment_setting( 'support_url', 'https://www.gravityforms.com/open-support-ticket/' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the unregistered_license_message config value.
|
||||
*
|
||||
* @since 2.6.7
|
||||
*
|
||||
* @return string The message to be displayed when the license is unregistered.
|
||||
*/
|
||||
public function get_unregistered_license_message() {
|
||||
return $this->get_environment_setting( 'unregistered_license_message' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Target of the pre_option_gform_pending_installation filter. Bypasses the installation wizard by returning 0 for the gform_pending_installation option.
|
||||
*
|
||||
* @hook pre_option_gform_pending_installation 10, 1
|
||||
*
|
||||
* @return int Returns 0 if the install wizard is set to be hidden by environment settings. Otherwise return false so that option is not overridden.
|
||||
*/
|
||||
public function maybe_override_gform_pending_installation() {
|
||||
|
||||
// If environment config is set to hide install wizard, override gform_pending_intallation option with 0. Otherwise, use existing option.
|
||||
$hide_install_wizard = $this->get_hide_install_wizard();
|
||||
return $hide_install_wizard ? 0 : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Maybe hides the automatic update message on the plugin's page.
|
||||
*
|
||||
* @since 2.6.8
|
||||
*/
|
||||
public function maybe_hide_plugin_page_message() {
|
||||
|
||||
if ( $this->get_hide_update_message() ) {
|
||||
remove_filter( 'transient_update_plugins', array( 'GFForms', 'check_update' ) );
|
||||
remove_filter( 'site_transient_update_plugins', array( 'GFForms', 'check_update' ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maybe hides the automatic update message on the Update page.
|
||||
*
|
||||
* @since 2.6.8
|
||||
*
|
||||
* @hook gform_updates_list, 20
|
||||
*
|
||||
* @param array $updates Updates array being filtered.
|
||||
*/
|
||||
public function maybe_hide_update_page_message( $updates ) {
|
||||
|
||||
if ( ! $this->get_hide_update_message() ) {
|
||||
return $updates;
|
||||
}
|
||||
|
||||
foreach ( $updates as & $update ) {
|
||||
if ( $update['slug'] == 'gravityforms' ) {
|
||||
$update['latest_version'] = '';
|
||||
}
|
||||
}
|
||||
|
||||
return $updates;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Target of the pre_option_rg_gforms_key filter. Uses the license key configured by the environment settings if one is set.
|
||||
*
|
||||
* @hook pre_option_rg_gforms_key 10, 1
|
||||
*
|
||||
* @since 2.6.7
|
||||
*
|
||||
* @return string Returns the environment license key if one is set. If not set, return false so that value is not overridden.
|
||||
*/
|
||||
public function maybe_override_rg_gforms_key() {
|
||||
|
||||
// Use environment license key if one is set. Otherwise, use rg_gforms_key option.
|
||||
$env_license_key = $this->get_license_key();
|
||||
return $env_license_key !== false ? $env_license_key : false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Target of the gform_plugin_settings_fields filter. Removes sections from the settings page that are configured to be hidden.
|
||||
*
|
||||
* @since 2.6.9
|
||||
*
|
||||
* @param array $fields The settings fields to be filtered.
|
||||
*
|
||||
* @return array Returns the filtered $fields array.
|
||||
*/
|
||||
public function maybe_hide_setting( $fields ) {
|
||||
|
||||
if ( $this->get_hide_license() ) {
|
||||
unset( $fields['license_key'] );
|
||||
unset( $fields['license_key_details'] );
|
||||
}
|
||||
if ( $this->get_hide_background_updates() ) {
|
||||
unset( $fields['background_updates'] );
|
||||
}
|
||||
return $fields;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user