Skip to content

Instantly share code, notes, and snippets.

@wpmudev-sls
Last active May 3, 2024 08:46
Show Gist options
  • Save wpmudev-sls/ef60ae7796c18fa5773d17a469164790 to your computer and use it in GitHub Desktop.
Save wpmudev-sls/ef60ae7796c18fa5773d17a469164790 to your computer and use it in GitHub Desktop.
[SmartCrawl Pro] Post meta export (JSON and CSV)
<?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