importers = $importers;
add_action( 'admin_menu', array( $this, 'add_menu' ), 100 );
add_filter( 'plugin_action_links_' . AIR_WP_SYNC_BASENAME, array( $this, 'plugin_action_links' ) );
add_filter( 'manage_airwpsync-connection_posts_columns', array( $this, 'admin_table_columns' ), 10, 1 );
add_action( 'manage_airwpsync-connection_posts_custom_column', array( $this, 'admin_table_columns_html' ), 10, 2 );
add_action( 'add_meta_boxes', array( $this, 'add_meta_boxes' ) );
add_action( 'admin_enqueue_scripts', array( $this, 'register_styles_scripts' ) );
add_action( 'in_admin_header', array( $this, 'in_admin_header' ) );
add_action( 'edit_form_top', array( $this, 'add_header' ) );
add_action( 'dbx_post_sidebar', array( $this, 'add_footer' ) );
add_action( 'wp_insert_post_data', array( $this, 'wp_insert_post_data' ), 10, 2 );
add_filter( 'redirect_post_location', array( $this, 'redirect_post_location' ), 10, 2 );
add_action( 'admin_notices', array( $this, 'admin_notices' ), 10 );
add_action( 'wp_ajax_air_wp_sync_trigger_update', array( $this, 'trigger_update' ) );
add_filter( 'post_updated_messages', array( $this, 'post_updated_messages' ) );
add_action( 'wp_ajax_air_wp_sync_check_airtable_settings', array( $this, 'check_airtable_settings' ) );
}
/**
* Display plugin header
*/
public function in_admin_header() {
$screen = get_current_screen();
if ( 'airwpsync-connection' === $screen->post_type ) {
include_once AIR_WP_SYNC_PLUGIN_DIR . 'views/header.php';
}
}
/**
* Output wrapper opening for alpinejs x-data
*/
public function add_header( $post ) {
if ( 'airwpsync-connection' === get_post_type() ) {
$config = json_decode( $post->post_content, true );
$json = $config ? wp_json_encode( $config ) : '{}';
// Json output is already encoded
// phpcs:ignore
echo '';
echo '
';
echo '
' . esc_html__( 'Error:', 'airwpsync' ) . ' ' . esc_html__( 'Some required fields are missing.', 'airwpsync' ) . '
';
}
}
/**
* Output wrapper closing for alpinejs x-data
*/
public function add_footer() {
if ( 'airwpsync-connection' === get_post_type() ) {
echo '
';
}
}
/**
* Add menu
*/
public function add_menu() {
add_menu_page(
__( 'Air WP Sync', 'airwpsync' ),
__( 'Air WP Sync', 'airwpsync' ),
apply_filters( 'airwpsync/manage_options_capability', 'manage_options' ),
'edit.php?post_type=airwpsync-connection',
false,
'data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHZpZXdCb3g9IjAgMCA0MyAzNiIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CiAgICA8cGF0aCBkPSJNMTksMC40TDMuMSw3Yy0wLjksMC40LTAuOSwxLjYsMCwyTDE5LDE1LjNjMS40LDAuNiwzLDAuNiw0LjMsMEwzOS4zLDljMC45LTAuNCwwLjktMS42LDAtMkwyMy41LDAuNCBDMjItMC4xLDIwLjQtMC4xLDE5LDAuNCAiIGZpbGw9ImN1cnJlbnRDb2xvciIvPgogICAgPHBhdGggZD0iTTIyLjYsMTguN3YxNS43YzAsMC43LDAuOCwxLjMsMS41LDFsMTcuNy02LjkgYzAuNC0wLjIsMC43LTAuNiwwLjctMVYxMS44YzAtMC43LTAuOC0xLjMtMS41LTFsLTE3LjcsNi45QzIyLjksMTcuOSwyMi42LDE4LjMsMjIuNiwxOC43IiBmaWxsPSJjdXJyZW50Q29sb3IiLz4KICAgIDxwYXRoIGQ9Ik0xOC41LDE5LjVsLTUuMywyLjVsLTAuNSwwLjNMMS42LDI3LjYgQzAuOSwyOCwwLDI3LjUsMCwyNi43VjExLjljMC0wLjMsMC4xLTAuNSwwLjMtMC43QzAuNCwxMS4xLDAuNSwxMSwwLjYsMTFjMC4zLTAuMiwwLjYtMC4yLDEtMC4xbDE2LjgsNi43IEMxOS4zLDE3LjksMTkuMywxOS4xLDE4LjUsMTkuNSIgZmlsbD0iY3VycmVudENvbG9yIi8+Cjwvc3ZnPg=='
);
add_submenu_page(
'edit.php?post_type=airwpsync-connection',
__( 'All Connections', 'airwpsync' ),
__( 'All Connections', 'airwpsync' ),
apply_filters( 'airwpsync/manage_options_capability', 'manage_options' ),
'edit.php?post_type=airwpsync-connection'
);
add_submenu_page(
'edit.php?post_type=airwpsync-connection',
__( 'Add New', 'airwpsync' ),
__( 'Add New', 'airwpsync' ),
apply_filters( 'airwpsync/manage_options_capability', 'manage_options' ),
'post-new.php?post_type=airwpsync-connection'
);
}
/**
* Show action links on the plugin screen
*/
public function plugin_action_links( $links ) {
return array_merge(
$links,
array(
'settings' => '' . esc_html__( 'Settings', 'airwpsync' ) . '',
)
);
}
/**
* Customizes the admin table columns.
*/
public function admin_table_columns( $_columns ) {
$columns = array(
'cb' => $_columns['cb'],
'title' => $_columns['title'],
'modified-date' => __( 'Last Modified On', 'airwpsync' ),
'post-type' => __( 'Post Type', 'airwpsync' ),
'sync-date' => __( 'Last Synced On', 'airwpsync' ),
'sync-trigger' => __( 'Trigger', 'airwpsync' ),
);
return $columns;
}
/**
* Renders the admin table column HTML
*
* @param string $column_name The name of the column to display.
* @param int $post_id The current post ID.
* @return void
*/
public function admin_table_columns_html( $column_name, $post_id ) {
$importer = $this->get_importer_by_id( $post_id );
if ( ! $importer ) {
return;
}
switch ( $column_name ) {
case 'post-type':
$post_type = $importer->config()->get( 'post_type' );
$post_type_object = get_post_type_object( $post_type );
if ( $post_type_object ) {
echo ' ';
echo esc_html( $post_type_object->labels->name );
}
break;
case 'sync-trigger':
if ( 'manual' === $importer->config()->get( 'scheduled_sync.type' ) ) {
esc_html_e( 'Manual only', 'airwpsync' );
}
if ( 'cron' === $importer->config()->get( 'scheduled_sync.type' ) ) {
esc_html_e( 'Recurring', 'airwpsync' );
if ( $importer->config()->get( 'scheduled_sync.recurrence' ) ) {
$schedules = wp_get_schedules();
foreach ( $schedules as $slug => $schedule ) {
if ( $slug === $importer->config()->get( 'scheduled_sync.recurrence' ) ) {
echo esc_html( ' (' . $schedule['display'] . ')' );
}
}
}
}
break;
case 'sync-date':
$last_updated = get_post_meta( $post_id, 'last_updated', true );
echo esc_html( $last_updated ? Air_WP_Sync_Helpers::get_formatted_date_time( $last_updated ) : '--' );
break;
case 'modified-date':
$last_modified = $importer->infos()->get( 'modified' );
echo esc_html( $last_modified ? Air_WP_Sync_Helpers::get_formatted_date_time( $last_modified ) : '--' );
break;
}
}
/**
* Add metaboxes for importer post type
*/
public function add_meta_boxes() {
// Settings
add_meta_box(
'air-wp-sync-global-settings',
__( 'Airtable Settings', 'airwpsync' ),
array( $this, 'display_airtable_settings_metabox' ),
'airwpsync-connection',
'normal',
'high'
);
// Post Settings
add_meta_box(
'air-wp-sync-post-settings',
__( 'Import As...', 'airwpsync' ),
array( $this, 'display_post_settings_metabox' ),
'airwpsync-connection',
'normal',
'high'
);
// Mapping
$tooltip_html = '?';
add_meta_box(
'air-wp-sync-mapping',
__( 'Field Mapping', 'airwpsync' ) . $tooltip_html,
array( $this, 'display_mapping_metabox' ),
'airwpsync-connection',
'normal',
'high'
);
// Import Infos
add_meta_box(
'air-wp-sync-infos',
__( 'Actions', 'airwpsync' ),
array( $this, 'display_side_metabox' ),
'airwpsync-connection',
'side'
);
// Sync Settings
add_meta_box(
'air-wp-sync-sync-settings',
__( 'Sync Settings', 'airwpsync' ),
array( $this, 'display_sync_metabox' ),
'airwpsync-connection',
'normal'
);
}
/**
* Output global settings metabox HTML
*/
public function display_airtable_settings_metabox( $post ) {
include_once AIR_WP_SYNC_PLUGIN_DIR . 'views/metabox-airtable-settings.php';
}
/**
* Output mapping metabox HTML
*/
public function display_mapping_metabox( $post ) {
$wp_fields = apply_filters( 'airwpsync/get_wp_fields', array() );
include_once AIR_WP_SYNC_PLUGIN_DIR . 'views/metabox-mapping.php';
}
/**
* Output post settings metabox HTML
*/
public function display_post_settings_metabox( $post ) {
$post_types = $this->get_post_types();
$post_stati = $this->get_post_stati();
$post_authors = $this->get_post_authors();
include_once AIR_WP_SYNC_PLUGIN_DIR . 'views/metabox-post-settings.php';
}
/**
* Output side metabox HTML
*/
public function display_side_metabox( $post ) {
$importer = $this->get_importer_by_id( $post->ID );
include_once AIR_WP_SYNC_PLUGIN_DIR . 'views/metabox-side.php';
}
/**
* Output sync metabox HTML
*/
public function display_sync_metabox( $post ) {
$sync_strategies = $this->get_sync_strategies();
$schedules = $this->get_schedules();
include_once AIR_WP_SYNC_PLUGIN_DIR . 'views/metabox-sync.php';
}
/**
* Register admin styles and scripts
*/
public function register_styles_scripts() {
wp_enqueue_style( 'air-wp-sync-admin', plugins_url( 'assets/css/admin-page.css', __DIR__ ), false, AIR_WP_SYNC_VERSION );
$screen = get_current_screen();
if ( is_object( $screen ) && 'airwpsync-connection' === $screen->id ) {
wp_enqueue_script( 'air-wp-sync-alpine', plugins_url( 'assets/js/alpinejs@3.10.2.min.js', __DIR__ ), false, AIR_WP_SYNC_VERSION, false );
wp_enqueue_script( 'air-wp-sync-admin', plugins_url( 'assets/js/admin-page.js', __DIR__ ), array( 'air-wp-sync-alpine' ), AIR_WP_SYNC_VERSION, false );
}
}
/**
* Save post
*/
public function wp_insert_post_data( $data, $postarr ) {
if ( 'airwpsync-connection' !== $postarr['post_type'] ) {
return $data;
}
$post_id = $postarr['ID'] ?? 0;
// Limit active importers
if ( ! isset( $data['post_status'] ) || $data['post_status'] === 'publish' ) {
$importers = get_posts(
array(
'post_type' => 'airwpsync-connection',
'post_status' => 'publish',
'posts_per_page' => -1,
'post__not_in' => array( $post_id ),
)
);
if ( count( $importers ) > 0 ) {
$data['post_status'] = 'draft';
$this->display_max_connection = true;
}
}
// Update slug
if ( array_key_exists( 'post_title', $data ) ) {
$data['post_name'] = sanitize_title( $data['post_title'] );
}
// Clear schedule
if ( ! empty( $post_id ) ) {
wp_clear_scheduled_hook( 'air_wp_sync_importer_' . $post_id );
}
return $data;
}
public function redirect_post_location( $location, $post_id ) {
if ( $this->display_max_connection ) {
$location = add_query_arg( 'display_max_connection', 1, $location );
}
return $location;
}
public function admin_notices() {
// phpcs:ignore
if ( ! empty( $_GET['display_max_connection'] ) && '1' === $_GET['display_max_connection'] ) {
printf(
'',
esc_attr( 'notice notice-error' ),
wp_kses(
__( 'Thank you for using the free version of our plugin! To be able to create as many connections as you want, upgrade to Pro version.', 'airwpsync' ),
array(
'a' => array(
'href' => array(),
'target' => array(),
),
)
)
);
}
}
/**
* Add back link to message when connection is updated.
*/
public function post_updated_messages( $messages ) {
$back_link_html = sprintf(
' %2$s',
esc_url( admin_url( 'edit.php?post_type=airwpsync-connection' ) ),
__( 'Back to list', 'airwpsync' )
);
$messages['post'][1] .= $back_link_html;
return $messages;
}
/**
* Manual sync AJAX function
*/
public function trigger_update() {
// AJAX check
if ( ( empty( $_SERVER['HTTP_X_REQUESTED_WITH'] ) && strtolower( $_SERVER['HTTP_X_REQUESTED_WITH'] ) === 'xmlhttprequest' ) ) {
wp_die();
}
// Nonce check
check_ajax_referer( 'air-wp-sync-trigger-update', 'nonce' );
$importer_id = (int) $_POST['importer'] ?? 0;
$nonce = wp_create_nonce( 'air-wp-sync-trigger-update' );
try {
$importer = $this->get_importer_by_id( $importer_id );
if ( ! $importer ) {
throw new Exception( 'No connection found.' );
}
$result = $importer->run();
if ( is_wp_error( $result ) ) {
throw new Exception( $result->get_error_message() );
}
wp_send_json_success(
array(
'nonce' => $nonce,
'infosHtml' => $this->get_stats_html( $importer_id ),
'feedback' => __( 'Finished!', 'airwpsync' ),
)
);
} catch ( Exception $e ) {
wp_send_json_error(
array(
'nonce' => $nonce,
'infosHtml' => $this->get_stats_html( $importer_id, $e->getMessage() ),
'feedback' => __( 'Finished with errors.', 'airwpsync' ),
)
);
}
}
public function check_airtable_settings() {
// AJAX check
if ( empty( $_SERVER['HTTP_X_REQUESTED_WITH'] ) && strtolower( $_SERVER['HTTP_X_REQUESTED_WITH'] ) === 'xmlhttprequest' ) {
wp_die();
}
// Data check
if ( empty( $_POST['apiKey'] ) || empty( $_POST['appId'] ) || empty( $_POST['table'] ) ) {
wp_die();
}
// Nonce check
check_ajax_referer( 'air-wp-sync-check-airtable-settings', 'nonce' );
// Get data
$api_key = sanitize_text_field( $_POST['apiKey'] );
$app_id = sanitize_text_field( $_POST['appId'] );
$table = sanitize_text_field( $_POST['table'] );
$nonce = wp_create_nonce( 'air-wp-sync-check-airtable-settings' );
try {
$client = new Air_WP_Sync_Airtable_Api_Client( $app_id, $api_key );
$client->list( $table );
wp_send_json_success(
array(
'nonce' => $nonce,
'feedback' => __( 'Finished!', 'airwpsync' ),
)
);
} catch ( Exception $e ) {
$response = array(
'nonce' => $nonce,
);
if ( strpos( $e->getMessage(), 'Could not find table' ) > -1 || strpos( $e->getMessage(), 'NOT_FOUND' ) > -1 ) {
$response['fields'] = array( 'appId', 'table' );
$response['message'] = $e->getMessage();
}
if ( strpos( $e->getMessage(), 'Authentication required' ) > -1 ) {
$response['fields'] = array( 'apiKey' );
$response['message'] = __( 'Wrong API Key', 'airwpsync' );
}
wp_send_json_error( $response );
}
}
/**
* Get importer instance from id
*/
protected function get_importer_by_id( $id ) {
return array_reduce(
$this->importers,
function( $result, $importer ) use ( $id ) {
return $importer->infos()->get( 'id' ) === (int) $id ? $importer : $result;
},
false
);
}
/**
* Get importer statistics html
*/
protected function get_stats_html( $importer_id, $last_error = null ) {
ob_start();
$status = $importer_id ? get_post_meta( $importer_id, 'status', true ) : '';
$last_updated = $importer_id ? get_post_meta( $importer_id, 'last_updated', true ) : '';
$next_sync = $importer_id ? wp_next_scheduled( 'air_wp_sync_importer_' . $importer_id ) : '';
$last_error = $last_error ? $last_error : ( $importer_id ? get_post_meta( $importer_id, 'last_error', true ) : '' );
include AIR_WP_SYNC_PLUGIN_DIR . 'views/metabox-side/infos.php';
return ob_get_clean();
}
/**
* Get available post types
*/
protected function get_post_types() {
$excluded = array( 'attachment' );
$enabled = array( 'page', 'post' );
$post_types = array();
$wp_post_types = get_post_types(
array(
'public' => true,
),
'objects'
);
foreach ( $wp_post_types as $wp_post_type ) {
if ( ! in_array( $wp_post_type->name, $excluded, true ) ) {
$post_types[] = array(
'value' => $wp_post_type->name,
'label' => $wp_post_type->labels->singular_name . ( ! in_array( $wp_post_type->name, $enabled, true ) ? ' ' . __( '(Pro version, coming soon)', 'airwpsync' ) : '' ),
'enabled' => in_array( $wp_post_type->name, $enabled, true ),
);
}
}
$post_types[] = array(
'value' => 'custom',
'label' => __( 'Custom Post Type (Pro version, coming soon)', 'airwpsync' ),
'enabled' => false,
);
return apply_filters( 'airwpsync/get_post_types', $post_types );
}
/**
* Get post stati
*/
protected function get_post_stati() {
$whitelist = array( 'publish', 'future', 'draft', 'pending', 'trash', 'private' );
$post_stati = array();
$wp_post_stati = get_post_stati(
array( 'internal' => false ),
'objects'
);
foreach ( $wp_post_stati as $wp_post_status ) {
$enabled = in_array( $wp_post_status->name, $whitelist, true );
$post_stati[] = array(
'value' => $wp_post_status->name,
'label' => $wp_post_status->label . ( ! $enabled ? ' ' . __( '(Pro version, coming soon)', 'airwpsync' ) : '' ),
'enabled' => $enabled,
);
}
return apply_filters( 'airwpsync/get_post_stati', $post_stati );
}
/**
* Get post authors
*/
protected function get_post_authors() {
$authors = array();
$wp_authors = get_users( array( 'role__in' => array( 'administrator', 'editor', 'author', 'contributor' ) ) );
foreach ( $wp_authors as $wp_author ) {
$authors[] = array(
'value' => $wp_author->ID,
'label' => $wp_author->display_name,
'enabled' => true,
);
}
return apply_filters( 'airwpsync/get_post_authors', $authors );
}
/**
* Get sync strategies
*/
protected function get_sync_strategies() {
return array(
'add' => __( 'Add', 'airwpsync' ),
'add_update' => __( 'Add & Update', 'airwpsync' ),
'add_update_delete' => __( 'Add, Update & Delete', 'airwpsync' ),
);
}
/**
* Get schedules
*/
protected function get_schedules() {
$whitelist = array( 'hourly', 'twicedaily', 'daily', 'weekly' );
$schedules = array();
foreach ( wp_get_schedules() as $key => $schedule ) {
if ( in_array( $key, $whitelist, true ) || strpos( $key, 'airwpsync_' ) === 0 ) {
$enabled = $schedule['interval'] >= DAY_IN_SECONDS;
$schedules[ $schedule['interval'] ] = array(
'value' => $key,
'label' => $schedule['display'] . ( ! $enabled ? ' ' . __( '(Pro version, coming soon)', 'airwpsync' ) : '' ),
'enabled' => $enabled,
);
}
}
ksort( $schedules );
return array_reverse( $schedules );
}
}