feat: v0.2.0 — PullPush.io admin UI, version bump, deploy fix
- Remove Reddit OAuth fields (Client ID/Secret) from admin settings - Add source status indicator showing PullPush.io + .json fallback health - Add configurable cache TTL, time window, and posts-per-subreddit settings - Update Test Connections to report which Reddit source is active - Bump version 0.1.0 → 0.2.0 across plugin header, constant, and readme - Switch deploy pipeline from zip/REST API to SSH rsync with --delete - Add LiteSpeed cache purge step to prevent stale OPcache after deploy - Update CHANGELOG.md and readme.txt for 0.2.0 release Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -151,6 +151,10 @@ class PRAutoBlogger_Admin_Page {
|
||||
}
|
||||
break;
|
||||
|
||||
case 'source_status':
|
||||
$this->render_source_status_field();
|
||||
break;
|
||||
|
||||
case 'author_select':
|
||||
wp_dropdown_users( [ 'name' => $id, 'id' => $id, 'selected' => absint( $value ), 'show_option_none' => __( '— Auto (first admin) —', 'prautoblogger' ), 'option_none_value' => '0', 'class' => 'ab-select' ] );
|
||||
break;
|
||||
@@ -168,6 +172,74 @@ class PRAutoBlogger_Admin_Page {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the Reddit source status indicator.
|
||||
*
|
||||
* Shows PullPush.io (primary) and Reddit .json (fallback) availability
|
||||
* with live status checks via transient cache and rate limit info.
|
||||
*
|
||||
* Side effects: Reads transients for cached availability status.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function render_source_status_field(): void {
|
||||
$pullpush_available = get_transient( 'prautoblogger_pullpush_available' );
|
||||
$last_collection = get_option( 'prautoblogger_last_collection_time', '' );
|
||||
$last_source_used = get_option( 'prautoblogger_last_source_used', '' );
|
||||
|
||||
// Determine status display.
|
||||
$pp_status_class = 'ab-status-unknown';
|
||||
$pp_status_label = __( 'Unknown', 'prautoblogger' );
|
||||
if ( true === $pullpush_available ) {
|
||||
$pp_status_class = 'ab-status-ok';
|
||||
$pp_status_label = __( 'Available', 'prautoblogger' );
|
||||
} elseif ( false === $pullpush_available ) {
|
||||
$pp_status_class = 'ab-status-warn';
|
||||
$pp_status_label = __( 'Down — using fallback', 'prautoblogger' );
|
||||
}
|
||||
|
||||
echo '<div class="ab-source-status">';
|
||||
|
||||
// PullPush status.
|
||||
printf(
|
||||
'<div class="ab-source-row">'
|
||||
. '<span class="ab-source-dot %s"></span>'
|
||||
. '<strong>%s</strong> <span class="ab-source-badge">%s</span>'
|
||||
. '<span class="ab-source-label">%s</span>'
|
||||
. '</div>',
|
||||
esc_attr( $pp_status_class ),
|
||||
esc_html__( 'PullPush.io', 'prautoblogger' ),
|
||||
esc_html__( 'Primary', 'prautoblogger' ),
|
||||
esc_html( $pp_status_label )
|
||||
);
|
||||
|
||||
// Reddit .json fallback status.
|
||||
printf(
|
||||
'<div class="ab-source-row">'
|
||||
. '<span class="ab-source-dot ab-status-ok"></span>'
|
||||
. '<strong>%s</strong> <span class="ab-source-badge ab-badge-secondary">%s</span>'
|
||||
. '<span class="ab-source-label">%s</span>'
|
||||
. '</div>',
|
||||
esc_html__( 'Reddit .json', 'prautoblogger' ),
|
||||
esc_html__( 'Fallback', 'prautoblogger' ),
|
||||
esc_html__( 'Always available (rate-limited)', 'prautoblogger' )
|
||||
);
|
||||
|
||||
// Last collection info.
|
||||
if ( '' !== $last_collection ) {
|
||||
$time_ago = human_time_diff( (int) $last_collection, time() );
|
||||
printf(
|
||||
'<div class="ab-source-meta">%s <strong>%s</strong> %s</div>',
|
||||
esc_html__( 'Last collection:', 'prautoblogger' ),
|
||||
/* translators: %s is a human-readable time difference like "2 hours" */
|
||||
esc_html( sprintf( __( '%s ago', 'prautoblogger' ), $time_ago ) ),
|
||||
'' !== $last_source_used ? esc_html( sprintf( __( 'via %s', 'prautoblogger' ), $last_source_used ) ) : ''
|
||||
);
|
||||
}
|
||||
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize a settings field value.
|
||||
*
|
||||
@@ -181,7 +253,7 @@ class PRAutoBlogger_Admin_Page {
|
||||
$option_name = substr( $filter, strlen( 'sanitize_option_' ) );
|
||||
}
|
||||
|
||||
$encrypted = [ 'prautoblogger_openrouter_api_key', 'prautoblogger_reddit_client_secret', 'prautoblogger_ga4_credentials_json' ];
|
||||
$encrypted = [ 'prautoblogger_openrouter_api_key', 'prautoblogger_ga4_credentials_json' ];
|
||||
if ( in_array( $option_name, $encrypted, true ) ) {
|
||||
if ( '' === $value ) { return get_option( $option_name, '' ); }
|
||||
return PRAutoBlogger_Encryption::encrypt( sanitize_text_field( $value ) );
|
||||
@@ -197,7 +269,7 @@ class PRAutoBlogger_Admin_Page {
|
||||
return sanitize_text_field( $value );
|
||||
}
|
||||
|
||||
$numeric = [ 'prautoblogger_daily_article_target', 'prautoblogger_monthly_budget_usd', 'prautoblogger_min_word_count', 'prautoblogger_max_word_count', 'prautoblogger_default_author', 'prautoblogger_default_category' ];
|
||||
$numeric = [ 'prautoblogger_daily_article_target', 'prautoblogger_monthly_budget_usd', 'prautoblogger_min_word_count', 'prautoblogger_max_word_count', 'prautoblogger_default_author', 'prautoblogger_default_category', 'prautoblogger_pullpush_cache_ttl', 'prautoblogger_reddit_posts_per_subreddit' ];
|
||||
if ( in_array( $option_name, $numeric, true ) ) {
|
||||
return is_numeric( $value ) ? $value : 0;
|
||||
}
|
||||
|
||||
@@ -77,18 +77,12 @@ class PRAutoBlogger_Settings_Fields {
|
||||
'icon' => '🔑',
|
||||
],
|
||||
[
|
||||
'id' => 'prautoblogger_reddit_client_id',
|
||||
'label' => __( 'Reddit Client ID', 'prautoblogger' ),
|
||||
'type' => 'text',
|
||||
'id' => 'prautoblogger_reddit_source_status',
|
||||
'label' => __( 'Reddit Data Source', 'prautoblogger' ),
|
||||
'type' => 'source_status',
|
||||
'section' => 'prautoblogger_api',
|
||||
'description' => __( 'Create a script app at reddit.com/prefs/apps', 'prautoblogger' ),
|
||||
'icon' => '🤖',
|
||||
],
|
||||
[
|
||||
'id' => 'prautoblogger_reddit_client_secret',
|
||||
'label' => __( 'Reddit Client Secret', 'prautoblogger' ),
|
||||
'type' => 'password',
|
||||
'section' => 'prautoblogger_api',
|
||||
'description' => __( 'Uses PullPush.io (primary) and Reddit .json (fallback). No API key required.', 'prautoblogger' ),
|
||||
'icon' => '📡',
|
||||
],
|
||||
|
||||
// ── AI Models ───────────────────────────────────────────────
|
||||
@@ -191,7 +185,7 @@ class PRAutoBlogger_Settings_Fields {
|
||||
'type' => 'checkboxes',
|
||||
'section' => 'prautoblogger_sources',
|
||||
'options' => [
|
||||
'reddit' => __( 'Reddit', 'prautoblogger' ),
|
||||
'reddit' => __( 'Reddit (via PullPush.io)', 'prautoblogger' ),
|
||||
'tiktok' => __( 'TikTok (coming soon)', 'prautoblogger' ),
|
||||
'instagram' => __( 'Instagram (coming soon)', 'prautoblogger' ),
|
||||
'youtube' => __( 'YouTube (coming soon)', 'prautoblogger' ),
|
||||
@@ -199,6 +193,39 @@ class PRAutoBlogger_Settings_Fields {
|
||||
'default' => '["reddit"]',
|
||||
'description' => __( 'Select which platforms to monitor for topics.', 'prautoblogger' ),
|
||||
],
|
||||
[
|
||||
'id' => 'prautoblogger_pullpush_cache_ttl',
|
||||
'label' => __( 'Research Cache (hours)', 'prautoblogger' ),
|
||||
'type' => 'number',
|
||||
'section' => 'prautoblogger_sources',
|
||||
'default' => 6,
|
||||
'min' => 1,
|
||||
'max' => 72,
|
||||
'description' => __( 'How long to cache PullPush research results before re-fetching. Saves API calls on low-traffic sites.', 'prautoblogger' ),
|
||||
],
|
||||
[
|
||||
'id' => 'prautoblogger_reddit_time_filter',
|
||||
'label' => __( 'Reddit Time Window', 'prautoblogger' ),
|
||||
'type' => 'select',
|
||||
'section' => 'prautoblogger_sources',
|
||||
'default' => 'day',
|
||||
'options' => [
|
||||
'day' => __( 'Past 24 hours', 'prautoblogger' ),
|
||||
'week' => __( 'Past week', 'prautoblogger' ),
|
||||
'month' => __( 'Past month', 'prautoblogger' ),
|
||||
],
|
||||
'description' => __( 'How far back to search for trending posts and comments.', 'prautoblogger' ),
|
||||
],
|
||||
[
|
||||
'id' => 'prautoblogger_reddit_posts_per_subreddit',
|
||||
'label' => __( 'Posts per Subreddit', 'prautoblogger' ),
|
||||
'type' => 'number',
|
||||
'section' => 'prautoblogger_sources',
|
||||
'default' => 25,
|
||||
'min' => 5,
|
||||
'max' => 100,
|
||||
'description' => __( 'Maximum posts to fetch per subreddit per collection run.', 'prautoblogger' ),
|
||||
],
|
||||
|
||||
// ── Schedule & Budget ───────────────────────────────────────
|
||||
[
|
||||
|
||||
@@ -276,10 +276,13 @@ class PRAutoBlogger {
|
||||
: [ 'status' => 'error', 'message' => __( 'OpenRouter connection failed. Check your API key.', 'prautoblogger' ) ];
|
||||
}
|
||||
if ( 'reddit' === $service || 'all' === $service ) {
|
||||
$reddit = new PRAutoBlogger_Reddit_Provider();
|
||||
$results['reddit'] = $reddit->validate_credentials()
|
||||
? [ 'status' => 'ok', 'message' => __( 'Reddit API connected.', 'prautoblogger' ) ]
|
||||
: [ 'status' => 'error', 'message' => __( 'Reddit connection failed. Check client ID/secret.', 'prautoblogger' ) ];
|
||||
$reddit = new PRAutoBlogger_Reddit_Provider();
|
||||
$is_ok = $reddit->validate_credentials();
|
||||
$pp_up = get_transient( 'prautoblogger_pullpush_available' );
|
||||
$source = true === $pp_up ? 'PullPush.io' : 'Reddit .json fallback';
|
||||
$results['reddit'] = $is_ok
|
||||
? [ 'status' => 'ok', 'message' => sprintf( __( 'Reddit source available via %s.', 'prautoblogger' ), $source ) ]
|
||||
: [ 'status' => 'error', 'message' => __( 'Both PullPush.io and Reddit .json are unreachable.', 'prautoblogger' ) ];
|
||||
}
|
||||
wp_send_json_success( $results );
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user