- WordPress 6.9.4 (es_ES) with Kadence theme - Homepage: Hero, La Asociación, Pilares, Beneficios, Eventos, Miembros, Hazte Miembro, Contacto - Brand identity: #13294b navy, #a12932 burgundy, #c69c48 gold - Fonts: Raleway (headings) + Source Sans 3 (body) + Lato (UI) - Plugins: Kadence Blocks, Polylang, Contact Form 7 - Custom CSS with full brand styling and responsive layout - HTTPS enforced via wp-config.php proxy detection
529 lines
14 KiB
PHP
529 lines
14 KiB
PHP
<?php
|
|
|
|
/**
|
|
* Class Kadence_Blocks_Import_Export
|
|
*
|
|
* Creates a zip file for Kadence Custom Post Types.
|
|
* Will include all related sub custom post types and post meta.
|
|
*/
|
|
|
|
if (!defined('ABSPATH')) {
|
|
exit;
|
|
}
|
|
|
|
class Kadence_Blocks_Cpt_Import_Export
|
|
{
|
|
/**
|
|
* Post type slug
|
|
*
|
|
* @var string
|
|
*/
|
|
private $slug = '';
|
|
|
|
/**
|
|
* Blocks that are based on CPT
|
|
*
|
|
* @var array
|
|
*/
|
|
private $kadence_cpt_blocks = array(
|
|
'kadence/header',
|
|
'kadence/navigation',
|
|
'kadence/query',
|
|
'kadence/query-card',
|
|
'kadence/advanced-form',
|
|
);
|
|
|
|
/*
|
|
* Post meta keys to exclude from export.
|
|
*/
|
|
private $excluded_meta_keys = array(
|
|
'_edit_lock',
|
|
);
|
|
|
|
/**
|
|
* Posts to process
|
|
*
|
|
* @var array
|
|
*/
|
|
private $processed_posts = array();
|
|
|
|
/**
|
|
* ID map for related posts
|
|
*
|
|
* @var array
|
|
*/
|
|
private $relationship_map = array();
|
|
|
|
public function __construct($slug = '') {
|
|
if (!empty($slug)) {
|
|
$this->slug = $slug;
|
|
add_action('admin_menu', array($this, 'add_import_export_buttons'), 20);
|
|
add_action('admin_post_kadence_export_posts-' . $slug, array($this, 'handle_export'));
|
|
add_action('admin_post_kadence_import_posts-' . $slug, array($this, 'handle_import'));
|
|
add_action('admin_notices', array($this, 'display_import_notices'));
|
|
|
|
add_action('admin_enqueue_scripts', array($this, 'enqueue_scripts'));
|
|
add_action('admin_footer', array($this, 'add_import_modal_styles'));
|
|
add_filter('bulk_actions-edit-' . $slug, array($this, 'register_bulk_export_action'));
|
|
add_filter('handle_bulk_actions-edit-' . $slug, array($this, 'handle_bulk_export'), 10, 3);
|
|
|
|
}
|
|
}
|
|
|
|
public function handle_bulk_export($redirect_to, $action, $post_ids)
|
|
{
|
|
if ($action !== 'export_selected') {
|
|
return $redirect_to;
|
|
}
|
|
|
|
if (!current_user_can('manage_options')) {
|
|
wp_die(__('You do not have sufficient permissions to export content.', 'kadence-blocks'));
|
|
}
|
|
|
|
$this->handle_export($post_ids, false);
|
|
}
|
|
|
|
public function enqueue_scripts($hook) {
|
|
if ('edit.php' === $hook && $this->slug === get_current_screen()->post_type) {
|
|
wp_register_script(
|
|
'kadence-cpt-import-export',
|
|
'', // Empty source as we're using inline script
|
|
array('jquery'),
|
|
'1.0',
|
|
true
|
|
);
|
|
|
|
wp_enqueue_script('kadence-cpt-import-export');
|
|
|
|
wp_add_inline_script('kadence-cpt-import-export', '
|
|
jQuery(document).ready(function($) {
|
|
// Move buttons to after views
|
|
var $buttons = $(".kadence-import-export-buttons");
|
|
$buttons.insertAfter(".subsubsub");
|
|
|
|
// Handle import button click
|
|
$("#kadence-reveal-import").on("click", function(e) {
|
|
e.preventDefault();
|
|
$(".kadence-import-form").slideToggle();
|
|
});
|
|
});
|
|
');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Register bulk action
|
|
*/
|
|
public function register_bulk_export_action($bulk_actions) {
|
|
$bulk_actions['export_selected'] = __('Export', 'kadence-blocks');
|
|
return $bulk_actions;
|
|
}
|
|
|
|
/**
|
|
* Add styles for import modal
|
|
*/
|
|
public function add_import_modal_styles() {
|
|
if (get_current_screen()->post_type !== $this->slug) {
|
|
return;
|
|
}
|
|
?>
|
|
<style>
|
|
.subsubsub {
|
|
margin-bottom: 0;
|
|
float: left;
|
|
}
|
|
.kadence-import-export-buttons {
|
|
float: left;
|
|
margin: 5px 0 0 15px;
|
|
padding: 0;
|
|
}
|
|
.kadence-import-export-buttons form {
|
|
display: inline-block;
|
|
}
|
|
.kadence-import-form {
|
|
display: none;
|
|
clear: both;
|
|
margin: 10px 0;
|
|
padding: 10px;
|
|
background: #fff;
|
|
border: 1px solid #ccd0d4;
|
|
border-radius: 4px;
|
|
box-shadow: 0 1px 1px rgba(0,0,0,.04);
|
|
}
|
|
.kadence-import-form form {
|
|
display: flex;
|
|
gap: 10px;
|
|
align-items: center;
|
|
}
|
|
.kadence-import-form:before {
|
|
content: "";
|
|
display: table;
|
|
clear: both;
|
|
}
|
|
.search-box {
|
|
float: right;
|
|
}
|
|
@media screen and (max-width: 782px) {
|
|
.kadence-import-export-buttons {
|
|
display: none;
|
|
}
|
|
}
|
|
</style>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Display admin notices for import success/failure
|
|
*/
|
|
public function display_import_notices() {
|
|
if ( empty($_GET['post_type']) || $_GET['post_type'] !== $this->slug || empty($_GET['import_status'])) {
|
|
return;
|
|
}
|
|
|
|
if ($_GET['import_status'] === 'success') {
|
|
?>
|
|
<div class="notice notice-success is-dismissible">
|
|
<p><?php _e('Import completed successfully!', 'kadence-blocks'); ?></p>
|
|
</div>
|
|
<?php
|
|
} elseif ($_GET['import_status'] === 'error' && isset($_GET['error_message'])) {
|
|
?>
|
|
<div class="notice notice-error is-dismissible">
|
|
<p><?php echo esc_html(urldecode($_GET['error_message'])); ?></p>
|
|
</div>
|
|
<?php
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add import/export buttons to the post type admin page
|
|
*/
|
|
public function add_import_export_buttons() {
|
|
global $pagenow, $typenow;
|
|
|
|
if ('edit.php' === $pagenow && $this->slug === $typenow && current_user_can('manage_options') ) {
|
|
add_action('admin_notices', array($this, 'render_import_export_buttons'));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Render the import/export buttons
|
|
*/
|
|
public function render_import_export_buttons() {
|
|
?>
|
|
<div class="kadence-import-export-buttons">
|
|
<form method="post" action="<?php echo admin_url('admin-post.php'); ?>" style="display: inline-block; margin-right: 10px;">
|
|
<?php wp_nonce_field('kadence_export_posts_nonce', 'kadence_export_nonce'); ?>
|
|
<input type="hidden" name="action" value="kadence_export_posts-<?php echo esc_attr($this->slug); ?>">
|
|
<input type="hidden" name="post_type" value="<?php echo esc_attr($this->slug); ?>">
|
|
<input type="submit" class="button button-secondary" value="<?php esc_attr_e('Export All', 'kadence-blocks'); ?>">
|
|
</form>
|
|
|
|
<button id="kadence-reveal-import" class="button button-secondary">
|
|
<?php esc_html_e('Import', 'kadence-blocks'); ?>
|
|
</button>
|
|
|
|
<div class="kadence-import-form">
|
|
<form method="post" action="<?php echo admin_url('admin-post.php'); ?>" enctype="multipart/form-data">
|
|
<?php wp_nonce_field('kadence_import_posts_nonce', 'kadence_import_nonce'); ?>
|
|
<input type="hidden" name="action" value="kadence_import_posts-<?php echo esc_attr($this->slug); ?>">
|
|
<input type="hidden" name="post_type" value="<?php echo esc_attr($this->slug); ?>">
|
|
<input type="file" name="import_file" accept=".zip" required>
|
|
<input type="submit" class="button button-primary" value="<?php esc_attr_e('Upload & Import', 'kadence-blocks'); ?>">
|
|
</form>
|
|
</div>
|
|
</div>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Export to zip and download.
|
|
*/
|
|
public function handle_export( $post_ids = array(), $check_referrer = true ) {
|
|
if (!current_user_can('manage_options')) {
|
|
$this->redirect_with_error( __('You do not have sufficient permissions to export content.', 'kadence-blocks') );
|
|
}
|
|
|
|
if( $check_referrer ) {
|
|
check_admin_referer('kadence_export_posts_nonce', 'kadence_export_nonce');
|
|
}
|
|
|
|
$posts_args = array(
|
|
'post_type' => $this->slug,
|
|
'posts_per_page' => -1,
|
|
'post_status' => 'any',
|
|
);
|
|
|
|
if( ! empty( $post_ids ) ) {
|
|
$posts_args['post__in'] = $post_ids;
|
|
}
|
|
|
|
$posts = get_posts($posts_args);
|
|
|
|
$export_data = array(
|
|
'post_type' => $this->slug,
|
|
'posts' => array(),
|
|
'related_posts' => array(),
|
|
'relationship_map' => array()
|
|
);
|
|
|
|
$this->processed_posts = array();
|
|
$this->relationship_map = array();
|
|
|
|
|
|
foreach ($posts as $post) {
|
|
$post_data = $this->prepare_post_for_export($post);
|
|
$export_data['posts'][] = $post_data;
|
|
$this->processed_posts[] = $post->ID;
|
|
|
|
$this->get_related_posts_recursive($post->post_content, $export_data['related_posts'], $post->ID);
|
|
}
|
|
|
|
$export_data['related_posts'] = array_values(array_unique($export_data['related_posts'], SORT_REGULAR));
|
|
$export_data['relationship_map'] = $this->relationship_map;
|
|
|
|
$temp_dir = get_temp_dir();
|
|
$filename = $this->slug . '_export_' . date('Y-m-d_H-i-s');
|
|
$json_file = $temp_dir . $filename . '.json';
|
|
$zip_file = $temp_dir . $filename . '.zip';
|
|
|
|
file_put_contents($json_file, wp_json_encode($export_data));
|
|
|
|
$zip = new ZipArchive();
|
|
$zip->open($zip_file, ZipArchive::CREATE | ZipArchive::OVERWRITE);
|
|
$zip->addFile($json_file, 'export.json');
|
|
$zip->close();
|
|
|
|
header('Content-Type: application/zip');
|
|
header('Content-Disposition: attachment; filename=' . basename($zip_file));
|
|
header('Content-Length: ' . filesize($zip_file));
|
|
readfile($zip_file);
|
|
|
|
unlink($json_file);
|
|
unlink($zip_file);
|
|
}
|
|
|
|
/**
|
|
* Recursively get related posts
|
|
*/
|
|
private function get_related_posts_recursive($content, &$related_posts, $parent_id = null) {
|
|
$blocks = parse_blocks($content);
|
|
|
|
foreach ($blocks as $block) {
|
|
if( in_array( $block['blockName'], $this->kadence_cpt_blocks ) && !empty( $block['attrs']['id'] ) ) {
|
|
$nested_id = $block['attrs']['id'];
|
|
|
|
if ($parent_id !== null) {
|
|
if (!isset($this->relationship_map[$parent_id])) {
|
|
$this->relationship_map[$parent_id] = [];
|
|
}
|
|
$this->relationship_map[$parent_id][] = $nested_id;
|
|
}
|
|
|
|
if ( ! in_array( $nested_id, $this->processed_posts ) ) {
|
|
$nested_post = get_post( $nested_id );
|
|
if ( $nested_post ) {
|
|
$related_posts[] = $this->prepare_post_for_export( $nested_post );
|
|
$this->processed_posts[] = $nested_id;
|
|
$this->get_related_posts_recursive($nested_post->post_content, $related_posts, $nested_id);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!empty($block['innerBlocks'])) {
|
|
foreach ($block['innerBlocks'] as $inner_block) {
|
|
$this->get_related_posts_recursive(serialize_blocks(array($inner_block)), $related_posts, $parent_id);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public function handle_import() {
|
|
if (!current_user_can('manage_options')) {
|
|
$this->redirect_with_error( __('You do not have sufficient permissions to import content.', 'kadence-blocks' ) );
|
|
return;
|
|
}
|
|
|
|
check_admin_referer('kadence_import_posts_nonce', 'kadence_import_nonce');
|
|
|
|
if (!isset($_FILES['import_file'])) {
|
|
$this->redirect_with_error( __( 'Please upload a file to import.', 'kadence-blocks' ) );
|
|
return;
|
|
}
|
|
|
|
$file = $_FILES['import_file'];
|
|
|
|
if ($file['error'] !== UPLOAD_ERR_OK) {
|
|
$this->redirect_with_error( __('File upload failed.', 'kadence-blocks' ) );
|
|
return;
|
|
}
|
|
|
|
if ($file['type'] !== 'application/zip' && $file['type'] !== 'application/x-zip-compressed') {
|
|
$this->redirect_with_error( __('Invalid file type. Please upload a ZIP file.', 'kadence-blocks' ) );
|
|
return;
|
|
}
|
|
|
|
$temp_dir = get_temp_dir();
|
|
|
|
$zip = new ZipArchive();
|
|
if ($zip->open($file['tmp_name']) === TRUE) {
|
|
if ($zip->numFiles > 5) {
|
|
$this->redirect_with_error( __('Invalid data in import file.', 'kadence-blocks' ) );
|
|
return;
|
|
}
|
|
|
|
$zip->extractTo($temp_dir);
|
|
$zip->close();
|
|
|
|
if (!file_exists($temp_dir . 'export.json')) {
|
|
$this->redirect_with_error( __( 'Invalid import file structure. Missing export.json', 'kadence-blocks' ) );
|
|
return;
|
|
}
|
|
|
|
$json_content = file_get_contents($temp_dir . 'export.json');
|
|
$import_data = json_decode($json_content, true);
|
|
|
|
if (json_last_error() !== JSON_ERROR_NONE) {
|
|
$this->redirect_with_error( __('Invalid JSON data in import file.', 'kadence-blocks' ) );
|
|
return;
|
|
}
|
|
|
|
try {
|
|
|
|
$id_map = array();
|
|
|
|
if (!empty($import_data['related_posts'])) {
|
|
foreach ($import_data['related_posts'] as $related_post) {
|
|
$new_id = $this->import_single_post($related_post);
|
|
if ($new_id) {
|
|
$id_map[$related_post['ID']] = $new_id;
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach (array_merge($import_data['related_posts'], $import_data['posts']) as $post_data) {
|
|
$post_content = $post_data['post_content'];
|
|
$updated_content = $this->update_block_ids($post_content, $id_map);
|
|
|
|
$new_post_id = $id_map[$post_data['ID']] ?? null;
|
|
|
|
if ($new_post_id) {
|
|
wp_update_post(array(
|
|
'ID' => $new_post_id,
|
|
'post_content' => wp_slash($updated_content)
|
|
));
|
|
}
|
|
}
|
|
|
|
foreach ($import_data['posts'] as $post_data) {
|
|
$this->import_single_post($post_data, $id_map);
|
|
}
|
|
|
|
unlink($temp_dir . 'export.json');
|
|
|
|
wp_redirect(add_query_arg(
|
|
array(
|
|
'post_type' => $this->slug,
|
|
'import_status' => 'success'
|
|
),
|
|
admin_url('edit.php')
|
|
));
|
|
exit;
|
|
|
|
} catch (Exception $e) {
|
|
$this->redirect_with_error('Import failed: ' . $e->getMessage());
|
|
return;
|
|
}
|
|
} else {
|
|
$this->redirect_with_error('Failed to process the import file.');
|
|
return;
|
|
}
|
|
}
|
|
|
|
private function prepare_post_for_export($post) {
|
|
$post_data = array(
|
|
'ID' => $post->ID,
|
|
'post_content' => $post->post_content,
|
|
'post_title' => $post->post_title,
|
|
'post_excerpt' => $post->post_excerpt,
|
|
'post_status' => $post->post_status,
|
|
'post_type' => $post->post_type,
|
|
'meta' => array()
|
|
);
|
|
|
|
$meta = get_post_custom($post->ID);
|
|
foreach ($meta as $key => $values) {
|
|
if ( ! in_array( $key, $this->excluded_meta_keys ) ) {
|
|
$post_data['meta'][$key] = array_map('maybe_unserialize', $values);
|
|
}
|
|
}
|
|
|
|
return $post_data;
|
|
}
|
|
|
|
private function import_single_post($post_data, $id_map = array()) {
|
|
$temp_content = $post_data['post_content'];
|
|
$post_data['post_content'] = '';
|
|
unset($post_data['ID']);
|
|
|
|
$new_post_id = wp_insert_post($post_data, true);
|
|
|
|
if (!is_wp_error($new_post_id)) {
|
|
if (!empty($post_data['meta'])) {
|
|
foreach ($post_data['meta'] as $meta_key => $meta_values) {
|
|
foreach ($meta_values as $meta_value) {
|
|
add_post_meta($new_post_id, $meta_key, $meta_value);
|
|
}
|
|
}
|
|
}
|
|
|
|
$updated_content = $this->update_block_ids($temp_content, $id_map);
|
|
wp_update_post(array(
|
|
'ID' => $new_post_id,
|
|
'post_content' => wp_slash($updated_content)
|
|
));
|
|
|
|
return $new_post_id;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Update block ID in content with new ID
|
|
*/
|
|
private function update_block_ids($content, $id_map) {
|
|
$blocks = parse_blocks($content);
|
|
|
|
foreach ($blocks as &$block) {
|
|
if ( in_array( $block['blockName'], $this->kadence_cpt_blocks )
|
|
&& !empty($block['attrs']['id'])
|
|
&& isset($id_map[$block['attrs']['id']])) {
|
|
$block['attrs']['id'] = $id_map[$block['attrs']['id']];
|
|
}
|
|
|
|
if (!empty($block['innerBlocks'])) {
|
|
$inner_content = serialize_blocks($block['innerBlocks']);
|
|
$updated_inner_content = $this->update_block_ids($inner_content, $id_map);
|
|
$block['innerBlocks'] = parse_blocks($updated_inner_content);
|
|
}
|
|
}
|
|
|
|
return serialize_blocks($blocks);
|
|
}
|
|
|
|
private function redirect_with_error($message) {
|
|
wp_redirect(add_query_arg(
|
|
array(
|
|
'post_type' => $this->slug,
|
|
'import_status' => 'error',
|
|
'error_message' => urlencode($message)
|
|
),
|
|
admin_url('edit.php')
|
|
),
|
|
302
|
|
);
|
|
exit;
|
|
}
|
|
}
|