Last active
May 3, 2024 08:46
-
-
Save wpmudev-sls/ef60ae7796c18fa5773d17a469164790 to your computer and use it in GitHub Desktop.
[SmartCrawl Pro] Post meta export (JSON and CSV)
This file contains 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: [SmartCrawl Pro] Post meta export (JSON and CSV) (Rev. 3) | |
* Description: Adds a "SmartCrawl Export" admin bar menu where the user can easily export the site data | |
* Author: Anderson Salas @ WPMUDEV | |
* Task: SLS-4067 | |
* Author URI: https://premium.wpmudev.org | |
* License: GPLv2 or later | |
*/ | |
/* | |
* Post meta import: https://gist.github.com/wpmudev-sls/02a03fb6ef57b7a09c0fac7ddcbde393 | |
*/ | |
add_action( 'plugins_loaded', function() { | |
if ( ! class_exists( '\SmartCrawl\SmartCrawl' ) ) { | |
return; // SmartCrawl is not installed/enabled. | |
} | |
if ( ! class_exists( 'SmartCrawl_Custom_Meta_Exporter' ) ) { | |
class SmartCrawl_Custom_Meta_Exporter { | |
private static $instance; | |
private static $columns = array( | |
'post_id', | |
'post_type', | |
'post_name', | |
'post_title', | |
'meta_title', | |
'meta_description', | |
'focus_keywords', | |
'og_title', | |
'og_description', | |
'og_image', | |
'tw_title', | |
'tw_description', | |
'tw_image', | |
'canonical', | |
'noindex', | |
'nofollow', | |
'robotsadv', | |
'autolinks', | |
'redirect', | |
); | |
private $post_types = array(); | |
private $excluded_slugs = array(); | |
public static function get() { | |
if ( null === self::$instance ) { | |
self::$instance = new self(); | |
} | |
return self::$instance; | |
} | |
public function __construct() { | |
$this->register_menus(); | |
} | |
public function get_sql_post_types() { | |
return implode( ',', array_map( | |
function( $item ) { | |
return "'" . $item . "'"; | |
}, $this->post_types ) | |
); | |
} | |
/** | |
* @deprecated | |
*/ | |
public function set_post_types( $post_types ) { | |
$this->post_types = $post_types; | |
return $this; | |
} | |
private function validate( $action = null ) { | |
if ( null !== $action && ! empty( $_REQUEST['_wpnonce'] ) && ! wp_verify_nonce( $_REQUEST['_wpnonce'], $action ) ) { | |
return false; // Invalid nonce. | |
} | |
return is_admin() && current_user_can( 'manage_options' ); | |
} | |
private function register_menus() { | |
if ( ! $this->validate() ) { | |
return; | |
} | |
$nonce = wp_create_nonce( 'sc_custom_meta_export' ); | |
add_action('admin_bar_menu', function( $wp_admin_bar ) use( $nonce ) { | |
$wp_admin_bar->add_node( array( | |
'id' => 'smartcrawl_export', | |
'title' => 'SmartCrawl Export', | |
)); | |
$wp_admin_bar->add_node( array( | |
'id' => 'smartcrawl_export_json', | |
'title' => 'Export Post Meta (JSON)', | |
'parent' => 'smartcrawl_export', | |
'href' => admin_url( '?sc_export_meta=json&_wpnonce=' . $nonce ), | |
)); | |
$wp_admin_bar->add_node( array( | |
'id' => 'smartcrawl_export_csv', | |
'title' => 'Export Post Meta (CSV)', | |
'parent' => 'smartcrawl_export', | |
'href' => admin_url( '?sc_export_meta=csv&_wpnonce=' . $nonce ), | |
)); | |
$wp_admin_bar->add_node( array( | |
'id' => 'smartcrawl_export_custom', | |
'title' => 'Custom export...', | |
'parent' => 'smartcrawl_export', | |
'href' => admin_url( 'admin.php?page=wds_export' ), | |
)); | |
}, 999 ); | |
add_action( 'admin_menu', array( $this, 'add_submenu_item' ) ); | |
add_action( 'admin_footer', array( $this, 'admin_exporter_scripts' ) ); | |
} | |
public function add_submenu_item() { | |
add_submenu_page( | |
\SmartCrawl\Settings::TAB_DASHBOARD, | |
'SmartCrawl Export', | |
'SmartCrawl Export', | |
'manage_options', | |
'wds_export', | |
array( $this, 'admin_exporter_page' ) | |
); | |
} | |
public function admin_exporter_scripts() { | |
?> | |
<script> | |
( function( $ ) { | |
$( '.cpt-all' ).click( function(e) { | |
e.preventDefault(); | |
$('input.cpt').prop('checked', true); | |
}); | |
$( '.cpt-clear' ).click( function(e) { | |
e.preventDefault(); | |
$('input.cpt').prop('checked', false); | |
}); | |
$( '.cpt-all, .cpt-clear, .cpt-label' ).click( function(e) { | |
$('.sc-begin-export').attr('disabled','disabled'); | |
if ( $('input.cpt:checked').length > 0 ) { | |
$('.sc-begin-export').removeAttr('disabled'); | |
} | |
}); | |
})( window.jQuery ) | |
</script> | |
<?php | |
} | |
private function default_post_types() { | |
$post_types = array( 'post', 'page' ); | |
$custom_types = get_post_types( array( | |
'public' => true, | |
'_builtin' => false | |
)); | |
if ( ! empty( $custom_types ) ) { | |
$post_types = array_merge( $post_types, array_keys( $custom_types ) ); | |
} | |
return $post_types; | |
} | |
public function admin_exporter_page() { | |
?> | |
<div style="max-width:500px; background: white; border: solid 1px #dadada; padding: 10px 20px 20px 20px; margin-top: 20px;"> | |
<form id="sc-export-form" method="post" enctype="multipart/form-data"> | |
<?php wp_nonce_field( 'sc_custom_meta_export' ); ?> | |
<h3>SmartCrawl Export</h3> | |
<p>Select the output format (CSV or JSON) and click <strong>Export</strong> to begin the export.</p> | |
<div class="advanced-import-container"> | |
<h5>Output format:</h5> | |
<div> | |
<label style="display:inline-block; min-width:70px"><input type="radio" name="sc_export_meta" value="csv" required /> CSV</label> | |
<label style="display:inline-block; min-width:70px"><input type="radio" name="sc_export_meta" value="json" /> JSON</label> | |
</div> | |
<h5>Post types: <div style="float:right"><a href="#" class="cpt-all">[Select all]</a> <a href="#" class="cpt-clear">[Clear]</a></div></h5> | |
<div class="cpt-selector"> | |
<?php | |
foreach( $this->default_post_types() as $i => $post_type ) { | |
?> | |
<label class="cpt-label"> | |
<input type="checkbox" class="cpt" name="post_types[<?php echo $post_type; ?>]" checked="checked" /> <?php echo ucfirst( str_ireplace( '_', ' ', $post_type ) ); ?> | |
</label> | |
<?php } ?> | |
</div> | |
<h5>Excluded post names:</h5> | |
<input type="text" name="excluded_slugs" style="display:block; width: 100%" placeholder="Comma-separated slugs. Example: hello-world, hoodie-with-logo"/> | |
<div style="margin-top: 15px"> | |
<input type="submit" id="submit" class="button button-primary sc-begin-export" value="Export" /> | |
</div> | |
</div> | |
</form> | |
</div> | |
<style> | |
.cpt-selector { | |
border: solid 1px #d2d2d2; | |
height: 141px; | |
overflow-y:auto; | |
} | |
.cpt-selector label { | |
display: block; | |
padding: 5px; | |
} | |
.cpt-selector label:hover, .cpt-selector label:focus { | |
background: #e7f3ff; | |
} | |
</style> | |
<?php | |
} | |
private function get_entries() { | |
global $wpdb; | |
$types = $this->get_sql_post_types(); | |
$prefix = $wpdb->prefix; | |
$query = | |
"SELECT | |
p.ID post_id, | |
p.post_type, | |
p.post_name, | |
p.post_title, | |
( SELECT meta_value FROM {$prefix}postmeta WHERE post_id = p.id AND meta_key = '_wds_title' LIMIT 1 ) meta_title, | |
( SELECT meta_value FROM {$prefix}postmeta WHERE post_id = p.id AND meta_key = '_wds_metadesc' LIMIT 1 ) meta_desc, | |
( SELECT meta_value FROM {$prefix}postmeta WHERE post_id = p.id AND meta_key = '_wds_focus-keywords' LIMIT 1 ) focus_keywords, | |
( SELECT meta_value FROM {$prefix}postmeta WHERE post_id = p.id AND meta_key = '_wds_opengraph' LIMIT 1 ) og_tags, | |
( SELECT meta_value FROM {$prefix}postmeta WHERE post_id = p.id AND meta_key = '_wds_twitter' LIMIT 1 ) tw_tags, | |
( SELECT meta_value FROM {$prefix}postmeta WHERE post_id = p.id AND meta_key = '_wds_canonical' LIMIT 1 ) canonical, | |
( SELECT meta_value FROM {$prefix}postmeta WHERE post_id = p.id AND meta_key = '_wds_meta-robots-noindex' LIMIT 1 ) noindex, | |
( SELECT meta_value FROM {$prefix}postmeta WHERE post_id = p.id AND meta_key = '_wds_meta-robots-nofollow' LIMIT 1 ) nofollow, | |
( SELECT meta_value FROM {$prefix}postmeta WHERE post_id = p.id AND meta_key = '_wds_meta-robots-adv' LIMIT 1 ) robotsadv, | |
( SELECT meta_value FROM {$prefix}postmeta WHERE post_id = p.id AND meta_key = '_wds_autolinks-exclude' LIMIT 1 ) autolinks, | |
( SELECT meta_value FROM {$prefix}postmeta WHERE post_id = p.id AND meta_key = '_wds_redirect' LIMIT 1 ) redirect | |
FROM | |
{$prefix}posts p | |
WHERE | |
p.post_type IN ($types) | |
AND p.post_parent = 0;"; | |
$result = $wpdb->get_results( $query, ARRAY_A ); | |
$entries = array(); | |
if ( ! empty( $result ) && is_array( $result ) ) { | |
foreach ( $result as $item ) { | |
if ( in_array( $item['post_name'], $this->excluded_slugs ) ) { | |
continue; | |
} | |
$entry = array( | |
$item['post_id'], | |
$item['post_type'], | |
$item['post_name'], | |
$item['post_title'], | |
$item['meta_title'], | |
$item['meta_desc'], | |
$item['focus_keywords'], | |
null, // 7:og_title | |
null, // 8:og_description | |
null, // 9:og_image_id | |
null, // 10:tw_title | |
null, // 11:tw_descrciption | |
null, // 12:tw_image_id | |
$item['canonical'], | |
$item['noindex'], | |
$item['nofollow'], | |
$item['robotsadv'], | |
$item['autolinks'], | |
$item['redirect'], | |
); | |
if ( ! empty( $item['og_tags'] ) ) { | |
$og_tag = maybe_unserialize( $item['og_tags'] ); | |
$entry[7] = ! empty( $og_tag['title'] ) ? $og_tag['title'] : null; | |
$entry[8] = ! empty( $og_tag['description'] ) ? $og_tag['description'] : null; | |
$entry[9] = ! empty( $og_tag['images'] ) ? $og_tag['images'] : null; | |
} | |
if ( ! empty( $item['tw_tags'] ) ) { | |
$tw_tag = maybe_unserialize( $item['tw_tags'] ); | |
$entry[10] = ! empty( $tw_tag['title'] ) ? $tw_tag['title'] : null; | |
$entry[11] = ! empty( $tw_tag['description'] ) ? $tw_tag['description'] : null; | |
$entry[12] = ! empty( $tw_tag['images'] ) ? $tw_tag['images'] : null; | |
} | |
$entries[] = $entry; | |
} | |
} | |
return $entries; | |
} | |
public function export( $format ) { | |
if ( empty( $format ) || ! $this->validate( 'sc_custom_meta_export' ) ) { | |
return; | |
} | |
if ( ! empty( $_REQUEST['post_types'] ) && is_array( $_REQUEST['post_types'] ) ) { | |
$post_types = array(); | |
foreach( array_keys( $_REQUEST['post_types'] ) as $type ) { | |
$post_types[] = $type; | |
} | |
$this->post_types = $post_types; | |
} else { | |
$this->post_types = $this->default_post_types(); | |
} | |
if ( ! empty( $_REQUEST['excluded_slugs'] ) ) { | |
$excluded_slugs = array(); | |
foreach( explode(',', $_REQUEST['excluded_slugs'] ) as $slug ) { | |
$slug = trim( $slug ); | |
if ( ! empty( $slug ) ) { | |
$excluded_slugs[] = $slug; | |
} | |
} | |
$this->excluded_slugs = $excluded_slugs; | |
} | |
if ( 'json' === $format ) { | |
$this->export_json(); | |
} else if ( 'csv' === $format ) { | |
$this->export_csv(); | |
} | |
} | |
private function export_json() { | |
$json = array(); | |
foreach( $this->get_entries() as $entry ) { | |
$json[] = array_combine( self::$columns, $entry ); | |
} | |
header( 'Content-Type: application/json' ); | |
header( 'Content-Disposition: attachment; filename="smartcrawl_meta_export.json"' ); | |
die( json_encode( $json, JSON_PRETTY_PRINT ) ); | |
} | |
private function export_csv() { | |
$csv = fopen( 'php://temp/maxmemory:' . ( 5 * 1024 * 1024 ), 'r+' ) ; | |
fputcsv( $csv, self::$columns ); | |
foreach( $this->get_entries() as $entry ) { | |
if ( ! empty( $entry[9] ) && is_array( $entry[9] ) ) { | |
$entry[9] = implode( ',', $entry[9] ); // og_image | |
} | |
if ( ! empty( $entry[12] ) && is_array( $entry[12] ) ) { | |
$entry[12] = implode( ',', $entry[12] ); // tw_image | |
} | |
fputcsv( $csv, $entry ); | |
} | |
rewind( $csv ); | |
header( 'Content-Type: text/csv' ); | |
header( 'Content-Disposition: attachment; filename="smartcrawl_meta_export.csv"' ); | |
die( stream_get_contents( $csv ) ); | |
} | |
} | |
SmartCrawl_Custom_Meta_Exporter::get(); | |
} | |
add_action( 'admin_init', function() { | |
if ( isset( $_REQUEST['sc_export_meta'] ) ) { | |
( SmartCrawl_Custom_Meta_Exporter::get() )->export( $_REQUEST['sc_export_meta'] ); | |
} | |
}); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment