Created
September 2, 2025 01:52
-
-
Save westcoastdigital/dd58040befba8c0a109a7ad6081c42a5 to your computer and use it in GitHub Desktop.
This plugin adds functionality to export Gravity Forms entries and then import into another site. Ensures no duplicates
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Plugin Name: Gravity Forms Entry Transfer | |
* Plugin URI: https://jonmather.au | |
* Description: Export and import Gravity Forms entries between WordPress sites with duplicate prevention | |
* Version: 1.0.0 | |
* Author: Jon Mather | |
* License: GPL v2 or later | |
*/ | |
// Prevent direct access | |
if (!defined('ABSPATH')) { | |
exit; | |
} | |
class GF_Entry_Transfer { | |
private $plugin_url; | |
private $plugin_path; | |
public function __construct() { | |
$this->plugin_url = plugin_dir_url(__FILE__); | |
$this->plugin_path = plugin_dir_path(__FILE__); | |
add_action('init', array($this, 'init')); | |
} | |
public function init() { | |
// Check if Gravity Forms is active | |
if (!class_exists('GFForms')) { | |
add_action('admin_notices', array($this, 'gf_not_active_notice')); | |
return; | |
} | |
// Add admin menu | |
add_action('admin_menu', array($this, 'add_admin_menu')); | |
// Handle AJAX requests | |
add_action('wp_ajax_gf_export_entries', array($this, 'export_entries')); | |
add_action('wp_ajax_gf_import_entries', array($this, 'import_entries')); | |
// Enqueue scripts and styles | |
add_action('admin_enqueue_scripts', array($this, 'enqueue_scripts')); | |
} | |
public function gf_not_active_notice() { | |
?> | |
<div class="notice notice-error"> | |
<p><?php _e('Gravity Forms Entry Transfer requires Gravity Forms to be installed and activated.', 'gf-entry-transfer'); ?></p> | |
</div> | |
<?php | |
} | |
public function add_admin_menu() { | |
add_submenu_page( | |
'gf_edit_forms', | |
'Entry Transfer', | |
'Entry Transfer', | |
'manage_options', | |
'gf-entry-transfer', | |
array($this, 'admin_page') | |
); | |
} | |
public function enqueue_scripts($hook) { | |
if ($hook !== 'forms_page_gf-entry-transfer') { | |
return; | |
} | |
// Enqueue jQuery | |
wp_enqueue_script('jquery'); | |
// Localize script for AJAX | |
wp_localize_script('jquery', 'gf_transfer_ajax', array( | |
'ajax_url' => admin_url('admin-ajax.php'), | |
'nonce' => wp_create_nonce('gf_entry_transfer_nonce') | |
)); | |
// Add inline styles | |
$this->add_admin_styles(); | |
} | |
public function add_admin_styles() { | |
?> | |
<style> | |
.gf-transfer-section { | |
background: #fff; | |
border: 1px solid #ccd0d4; | |
border-radius: 4px; | |
margin: 20px 0; | |
padding: 20px; | |
} | |
.gf-transfer-section h3 { | |
margin-top: 0; | |
color: #23282d; | |
} | |
.form-list { | |
max-height: 200px; | |
overflow-y: auto; | |
border: 1px solid #ddd; | |
padding: 10px; | |
background: #f9f9f9; | |
} | |
.form-list label { | |
display: block; | |
margin: 5px 0; | |
} | |
.gf-btn { | |
background: #0073aa; | |
color: white; | |
padding: 8px 16px; | |
border: none; | |
border-radius: 3px; | |
cursor: pointer; | |
text-decoration: none; | |
display: inline-block; | |
} | |
.gf-btn:hover { | |
background: #005a87; | |
} | |
#import-file { | |
margin: 10px 0; | |
} | |
</style> | |
<?php | |
} | |
public function admin_page() { | |
$forms = GFAPI::get_forms(); | |
?> | |
<div class="wrap"> | |
<h1>Gravity Forms Entry Transfer</h1> | |
<!-- Export Section --> | |
<div class="gf-transfer-section"> | |
<h3>Export Entries</h3> | |
<p>Select the forms you want to export entries from:</p> | |
<div class="form-list"> | |
<label> | |
<input type="checkbox" id="select-all-export"> <strong>Select All</strong> | |
</label> | |
<hr> | |
<?php foreach ($forms as $form): ?> | |
<label> | |
<input type="checkbox" name="export_forms[]" value="<?php echo esc_attr($form['id']); ?>"> | |
<?php echo esc_html($form['title']); ?> (ID: <?php echo esc_html($form['id']); ?>) - | |
<?php echo GFAPI::count_entries($form['id']); ?> entries | |
</label> | |
<?php endforeach; ?> | |
</div> | |
<p> | |
<button id="export-btn" class="gf-btn">Export Selected Forms</button> | |
</p> | |
<div id="export-status"></div> | |
</div> | |
<!-- Import Section --> | |
<div class="gf-transfer-section"> | |
<h3>Import Entries</h3> | |
<p>Select the JSON export file to import:</p> | |
<input type="file" id="import-file" accept=".json" /> | |
<p> | |
<button id="import-btn" class="gf-btn">Import Entries</button> | |
</p> | |
<div id="import-status"></div> | |
<div style="margin-top: 20px; padding: 15px; background: #fff3cd; border: 1px solid #ffeaa7; border-radius: 4px;"> | |
<h4>Important Notes:</h4> | |
<ul> | |
<li>Entries will only be imported if a form with the same ID exists on this site</li> | |
<li>Duplicate entries are detected by comparing form ID, date created, and field values</li> | |
<li>File uploads and images will not be transferred (only the original filenames will be preserved)</li> | |
<li>Make sure you have sufficient server memory and execution time for large imports</li> | |
</ul> | |
</div> | |
</div> | |
</div> | |
<script type="text/javascript"> | |
jQuery(document).ready(function($) { | |
// Select all functionality | |
$('#select-all-export').change(function() { | |
$('input[name="export_forms[]"]').prop('checked', this.checked); | |
}); | |
// Export functionality | |
$('#export-btn').click(function(e) { | |
e.preventDefault(); | |
var selectedForms = []; | |
$('input[name="export_forms[]"]:checked').each(function() { | |
selectedForms.push($(this).val()); | |
}); | |
if (selectedForms.length === 0) { | |
alert('Please select at least one form to export.'); | |
return; | |
} | |
$('#export-status').html('<p>Exporting entries...</p>'); | |
$.ajax({ | |
url: gf_transfer_ajax.ajax_url, | |
type: 'POST', | |
data: { | |
action: 'gf_export_entries', | |
forms: selectedForms, | |
nonce: gf_transfer_ajax.nonce | |
}, | |
success: function(response) { | |
if (response.success) { | |
// var debugHtml = response.data.debug ? '<br><strong>Debug Info:</strong><br>' + response.data.debug : ''; | |
var debugHtm = ""; | |
$('#export-status').html('<p style="color: green;">Export completed! ' + response.data.count + ' entries exported from ' + response.data.forms + ' forms.</p>' + debugHtml); | |
// Create download link | |
var blob = new Blob([response.data.json], {type: 'application/json'}); | |
var url = window.URL.createObjectURL(blob); | |
var a = document.createElement('a'); | |
a.style.display = 'none'; | |
a.href = url; | |
a.download = 'gf-entries-export-' + new Date().toISOString().slice(0, 10) + '.json'; | |
document.body.appendChild(a); | |
a.click(); | |
window.URL.revokeObjectURL(url); | |
} else { | |
$('#export-status').html('<p style="color: red;">Export failed: ' + response.data + '</p>'); | |
} | |
}, | |
error: function() { | |
$('#export-status').html('<p style="color: red;">Export failed due to server error.</p>'); | |
} | |
}); | |
}); | |
// Import functionality | |
$('#import-btn').click(function(e) { | |
e.preventDefault(); | |
var fileInput = document.getElementById('import-file'); | |
if (!fileInput.files.length) { | |
alert('Please select a file to import.'); | |
return; | |
} | |
var file = fileInput.files[0]; | |
var reader = new FileReader(); | |
reader.onload = function(e) { | |
try { | |
var jsonData = JSON.parse(e.target.result); | |
$('#import-status').html('<p>Importing entries...</p>'); | |
$.ajax({ | |
url: gf_transfer_ajax.ajax_url, | |
type: 'POST', | |
data: { | |
action: 'gf_import_entries', | |
json_data: JSON.stringify(jsonData), | |
nonce: gf_transfer_ajax.nonce | |
}, | |
success: function(response) { | |
if (response.success) { | |
$('#import-status').html('<p style="color: green;">Import completed! ' + response.data.imported + ' entries imported, ' + response.data.duplicates + ' duplicates skipped.</p>'); | |
} else { | |
$('#import-status').html('<p style="color: red;">Import failed: ' + response.data + '</p>'); | |
} | |
}, | |
error: function() { | |
$('#import-status').html('<p style="color: red;">Import failed due to server error.</p>'); | |
} | |
}); | |
} catch (error) { | |
alert('Invalid JSON file. Please select a valid export file.'); | |
} | |
}; | |
reader.readAsText(file); | |
}); | |
}); | |
</script> | |
<?php | |
} | |
public function export_entries() { | |
// Verify nonce | |
if (!wp_verify_nonce($_POST['nonce'], 'gf_entry_transfer_nonce')) { | |
wp_die('Security check failed'); | |
} | |
if (!current_user_can('manage_options')) { | |
wp_send_json_error('Insufficient permissions'); | |
return; | |
} | |
$form_ids = isset($_POST['forms']) ? $_POST['forms'] : array(); | |
if (empty($form_ids)) { | |
wp_send_json_error('No forms selected'); | |
return; | |
} | |
// Increase memory limit and execution time for large exports | |
ini_set('memory_limit', '512M'); | |
set_time_limit(300); // 5 minutes | |
$export_data = array( | |
'export_date' => current_time('mysql'), | |
'site_url' => get_site_url(), | |
'gf_version' => GFForms::$version, | |
'forms' => array() | |
); | |
$total_entries = 0; | |
$debug_info = array(); | |
foreach ($form_ids as $form_id) { | |
$form = GFAPI::get_form($form_id); | |
if (!$form) { | |
$debug_info[] = "Form ID {$form_id}: Form not found"; | |
continue; | |
} | |
// Get entries with no limit to ensure we get all entries | |
$search_criteria = array(); | |
$sorting = null; | |
$paging = array('offset' => 0, 'page_size' => 999999); // Get all entries | |
$entries = GFAPI::get_entries($form_id, $search_criteria, $sorting, $paging); | |
if (is_wp_error($entries)) { | |
$debug_info[] = "Form ID {$form_id}: Error retrieving entries - " . $entries->get_error_message(); | |
continue; | |
} | |
$entry_count = count($entries); | |
$debug_info[] = "Form ID {$form_id} ({$form['title']}): Retrieved {$entry_count} entries"; | |
$export_data['forms'][] = array( | |
'form' => $form, | |
'entries' => $entries | |
); | |
$total_entries += $entry_count; | |
} | |
// Add debug information to export | |
$export_data['debug_info'] = $debug_info; | |
wp_send_json_success(array( | |
'json' => json_encode($export_data), | |
'count' => $total_entries, | |
'forms' => count($export_data['forms']), | |
'debug' => implode('<br>', $debug_info) | |
)); | |
} | |
public function import_entries() { | |
// Verify nonce | |
if (!wp_verify_nonce($_POST['nonce'], 'gf_entry_transfer_nonce')) { | |
wp_die('Security check failed'); | |
} | |
if (!current_user_can('manage_options')) { | |
wp_send_json_error('Insufficient permissions'); | |
return; | |
} | |
$json_data = json_decode(stripslashes($_POST['json_data']), true); | |
if (!$json_data || !isset($json_data['forms'])) { | |
wp_send_json_error('Invalid import data'); | |
return; | |
} | |
$imported_count = 0; | |
$duplicate_count = 0; | |
foreach ($json_data['forms'] as $form_data) { | |
$form_id = $form_data['form']['id']; | |
// Check if form exists on this site | |
$existing_form = GFAPI::get_form($form_id); | |
if (!$existing_form) { | |
continue; // Skip if form doesn't exist | |
} | |
foreach ($form_data['entries'] as $entry_data) { | |
// Check for duplicate entry | |
if ($this->is_duplicate_entry($entry_data)) { | |
$duplicate_count++; | |
continue; | |
} | |
// Prepare entry for import | |
$entry = $this->prepare_entry_for_import($entry_data, $form_id); | |
// Import the entry | |
$result = GFAPI::add_entry($entry); | |
if (!is_wp_error($result)) { | |
$imported_count++; | |
} | |
} | |
} | |
wp_send_json_success(array( | |
'imported' => $imported_count, | |
'duplicates' => $duplicate_count | |
)); | |
} | |
private function is_duplicate_entry($entry_data) { | |
global $wpdb; | |
$form_id = $entry_data['form_id']; | |
$date_created = $entry_data['date_created']; | |
// Get existing entries for this form created on the same date | |
$existing_entries = GFAPI::get_entries($form_id, array( | |
'start_date' => date('Y-m-d', strtotime($date_created)), | |
'end_date' => date('Y-m-d', strtotime($date_created . ' +1 day')) | |
)); | |
if (is_wp_error($existing_entries)) { | |
return false; | |
} | |
foreach ($existing_entries as $existing_entry) { | |
// Compare key fields to detect duplicates | |
$is_duplicate = true; | |
// Compare date created (within 1 minute tolerance) | |
if (abs(strtotime($existing_entry['date_created']) - strtotime($date_created)) > 60) { | |
$is_duplicate = false; | |
continue; | |
} | |
// Compare field values | |
foreach ($entry_data as $key => $value) { | |
if (is_numeric($key) && isset($existing_entry[$key])) { | |
if ($existing_entry[$key] !== $value) { | |
$is_duplicate = false; | |
break; | |
} | |
} | |
} | |
if ($is_duplicate) { | |
return true; | |
} | |
} | |
return false; | |
} | |
private function prepare_entry_for_import($entry_data, $form_id) { | |
// Remove auto-generated fields that should be set by GF | |
unset($entry_data['id']); | |
// Ensure form_id is correct | |
$entry_data['form_id'] = $form_id; | |
// Set import timestamp but preserve original date in meta if needed | |
$original_date = $entry_data['date_created']; | |
$entry_data['date_created'] = current_time('mysql'); | |
$entry_data['date_updated'] = current_time('mysql'); | |
return $entry_data; | |
} | |
} | |
// Initialize the plugin | |
new GF_Entry_Transfer(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Gravity Forms Entry Transfer
Export and import Gravity Forms entries between WordPress sites with built-in duplicate prevention.
Features
Requirements
Installation
Since this plugin is distributed as a single PHP file via a Gist, youβll need to create the plugin manually.
Option 1: Install directly on the server
Copy the PHP code from the Gist file.
On your WordPress server, navigate to:
Create a new folder named:
Inside that folder, create a file named:
Paste the copied PHP code into that file and save it.
In the WordPress admin, go to Plugins β Installed Plugins and activate Gravity Forms Entry Transfer.
Option 2: Create locally and upload as ZIP
On your computer, create a new folder named:
Inside that folder, create a file named:
Copy the PHP code from the Gist into that file and save it.
Compress the folder into a ZIP file named:
In the WordPress admin, go to Plugins β Add New β Upload Plugin.
Upload the ZIP file and activate the plugin.
Usage
Notes
License
This plugin is licensed under the GPL v2 or later.