style(phpcs): autofix WordPress-Core violations via phpcbf
Run phpcbf --standard=WordPress-Core across includes/, prautoblogger.php, and uninstall.php. Fixed 1,362 formatting violations in 75 files: - Short array syntax: [] → array() - Line breaking for long function calls - Consistent indentation - Function call parameter alignment No logic changes; purely mechanical formatting.
This commit is contained in:
@@ -62,11 +62,13 @@ class PRAutoBlogger_Admin_Notices {
|
||||
} elseif ( $utilization >= 80.0 ) {
|
||||
printf(
|
||||
'<div class="notice notice-warning"><p>%s</p></div>',
|
||||
esc_html( sprintf(
|
||||
esc_html(
|
||||
sprintf(
|
||||
/* translators: %s: budget utilization percentage */
|
||||
__( 'PRAutoBlogger: Monthly API budget is at %.0f%%. Consider increasing the budget or reducing daily article count.', 'prautoblogger' ),
|
||||
$utilization
|
||||
) )
|
||||
__( 'PRAutoBlogger: Monthly API budget is at %.0f%%. Consider increasing the budget or reducing daily article count.', 'prautoblogger' ),
|
||||
$utilization
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ class PRAutoBlogger_Admin_Page {
|
||||
__( 'PRAutoBlogger', 'prautoblogger' ),
|
||||
'manage_options',
|
||||
self::PAGE_SLUG,
|
||||
[ $this, 'render_page' ],
|
||||
array( $this, 'render_page' ),
|
||||
'dashicons-edit-page',
|
||||
30
|
||||
);
|
||||
@@ -43,54 +43,62 @@ class PRAutoBlogger_Admin_Page {
|
||||
|
||||
foreach ( $fields as $field ) {
|
||||
$section = $field['section'] ?? 'prautoblogger_api';
|
||||
register_setting( self::OPTION_GROUP, $field['id'], [
|
||||
'type' => $field['wp_type'] ?? 'string',
|
||||
'sanitize_callback' => [ $this, 'sanitize_field' ],
|
||||
'default' => $field['default'] ?? '',
|
||||
] );
|
||||
add_settings_field( $field['id'], $field['label'], [ $this, 'render_field' ], self::PAGE_SLUG, $section, $field );
|
||||
register_setting(
|
||||
self::OPTION_GROUP,
|
||||
$field['id'],
|
||||
array(
|
||||
'type' => $field['wp_type'] ?? 'string',
|
||||
'sanitize_callback' => array( $this, 'sanitize_field' ),
|
||||
'default' => $field['default'] ?? '',
|
||||
)
|
||||
);
|
||||
add_settings_field( $field['id'], $field['label'], array( $this, 'render_field' ), self::PAGE_SLUG, $section, $field );
|
||||
}
|
||||
}
|
||||
|
||||
/** Enqueue admin CSS and JS on all PRAutoBlogger admin pages. */
|
||||
public function on_enqueue_assets( string $hook_suffix ): void {
|
||||
$pages = [
|
||||
$pages = array(
|
||||
'toplevel_page_' . self::PAGE_SLUG,
|
||||
'prautoblogger_page_prautoblogger-metrics',
|
||||
'prautoblogger_page_prautoblogger-review-queue',
|
||||
'prautoblogger_page_prautoblogger-ideas',
|
||||
'prautoblogger_page_prautoblogger-logs',
|
||||
];
|
||||
);
|
||||
if ( ! in_array( $hook_suffix, $pages, true ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
wp_enqueue_style( 'prautoblogger-admin', PRAUTOBLOGGER_PLUGIN_URL . 'assets/css/admin.css', [], PRAUTOBLOGGER_VERSION );
|
||||
wp_enqueue_style( 'prautoblogger-model-picker', PRAUTOBLOGGER_PLUGIN_URL . 'assets/css/model-picker.css', [ 'prautoblogger-admin' ], PRAUTOBLOGGER_VERSION );
|
||||
wp_enqueue_script( 'prautoblogger-admin', PRAUTOBLOGGER_PLUGIN_URL . 'assets/js/admin.js', [ 'jquery' ], PRAUTOBLOGGER_VERSION, true );
|
||||
wp_enqueue_script( 'prautoblogger-model-picker', PRAUTOBLOGGER_PLUGIN_URL . 'assets/js/model-picker.js', [ 'jquery', 'prautoblogger-admin' ], PRAUTOBLOGGER_VERSION, true );
|
||||
wp_enqueue_style( 'prautoblogger-admin', PRAUTOBLOGGER_PLUGIN_URL . 'assets/css/admin.css', array(), PRAUTOBLOGGER_VERSION );
|
||||
wp_enqueue_style( 'prautoblogger-model-picker', PRAUTOBLOGGER_PLUGIN_URL . 'assets/css/model-picker.css', array( 'prautoblogger-admin' ), PRAUTOBLOGGER_VERSION );
|
||||
wp_enqueue_script( 'prautoblogger-admin', PRAUTOBLOGGER_PLUGIN_URL . 'assets/js/admin.js', array( 'jquery' ), PRAUTOBLOGGER_VERSION, true );
|
||||
wp_enqueue_script( 'prautoblogger-model-picker', PRAUTOBLOGGER_PLUGIN_URL . 'assets/js/model-picker.js', array( 'jquery', 'prautoblogger-admin' ), PRAUTOBLOGGER_VERSION, true );
|
||||
|
||||
wp_localize_script( 'prautoblogger-admin', 'prautobloggerAdmin', [
|
||||
'ajaxUrl' => admin_url( 'admin-ajax.php' ),
|
||||
'adminUrl' => admin_url(),
|
||||
'siteUrl' => home_url( '/' ),
|
||||
'generateNonce' => wp_create_nonce( 'prautoblogger_generate_now' ),
|
||||
'imageNonce' => wp_create_nonce( 'prautoblogger_generate_image' ),
|
||||
'testNonce' => wp_create_nonce( 'prautoblogger_test_connection' ),
|
||||
'modelsNonce' => wp_create_nonce( 'prautoblogger_get_models' ),
|
||||
'reviewNonce' => wp_create_nonce( 'prautoblogger_review_queue' ),
|
||||
'ideaGenNonce' => wp_create_nonce( 'prautoblogger_idea_gen' ),
|
||||
'generatingText' => __( 'Generating...', 'prautoblogger' ),
|
||||
'generateText' => __( 'Generate Now', 'prautoblogger' ),
|
||||
'testingText' => __( 'Testing...', 'prautoblogger' ),
|
||||
'testText' => __( 'Test Connections', 'prautoblogger' ),
|
||||
// Image models for the picker — defined in the settings class to keep this file short.
|
||||
'imageModels' => PRAutoBlogger_Settings_Fields_Extended::get_image_models(),
|
||||
] );
|
||||
wp_localize_script(
|
||||
'prautoblogger-admin',
|
||||
'prautobloggerAdmin',
|
||||
array(
|
||||
'ajaxUrl' => admin_url( 'admin-ajax.php' ),
|
||||
'adminUrl' => admin_url(),
|
||||
'siteUrl' => home_url( '/' ),
|
||||
'generateNonce' => wp_create_nonce( 'prautoblogger_generate_now' ),
|
||||
'imageNonce' => wp_create_nonce( 'prautoblogger_generate_image' ),
|
||||
'testNonce' => wp_create_nonce( 'prautoblogger_test_connection' ),
|
||||
'modelsNonce' => wp_create_nonce( 'prautoblogger_get_models' ),
|
||||
'reviewNonce' => wp_create_nonce( 'prautoblogger_review_queue' ),
|
||||
'ideaGenNonce' => wp_create_nonce( 'prautoblogger_idea_gen' ),
|
||||
'generatingText' => __( 'Generating...', 'prautoblogger' ),
|
||||
'generateText' => __( 'Generate Now', 'prautoblogger' ),
|
||||
'testingText' => __( 'Testing...', 'prautoblogger' ),
|
||||
'testText' => __( 'Test Connections', 'prautoblogger' ),
|
||||
// Image models for the picker — defined in the settings class to keep this file short.
|
||||
'imageModels' => PRAutoBlogger_Settings_Fields_Extended::get_image_models(),
|
||||
)
|
||||
);
|
||||
|
||||
// Ideas browser: generate-from-idea JS (only on the Ideas page).
|
||||
if ( 'prautoblogger_page_prautoblogger-ideas' === $hook_suffix ) {
|
||||
wp_enqueue_script( 'prautoblogger-ideas-browser', PRAUTOBLOGGER_PLUGIN_URL . 'assets/js/ideas-browser.js', [ 'jquery', 'prautoblogger-admin' ], PRAUTOBLOGGER_VERSION, true );
|
||||
wp_enqueue_script( 'prautoblogger-ideas-browser', PRAUTOBLOGGER_PLUGIN_URL . 'assets/js/ideas-browser.js', array( 'jquery', 'prautoblogger-admin' ), PRAUTOBLOGGER_VERSION, true );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,15 +135,22 @@ class PRAutoBlogger_Admin_Page {
|
||||
case 'url':
|
||||
case 'time':
|
||||
$attrs = '';
|
||||
if ( isset( $args['min'] ) ) { $attrs .= ' min="' . esc_attr( (string) $args['min'] ) . '"'; }
|
||||
if ( isset( $args['max'] ) ) { $attrs .= ' max="' . esc_attr( (string) $args['max'] ) . '"'; }
|
||||
if ( isset( $args['step'] ) ) { $attrs .= ' step="' . esc_attr( (string) $args['step'] ) . '"'; }
|
||||
if ( isset( $args['min'] ) ) {
|
||||
$attrs .= ' min="' . esc_attr( (string) $args['min'] ) . '"'; }
|
||||
if ( isset( $args['max'] ) ) {
|
||||
$attrs .= ' max="' . esc_attr( (string) $args['max'] ) . '"'; }
|
||||
if ( isset( $args['step'] ) ) {
|
||||
$attrs .= ' step="' . esc_attr( (string) $args['step'] ) . '"'; }
|
||||
printf( '<input type="%s" id="%s" name="%s" value="%s" class="ab-input" %s />', esc_attr( $type ), $id, $id, esc_attr( (string) $value ), $attrs );
|
||||
break;
|
||||
|
||||
case 'password':
|
||||
printf( '<input type="password" id="%s" name="%s" value="" class="ab-input" placeholder="%s" autocomplete="off" />',
|
||||
$id, $id, '' !== $value ? esc_attr__( 'Saved (enter new value to change)', 'prautoblogger' ) : '' );
|
||||
printf(
|
||||
'<input type="password" id="%s" name="%s" value="" class="ab-input" placeholder="%s" autocomplete="off" />',
|
||||
$id,
|
||||
$id,
|
||||
'' !== $value ? esc_attr__( 'Saved (enter new value to change)', 'prautoblogger' ) : ''
|
||||
);
|
||||
break;
|
||||
|
||||
case 'textarea':
|
||||
@@ -144,30 +159,40 @@ class PRAutoBlogger_Admin_Page {
|
||||
|
||||
case 'select':
|
||||
printf( '<select id="%s" name="%s" class="ab-select">', $id, $id );
|
||||
foreach ( ( $args['options'] ?? [] ) as $v => $label ) {
|
||||
foreach ( ( $args['options'] ?? array() ) as $v => $label ) {
|
||||
printf( '<option value="%s"%s>%s</option>', esc_attr( (string) $v ), selected( $value, (string) $v, false ), esc_html( $label ) );
|
||||
}
|
||||
echo '</select>';
|
||||
break;
|
||||
|
||||
case 'toggle':
|
||||
$checked = in_array( $value, [ '1', 'yes', true ], true );
|
||||
printf( '<label class="ab-toggle"><input type="hidden" name="%s" value="0" /><input type="checkbox" name="%s" value="1" %s /><span class="ab-toggle-slider"></span></label>',
|
||||
$id, $id, checked( $checked, true, false ) );
|
||||
$checked = in_array( $value, array( '1', 'yes', true ), true );
|
||||
printf(
|
||||
'<label class="ab-toggle"><input type="hidden" name="%s" value="0" /><input type="checkbox" name="%s" value="1" %s /><span class="ab-toggle-slider"></span></label>',
|
||||
$id,
|
||||
$id,
|
||||
checked( $checked, true, false )
|
||||
);
|
||||
break;
|
||||
|
||||
case 'checkboxes':
|
||||
$current = json_decode( (string) $value, true ) ?: [];
|
||||
$current = json_decode( (string) $value, true ) ?: array();
|
||||
// Hidden input ensures an empty value is POSTed when all boxes
|
||||
// are unchecked — without this, the browser omits the field
|
||||
// entirely, so WordPress never fires the sanitize callback and
|
||||
// the old value persists in wp_options.
|
||||
printf( '<input type="hidden" name="%s" value="" />', $id );
|
||||
foreach ( ( $args['options'] ?? [] ) as $v => $label ) {
|
||||
foreach ( ( $args['options'] ?? array() ) as $v => $label ) {
|
||||
$is_checked = in_array( $v, $current, true );
|
||||
$disabled = strpos( $label, 'coming soon' ) !== false ? ' disabled' : '';
|
||||
printf( '<label class="ab-checkbox-label"><input type="checkbox" name="%s[]" value="%s" %s%s /> %s</label>',
|
||||
$id, esc_attr( $v ), checked( $is_checked, true, false ), $disabled, esc_html( $label ) );
|
||||
printf(
|
||||
'<label class="ab-checkbox-label"><input type="checkbox" name="%s[]" value="%s" %s%s /> %s</label>',
|
||||
$id,
|
||||
esc_attr( $v ),
|
||||
checked( $is_checked, true, false ),
|
||||
$disabled,
|
||||
esc_html( $label )
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -180,11 +205,30 @@ class PRAutoBlogger_Admin_Page {
|
||||
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' ] );
|
||||
wp_dropdown_users(
|
||||
array(
|
||||
'name' => $id,
|
||||
'id' => $id,
|
||||
'selected' => absint( $value ),
|
||||
'show_option_none' => __( '— Auto (first admin) —', 'prautoblogger' ),
|
||||
'option_none_value' => '0',
|
||||
'class' => 'ab-select',
|
||||
)
|
||||
);
|
||||
break;
|
||||
|
||||
case 'category_select':
|
||||
wp_dropdown_categories( [ 'name' => $id, 'id' => $id, 'selected' => absint( $value ), 'show_option_none' => __( '— Auto-assign by type —', 'prautoblogger' ), 'option_none_value' => '0', 'class' => 'ab-select', 'hide_empty' => false ] );
|
||||
wp_dropdown_categories(
|
||||
array(
|
||||
'name' => $id,
|
||||
'id' => $id,
|
||||
'selected' => absint( $value ),
|
||||
'show_option_none' => __( '— Auto-assign by type —', 'prautoblogger' ),
|
||||
'option_none_value' => '0',
|
||||
'class' => 'ab-select',
|
||||
'hide_empty' => false,
|
||||
)
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -231,7 +275,7 @@ class PRAutoBlogger_Admin_Page {
|
||||
$option_name = substr( $filter, strlen( 'sanitize_option_' ) );
|
||||
}
|
||||
|
||||
$encrypted = [ 'prautoblogger_openrouter_api_key', 'prautoblogger_ga4_credentials_json', 'prautoblogger_runware_api_key' ];
|
||||
$encrypted = array( 'prautoblogger_openrouter_api_key', 'prautoblogger_ga4_credentials_json', 'prautoblogger_runware_api_key' );
|
||||
if ( in_array( $option_name, $encrypted, true ) ) {
|
||||
// Empty value means password field wasn't touched — keep existing.
|
||||
if ( '' === $value ) {
|
||||
@@ -251,7 +295,7 @@ class PRAutoBlogger_Admin_Page {
|
||||
return PRAutoBlogger_Encryption::encrypt( sanitize_text_field( $value ) );
|
||||
}
|
||||
|
||||
$json_fields = [ 'prautoblogger_target_subreddits', 'prautoblogger_topic_exclusions', 'prautoblogger_enabled_sources' ];
|
||||
$json_fields = array( 'prautoblogger_target_subreddits', 'prautoblogger_topic_exclusions', 'prautoblogger_enabled_sources' );
|
||||
if ( in_array( $option_name, $json_fields, true ) ) {
|
||||
// PHP array from checkboxes — sanitize each item and re-encode.
|
||||
if ( is_array( $value ) ) {
|
||||
@@ -275,7 +319,7 @@ class PRAutoBlogger_Admin_Page {
|
||||
return wp_json_encode( array_values( array_map( 'sanitize_text_field', $items ) ) );
|
||||
}
|
||||
|
||||
$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' ];
|
||||
$numeric = array( '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;
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ class PRAutoBlogger_Dashboard_Widget {
|
||||
wp_add_dashboard_widget(
|
||||
'prautoblogger_status',
|
||||
__( 'PRAutoBlogger Status', 'prautoblogger' ),
|
||||
[ $this, 'render_widget' ]
|
||||
array( $this, 'render_widget' )
|
||||
);
|
||||
}
|
||||
|
||||
@@ -44,7 +44,8 @@ class PRAutoBlogger_Dashboard_Widget {
|
||||
|
||||
$next_run = wp_next_scheduled( 'prautoblogger_daily_generation' );
|
||||
|
||||
printf( '<p><strong>%s</strong> $%.2f / $%.2f (%.0f%%)</p>',
|
||||
printf(
|
||||
'<p><strong>%s</strong> $%.2f / $%.2f (%.0f%%)</p>',
|
||||
esc_html__( 'Monthly Spend:', 'prautoblogger' ),
|
||||
$monthly_spend,
|
||||
$budget,
|
||||
@@ -52,7 +53,8 @@ class PRAutoBlogger_Dashboard_Widget {
|
||||
);
|
||||
|
||||
if ( false !== $next_run ) {
|
||||
printf( '<p><strong>%s</strong> %s</p>',
|
||||
printf(
|
||||
'<p><strong>%s</strong> %s</p>',
|
||||
esc_html__( 'Next Generation:', 'prautoblogger' ),
|
||||
esc_html( wp_date( 'M j, Y g:i A', $next_run ) )
|
||||
);
|
||||
|
||||
@@ -28,7 +28,7 @@ class PRAutoBlogger_Ideas_Browser {
|
||||
__( 'Ideas', 'prautoblogger' ),
|
||||
'manage_options',
|
||||
'prautoblogger-ideas',
|
||||
[ $this, 'render_page' ]
|
||||
array( $this, 'render_page' )
|
||||
);
|
||||
}
|
||||
|
||||
@@ -62,43 +62,51 @@ class PRAutoBlogger_Ideas_Browser {
|
||||
public function on_ajax_generate_from_idea(): void {
|
||||
check_ajax_referer( 'prautoblogger_idea_gen', 'nonce' );
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
wp_send_json_error( [ 'message' => 'Insufficient permissions.' ], 403 );
|
||||
wp_send_json_error( array( 'message' => 'Insufficient permissions.' ), 403 );
|
||||
return;
|
||||
}
|
||||
|
||||
$idea_id = absint( $_POST['idea_id'] ?? 0 );
|
||||
if ( $idea_id < 1 ) {
|
||||
wp_send_json_error( [ 'message' => 'Invalid idea ID.' ] );
|
||||
wp_send_json_error( array( 'message' => 'Invalid idea ID.' ) );
|
||||
return;
|
||||
}
|
||||
|
||||
// Build Article_Idea from the analysis row.
|
||||
$idea_data = self::load_idea_data( $idea_id );
|
||||
if ( null === $idea_data ) {
|
||||
wp_send_json_error( [ 'message' => 'Idea not found.' ] );
|
||||
wp_send_json_error( array( 'message' => 'Idea not found.' ) );
|
||||
return;
|
||||
}
|
||||
|
||||
// Store idea for the cron handler and set initial "running" status.
|
||||
set_transient( self::STATUS_PREFIX . 'data_' . $idea_id, $idea_data, self::STATUS_TTL );
|
||||
set_transient( self::STATUS_PREFIX . $idea_id, [
|
||||
'status' => 'running',
|
||||
'stage' => __( 'Starting generation…', 'prautoblogger' ),
|
||||
'started' => time(),
|
||||
], self::STATUS_TTL );
|
||||
set_transient(
|
||||
self::STATUS_PREFIX . $idea_id,
|
||||
array(
|
||||
'status' => 'running',
|
||||
'stage' => __( 'Starting generation…', 'prautoblogger' ),
|
||||
'started' => time(),
|
||||
),
|
||||
self::STATUS_TTL
|
||||
);
|
||||
|
||||
// Schedule background cron — passes idea_id as argument.
|
||||
$hook = 'prautoblogger_generate_from_idea';
|
||||
if ( ! wp_next_scheduled( $hook, [ $idea_id ] ) ) {
|
||||
wp_schedule_single_event( time(), $hook, [ $idea_id ] );
|
||||
if ( ! wp_next_scheduled( $hook, array( $idea_id ) ) ) {
|
||||
wp_schedule_single_event( time(), $hook, array( $idea_id ) );
|
||||
}
|
||||
spawn_cron();
|
||||
wp_remote_post(
|
||||
site_url( 'wp-cron.php?doing_wp_cron=' . sprintf( '%.22F', microtime( true ) ) ),
|
||||
[ 'timeout' => 0.01, 'blocking' => false, 'sslverify' => false ]
|
||||
array(
|
||||
'timeout' => 0.01,
|
||||
'blocking' => false,
|
||||
'sslverify' => false,
|
||||
)
|
||||
);
|
||||
|
||||
wp_send_json_success( [ 'message' => 'Generation started.' ] );
|
||||
wp_send_json_success( array( 'message' => 'Generation started.' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -109,14 +117,14 @@ class PRAutoBlogger_Ideas_Browser {
|
||||
public function on_ajax_idea_gen_status(): void {
|
||||
check_ajax_referer( 'prautoblogger_idea_gen', 'nonce' );
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
wp_send_json_error( [ 'message' => 'Insufficient permissions.' ], 403 );
|
||||
wp_send_json_error( array( 'message' => 'Insufficient permissions.' ), 403 );
|
||||
return;
|
||||
}
|
||||
|
||||
$idea_id = absint( $_POST['idea_id'] ?? 0 );
|
||||
$status = get_transient( self::STATUS_PREFIX . $idea_id );
|
||||
if ( ! is_array( $status ) ) {
|
||||
wp_send_json_success( [ 'status' => 'idle' ] );
|
||||
wp_send_json_success( array( 'status' => 'idle' ) );
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -142,12 +150,26 @@ class PRAutoBlogger_Ideas_Browser {
|
||||
$key = self::STATUS_PREFIX . $idea_id;
|
||||
$idea_data = get_transient( self::STATUS_PREFIX . 'data_' . $idea_id );
|
||||
if ( ! is_array( $idea_data ) ) {
|
||||
set_transient( $key, [ 'status' => 'error', 'message' => 'Idea data expired.' ], self::STATUS_TTL );
|
||||
set_transient(
|
||||
$key,
|
||||
array(
|
||||
'status' => 'error',
|
||||
'message' => 'Idea data expired.',
|
||||
),
|
||||
self::STATUS_TTL
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! PRAutoBlogger_Generation_Lock::acquire() ) {
|
||||
set_transient( $key, [ 'status' => 'error', 'message' => 'Another generation is running.' ], self::STATUS_TTL );
|
||||
set_transient(
|
||||
$key,
|
||||
array(
|
||||
'status' => 'error',
|
||||
'message' => 'Another generation is running.',
|
||||
),
|
||||
self::STATUS_TTL
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -166,19 +188,30 @@ class PRAutoBlogger_Ideas_Browser {
|
||||
// Find the generated post ID for the "View" link.
|
||||
$post_id = self::find_post_by_run_id( $cost_tracker->get_run_id() );
|
||||
|
||||
set_transient( $key, [
|
||||
'status' => 'complete',
|
||||
'generated' => $result['generated'],
|
||||
'published' => $result['published'],
|
||||
'cost' => $result['cost'],
|
||||
'post_id' => $post_id,
|
||||
], self::STATUS_TTL );
|
||||
set_transient(
|
||||
$key,
|
||||
array(
|
||||
'status' => 'complete',
|
||||
'generated' => $result['generated'],
|
||||
'published' => $result['published'],
|
||||
'cost' => $result['cost'],
|
||||
'post_id' => $post_id,
|
||||
),
|
||||
self::STATUS_TTL
|
||||
);
|
||||
} catch ( \Throwable $e ) {
|
||||
PRAutoBlogger_Logger::instance()->error(
|
||||
sprintf( 'Idea generation %s for #%d: %s', get_class( $e ), $idea_id, $e->getMessage() ),
|
||||
'pipeline'
|
||||
);
|
||||
set_transient( $key, [ 'status' => 'error', 'message' => $e->getMessage() ], self::STATUS_TTL );
|
||||
set_transient(
|
||||
$key,
|
||||
array(
|
||||
'status' => 'error',
|
||||
'message' => $e->getMessage(),
|
||||
),
|
||||
self::STATUS_TTL
|
||||
);
|
||||
}
|
||||
|
||||
PRAutoBlogger_Generation_Lock::release();
|
||||
@@ -231,26 +264,26 @@ class PRAutoBlogger_Ideas_Browser {
|
||||
return null;
|
||||
}
|
||||
|
||||
$meta = json_decode( $row['metadata_json'] ?? '{}', true ) ?: [];
|
||||
return [
|
||||
$meta = json_decode( $row['metadata_json'] ?? '{}', true ) ?: array();
|
||||
return array(
|
||||
'topic' => $row['topic'],
|
||||
'article_type' => $row['analysis_type'],
|
||||
'suggested_title' => $meta['suggested_title'] ?? $row['topic'],
|
||||
'summary' => $row['summary'] ?? '',
|
||||
'score' => (float) $row['relevance_score'],
|
||||
'analysis_id' => (int) $row['id'],
|
||||
'source_ids' => json_decode( $row['source_ids_json'] ?? '[]', true ) ?: [],
|
||||
'key_points' => $meta['key_points'] ?? [],
|
||||
'target_keywords' => $meta['target_keywords'] ?? [],
|
||||
];
|
||||
'source_ids' => json_decode( $row['source_ids_json'] ?? '[]', true ) ?: array(),
|
||||
'key_points' => $meta['key_points'] ?? array(),
|
||||
'target_keywords' => $meta['target_keywords'] ?? array(),
|
||||
);
|
||||
}
|
||||
|
||||
/** Query analysis results with optional filtering and pagination. */
|
||||
private function query_ideas( int $paged, string $type ): array {
|
||||
global $wpdb;
|
||||
$table = $wpdb->prefix . 'prautoblogger_analysis_results';
|
||||
$where = [];
|
||||
$params = [];
|
||||
$where = array();
|
||||
$params = array();
|
||||
|
||||
if ( '' !== $type ) {
|
||||
$where[] = 'analysis_type = %s';
|
||||
@@ -276,7 +309,10 @@ class PRAutoBlogger_Ideas_Browser {
|
||||
? $wpdb->get_results( $full_sql, ARRAY_A )
|
||||
: $wpdb->get_results( $wpdb->prepare( $full_sql, ...$params ), ARRAY_A );
|
||||
|
||||
return [ 'rows' => $rows ?: [], 'total' => $total ];
|
||||
return array(
|
||||
'rows' => $rows ?: array(),
|
||||
'total' => $total,
|
||||
);
|
||||
}
|
||||
|
||||
/** Get counts per analysis_type for the filter badges. */
|
||||
@@ -290,8 +326,8 @@ class PRAutoBlogger_Ideas_Browser {
|
||||
ARRAY_A
|
||||
);
|
||||
|
||||
$counts = [];
|
||||
foreach ( $rows ?: [] as $row ) {
|
||||
$counts = array();
|
||||
foreach ( $rows ?: array() as $row ) {
|
||||
$counts[ $row['analysis_type'] ] = (int) $row['cnt'];
|
||||
}
|
||||
return $counts;
|
||||
|
||||
@@ -33,64 +33,64 @@ class PRAutoBlogger_Image_Model_Registry {
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
public static function get_models(): array {
|
||||
return [
|
||||
[
|
||||
return array(
|
||||
array(
|
||||
'id' => 'runware:100@1',
|
||||
'name' => 'FLUX.1 schnell (Runware)',
|
||||
'provider' => 'runware',
|
||||
'cost_per_image' => 0.0006,
|
||||
'capabilities' => [ 'image_generation' ],
|
||||
'capabilities' => array( 'image_generation' ),
|
||||
'description' => __( 'Default. Fast, very cheap, comic-friendly fidelity.', 'prautoblogger' ),
|
||||
],
|
||||
[
|
||||
),
|
||||
array(
|
||||
'id' => 'runware:101@1',
|
||||
'name' => 'FLUX.1 dev (Runware)',
|
||||
'provider' => 'runware',
|
||||
'cost_per_image' => 0.02,
|
||||
'capabilities' => [ 'image_generation' ],
|
||||
'capabilities' => array( 'image_generation' ),
|
||||
'description' => __( 'Higher fidelity FLUX. ~30x schnell cost.', 'prautoblogger' ),
|
||||
],
|
||||
[
|
||||
),
|
||||
array(
|
||||
'id' => 'google/gemini-2.5-flash-image',
|
||||
'name' => 'Gemini 2.5 Flash Image (Nano Banana)',
|
||||
'provider' => 'openrouter',
|
||||
'cost_per_image' => 0.005,
|
||||
'capabilities' => [ 'image_generation' ],
|
||||
'capabilities' => array( 'image_generation' ),
|
||||
'description' => __( 'Mid-tier quality, proven.', 'prautoblogger' ),
|
||||
],
|
||||
[
|
||||
),
|
||||
array(
|
||||
'id' => 'google/gemini-3.1-flash-image-preview',
|
||||
'name' => 'Gemini 3.1 Flash Image (Nano Banana 2)',
|
||||
'provider' => 'openrouter',
|
||||
'cost_per_image' => 0.008,
|
||||
'capabilities' => [ 'image_generation' ],
|
||||
'capabilities' => array( 'image_generation' ),
|
||||
'description' => __( 'Latest Google. Better quality.', 'prautoblogger' ),
|
||||
],
|
||||
[
|
||||
),
|
||||
array(
|
||||
'id' => 'google/gemini-3-pro-image-preview',
|
||||
'name' => 'Gemini 3 Pro Image (Nano Banana Pro)',
|
||||
'provider' => 'openrouter',
|
||||
'cost_per_image' => 0.03,
|
||||
'capabilities' => [ 'image_generation' ],
|
||||
'capabilities' => array( 'image_generation' ),
|
||||
'description' => __( 'Highest quality Google. Premium.', 'prautoblogger' ),
|
||||
],
|
||||
[
|
||||
),
|
||||
array(
|
||||
'id' => 'openai/gpt-5-image-mini',
|
||||
'name' => 'GPT-5 Image Mini',
|
||||
'provider' => 'openrouter',
|
||||
'cost_per_image' => 0.02,
|
||||
'capabilities' => [ 'image_generation' ],
|
||||
'capabilities' => array( 'image_generation' ),
|
||||
'description' => __( 'OpenAI budget image model.', 'prautoblogger' ),
|
||||
],
|
||||
[
|
||||
),
|
||||
array(
|
||||
'id' => 'openai/gpt-5-image',
|
||||
'name' => 'GPT-5 Image',
|
||||
'provider' => 'openrouter',
|
||||
'cost_per_image' => 0.08,
|
||||
'capabilities' => [ 'image_generation' ],
|
||||
'capabilities' => array( 'image_generation' ),
|
||||
'description' => __( 'OpenAI premium. Photorealistic.', 'prautoblogger' ),
|
||||
],
|
||||
];
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -29,7 +29,7 @@ class PRAutoBlogger_Log_Viewer {
|
||||
__( 'Activity Log', 'prautoblogger' ),
|
||||
'manage_options',
|
||||
'prautoblogger-logs',
|
||||
[ $this, 'render_page' ]
|
||||
array( $this, 'render_page' )
|
||||
);
|
||||
}
|
||||
|
||||
@@ -66,19 +66,21 @@ class PRAutoBlogger_Log_Viewer {
|
||||
check_ajax_referer( 'prautoblogger_clear_logs', 'nonce' );
|
||||
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
wp_send_json_error( [ 'message' => __( 'Insufficient permissions.', 'prautoblogger' ) ], 403 );
|
||||
wp_send_json_error( array( 'message' => __( 'Insufficient permissions.', 'prautoblogger' ) ), 403 );
|
||||
return;
|
||||
}
|
||||
|
||||
$days = isset( $_POST['days'] ) ? absint( $_POST['days'] ) : 30;
|
||||
$deleted = PRAutoBlogger_Logger::prune( $days );
|
||||
|
||||
wp_send_json_success( [
|
||||
'message' => sprintf(
|
||||
wp_send_json_success(
|
||||
array(
|
||||
'message' => sprintf(
|
||||
/* translators: %d: number of entries deleted */
|
||||
__( '%d log entries cleared.', 'prautoblogger' ),
|
||||
$deleted
|
||||
),
|
||||
] );
|
||||
__( '%d log entries cleared.', 'prautoblogger' ),
|
||||
$deleted
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ class PRAutoBlogger_Metrics_Page {
|
||||
__( 'Metrics & Costs', 'prautoblogger' ),
|
||||
'manage_options',
|
||||
'prautoblogger-metrics',
|
||||
[ $this, 'render_page' ]
|
||||
array( $this, 'render_page' )
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -23,11 +23,11 @@ class PRAutoBlogger_Post_List_Columns {
|
||||
* Side effects: adds WordPress filters/actions for post list customization.
|
||||
*/
|
||||
public function register(): void {
|
||||
add_filter( 'manage_posts_columns', [ $this, 'filter_add_columns' ] );
|
||||
add_action( 'manage_posts_custom_column', [ $this, 'on_render_column' ], 10, 2 );
|
||||
add_filter( 'manage_edit-post_sortable_columns', [ $this, 'filter_sortable_columns' ] );
|
||||
add_action( 'pre_get_posts', [ $this, 'on_sort_by_model' ] );
|
||||
add_action( 'admin_head', [ $this, 'on_column_styles' ] );
|
||||
add_filter( 'manage_posts_columns', array( $this, 'filter_add_columns' ) );
|
||||
add_action( 'manage_posts_custom_column', array( $this, 'on_render_column' ), 10, 2 );
|
||||
add_filter( 'manage_edit-post_sortable_columns', array( $this, 'filter_sortable_columns' ) );
|
||||
add_action( 'pre_get_posts', array( $this, 'on_sort_by_model' ) );
|
||||
add_action( 'admin_head', array( $this, 'on_column_styles' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -37,7 +37,7 @@ class PRAutoBlogger_Post_List_Columns {
|
||||
* @return array<string, string> Modified columns.
|
||||
*/
|
||||
public function filter_add_columns( array $columns ): array {
|
||||
$new = [];
|
||||
$new = array();
|
||||
foreach ( $columns as $key => $label ) {
|
||||
$new[ $key ] = $label;
|
||||
if ( 'title' === $key ) {
|
||||
@@ -192,7 +192,7 @@ class PRAutoBlogger_Post_List_Columns {
|
||||
return;
|
||||
}
|
||||
|
||||
$total = array_sum( array_column( $rows, 'estimated_cost' ) );
|
||||
$total = array_sum( array_column( $rows, 'estimated_cost' ) );
|
||||
$formatted = $total < 0.01
|
||||
? sprintf( '$%.4f', $total )
|
||||
: sprintf( '$%.2f', $total );
|
||||
@@ -243,7 +243,7 @@ class PRAutoBlogger_Post_List_Columns {
|
||||
. '<td><strong>%s</strong></td></tr>',
|
||||
esc_html( $total_f )
|
||||
);
|
||||
$html .= '</table>';
|
||||
$html .= '</table>';
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ class PRAutoBlogger_Post_Metabox {
|
||||
add_meta_box(
|
||||
'prautoblogger_generation_info',
|
||||
__( 'PRAutoBlogger Generation Info', 'prautoblogger' ),
|
||||
[ $this, 'render_metabox' ],
|
||||
array( $this, 'render_metabox' ),
|
||||
'post',
|
||||
'side',
|
||||
'default'
|
||||
|
||||
@@ -28,11 +28,11 @@ class PRAutoBlogger_Review_Queue {
|
||||
__( 'Review Queue', 'prautoblogger' ),
|
||||
'manage_options',
|
||||
'prautoblogger-review-queue',
|
||||
[ $this, 'render_page' ]
|
||||
array( $this, 'render_page' )
|
||||
);
|
||||
|
||||
if ( false !== $hook ) {
|
||||
add_action( "load-{$hook}", [ $this, 'on_handle_bulk_actions' ] );
|
||||
add_action( "load-{$hook}", array( $this, 'on_handle_bulk_actions' ) );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,20 +60,22 @@ class PRAutoBlogger_Review_Queue {
|
||||
* @return \WP_Query
|
||||
*/
|
||||
public function get_pending_posts( int $paged = 1 ): \WP_Query {
|
||||
return new \WP_Query( [
|
||||
'post_type' => 'post',
|
||||
'post_status' => 'draft',
|
||||
'posts_per_page' => 20,
|
||||
'paged' => $paged,
|
||||
'meta_query' => [
|
||||
[
|
||||
'key' => '_prautoblogger_generated',
|
||||
'value' => '1',
|
||||
],
|
||||
],
|
||||
'orderby' => 'date',
|
||||
'order' => 'DESC',
|
||||
] );
|
||||
return new \WP_Query(
|
||||
array(
|
||||
'post_type' => 'post',
|
||||
'post_status' => 'draft',
|
||||
'posts_per_page' => 20,
|
||||
'paged' => $paged,
|
||||
'meta_query' => array(
|
||||
array(
|
||||
'key' => '_prautoblogger_generated',
|
||||
'value' => '1',
|
||||
),
|
||||
),
|
||||
'orderby' => 'date',
|
||||
'order' => 'DESC',
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -100,7 +102,7 @@ class PRAutoBlogger_Review_Queue {
|
||||
$action = sanitize_text_field( wp_unslash( $_POST['prautoblogger_bulk_action'] ) );
|
||||
$post_ids = isset( $_POST['prautoblogger_post_ids'] ) && is_array( $_POST['prautoblogger_post_ids'] )
|
||||
? array_map( 'absint', $_POST['prautoblogger_post_ids'] )
|
||||
: [];
|
||||
: array();
|
||||
|
||||
if ( empty( $post_ids ) ) {
|
||||
return;
|
||||
@@ -113,25 +115,31 @@ class PRAutoBlogger_Review_Queue {
|
||||
}
|
||||
|
||||
if ( 'approve' === $action ) {
|
||||
$result = wp_update_post( [ 'ID' => $post_id, 'post_status' => 'publish' ], true );
|
||||
$result = wp_update_post(
|
||||
array(
|
||||
'ID' => $post_id,
|
||||
'post_status' => 'publish',
|
||||
),
|
||||
true
|
||||
);
|
||||
if ( ! is_wp_error( $result ) ) {
|
||||
update_post_meta( $post_id, '_prautoblogger_approved_at', gmdate( 'c' ) );
|
||||
$count++;
|
||||
++$count;
|
||||
}
|
||||
} elseif ( 'reject' === $action ) {
|
||||
$result = wp_trash_post( $post_id );
|
||||
if ( false !== $result ) {
|
||||
$count++;
|
||||
++$count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$redirect = add_query_arg(
|
||||
[
|
||||
'page' => 'prautoblogger-review-queue',
|
||||
array(
|
||||
'page' => 'prautoblogger-review-queue',
|
||||
'prautoblogger_bulk_done' => $count,
|
||||
'prautoblogger_bulk_type' => $action,
|
||||
],
|
||||
),
|
||||
admin_url( 'admin.php' )
|
||||
);
|
||||
|
||||
@@ -150,25 +158,36 @@ class PRAutoBlogger_Review_Queue {
|
||||
check_ajax_referer( 'prautoblogger_review_queue', 'nonce' );
|
||||
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
wp_send_json_error( [ 'message' => __( 'Insufficient permissions.', 'prautoblogger' ) ], 403 );
|
||||
wp_send_json_error( array( 'message' => __( 'Insufficient permissions.', 'prautoblogger' ) ), 403 );
|
||||
return;
|
||||
}
|
||||
|
||||
$post_id = isset( $_POST['post_id'] ) ? absint( $_POST['post_id'] ) : 0;
|
||||
if ( $post_id <= 0 ) {
|
||||
wp_send_json_error( [ 'message' => __( 'Invalid post ID.', 'prautoblogger' ) ] );
|
||||
wp_send_json_error( array( 'message' => __( 'Invalid post ID.', 'prautoblogger' ) ) );
|
||||
return;
|
||||
}
|
||||
|
||||
$result = wp_update_post( [ 'ID' => $post_id, 'post_status' => 'publish' ], true );
|
||||
$result = wp_update_post(
|
||||
array(
|
||||
'ID' => $post_id,
|
||||
'post_status' => 'publish',
|
||||
),
|
||||
true
|
||||
);
|
||||
|
||||
if ( is_wp_error( $result ) ) {
|
||||
wp_send_json_error( [ 'message' => $result->get_error_message() ] );
|
||||
wp_send_json_error( array( 'message' => $result->get_error_message() ) );
|
||||
return;
|
||||
}
|
||||
|
||||
update_post_meta( $post_id, '_prautoblogger_approved_at', gmdate( 'c' ) );
|
||||
wp_send_json_success( [ 'message' => __( 'Post published.', 'prautoblogger' ), 'post_id' => $post_id ] );
|
||||
wp_send_json_success(
|
||||
array(
|
||||
'message' => __( 'Post published.', 'prautoblogger' ),
|
||||
'post_id' => $post_id,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -182,22 +201,27 @@ class PRAutoBlogger_Review_Queue {
|
||||
check_ajax_referer( 'prautoblogger_review_queue', 'nonce' );
|
||||
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
wp_send_json_error( [ 'message' => __( 'Insufficient permissions.', 'prautoblogger' ) ], 403 );
|
||||
wp_send_json_error( array( 'message' => __( 'Insufficient permissions.', 'prautoblogger' ) ), 403 );
|
||||
return;
|
||||
}
|
||||
|
||||
$post_id = isset( $_POST['post_id'] ) ? absint( $_POST['post_id'] ) : 0;
|
||||
if ( $post_id <= 0 ) {
|
||||
wp_send_json_error( [ 'message' => __( 'Invalid post ID.', 'prautoblogger' ) ] );
|
||||
wp_send_json_error( array( 'message' => __( 'Invalid post ID.', 'prautoblogger' ) ) );
|
||||
return;
|
||||
}
|
||||
|
||||
$result = wp_trash_post( $post_id );
|
||||
if ( false === $result || null === $result ) {
|
||||
wp_send_json_error( [ 'message' => __( 'Failed to reject post.', 'prautoblogger' ) ] );
|
||||
wp_send_json_error( array( 'message' => __( 'Failed to reject post.', 'prautoblogger' ) ) );
|
||||
return;
|
||||
}
|
||||
|
||||
wp_send_json_success( [ 'message' => __( 'Post rejected.', 'prautoblogger' ), 'post_id' => $post_id ] );
|
||||
wp_send_json_success(
|
||||
array(
|
||||
'message' => __( 'Post rejected.', 'prautoblogger' ),
|
||||
'post_id' => $post_id,
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,9 +33,9 @@ class PRAutoBlogger_Settings_Fields_Extended {
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
public static function get_fields(): array {
|
||||
return [
|
||||
return array(
|
||||
// ── Sources (extended) ──────────────────────────────────────
|
||||
[
|
||||
array(
|
||||
'id' => 'prautoblogger_research_model',
|
||||
'label' => __( 'Research Model', 'prautoblogger' ),
|
||||
'type' => 'model_select',
|
||||
@@ -44,43 +44,43 @@ class PRAutoBlogger_Settings_Fields_Extended {
|
||||
'capability' => 'text→text',
|
||||
'description' => __( 'Model for LLM Deep Research. Pick a reasoning-capable model (e.g. Grok 4.1 Fast, DeepSeek-R1) for best results. Only used when LLM Deep Research is enabled above.', 'prautoblogger' ),
|
||||
'badge' => __( 'Reasoning', 'prautoblogger' ),
|
||||
],
|
||||
[
|
||||
),
|
||||
array(
|
||||
'id' => 'prautoblogger_research_prompt',
|
||||
'label' => __( 'Research Prompt', 'prautoblogger' ),
|
||||
'type' => 'textarea',
|
||||
'section' => 'prautoblogger_sources',
|
||||
'default' => 'Conduct a deep research sweep of the {niche} space and identify the most substantive, actionable topics that practitioners and curious newcomers are searching for or actively discussing.' . "\n\n" . 'Focus on:' . "\n" . '- Practical questions real readers would search for (not theoretical debates)' . "\n" . '- Emerging trends gaining momentum in the community' . "\n" . '- Common misconceptions that deserve clarification' . "\n" . '- Comparative analysis (product vs. product, protocol vs. protocol)' . "\n" . '- Safety and efficacy topics that drive concern or curiosity' . "\n" . '- Beginner-to-advanced mix — cover entry-level and depth-focused topics' . "\n\n" . 'Aim for topics that feel timely, underserved by existing content, and likely to drive reader engagement. Return 8-12 findings with detailed analysis in each.',
|
||||
'description' => __( 'The research brief sent to the LLM. Use {niche} as a placeholder for your niche description. The system prompt handles output format — this controls WHAT to research.', 'prautoblogger' ),
|
||||
],
|
||||
),
|
||||
|
||||
// ── AI Models (extended) ────────────────────────────────────
|
||||
[
|
||||
array(
|
||||
'id' => 'prautoblogger_reasoning_enabled',
|
||||
'label' => __( 'Enable Reasoning', 'prautoblogger' ),
|
||||
'type' => 'toggle',
|
||||
'section' => 'prautoblogger_models',
|
||||
'default' => '0',
|
||||
'description' => __( 'Send reasoning instructions to models that support it (e.g. Grok, DeepSeek-R1). Reasoning tokens are billed as output tokens — expect higher costs per call. Models that don\'t support reasoning will ignore this.', 'prautoblogger' ),
|
||||
],
|
||||
[
|
||||
'id' => 'prautoblogger_reasoning_effort',
|
||||
'label' => __( 'Reasoning Effort', 'prautoblogger' ),
|
||||
'type' => 'select',
|
||||
'section' => 'prautoblogger_models',
|
||||
'default' => 'medium',
|
||||
'options' => [
|
||||
),
|
||||
array(
|
||||
'id' => 'prautoblogger_reasoning_effort',
|
||||
'label' => __( 'Reasoning Effort', 'prautoblogger' ),
|
||||
'type' => 'select',
|
||||
'section' => 'prautoblogger_models',
|
||||
'default' => 'medium',
|
||||
'options' => array(
|
||||
'xhigh' => __( 'Extra High — maximum depth, highest cost', 'prautoblogger' ),
|
||||
'high' => __( 'High — thorough reasoning', 'prautoblogger' ),
|
||||
'medium' => __( 'Medium — balanced (recommended)', 'prautoblogger' ),
|
||||
'low' => __( 'Low — light reasoning, lower cost', 'prautoblogger' ),
|
||||
'minimal' => __( 'Minimal — barely any reasoning', 'prautoblogger' ),
|
||||
],
|
||||
),
|
||||
'description' => __( 'How much effort the model spends reasoning before answering. Higher effort = more reasoning tokens = higher cost. Only applies when reasoning is enabled above.', 'prautoblogger' ),
|
||||
],
|
||||
),
|
||||
|
||||
// ── Schedule & Budget ───────────────────────────────────────
|
||||
[
|
||||
array(
|
||||
'id' => 'prautoblogger_daily_article_target',
|
||||
'label' => __( 'Articles per Day', 'prautoblogger' ),
|
||||
'type' => 'number',
|
||||
@@ -88,8 +88,8 @@ class PRAutoBlogger_Settings_Fields_Extended {
|
||||
'default' => 1,
|
||||
'min' => 1,
|
||||
'max' => 10,
|
||||
],
|
||||
[
|
||||
),
|
||||
array(
|
||||
'id' => 'prautoblogger_schedule_time',
|
||||
'label' => __( 'Generation Time', 'prautoblogger' ),
|
||||
'type' => 'time',
|
||||
@@ -100,8 +100,8 @@ class PRAutoBlogger_Settings_Fields_Extended {
|
||||
esc_html__( "Daily generation runs at this time in your site's configured timezone (Settings → General → Timezone). Current site timezone: %s.", 'prautoblogger' ),
|
||||
esc_html( function_exists( 'wp_timezone_string' ) ? wp_timezone_string() : 'UTC' )
|
||||
),
|
||||
],
|
||||
[
|
||||
),
|
||||
array(
|
||||
'id' => 'prautoblogger_monthly_budget_usd',
|
||||
'label' => __( 'Monthly Budget (USD)', 'prautoblogger' ),
|
||||
'type' => 'number',
|
||||
@@ -110,66 +110,66 @@ class PRAutoBlogger_Settings_Fields_Extended {
|
||||
'min' => 0,
|
||||
'step' => '0.01',
|
||||
'description' => __( 'Hard stop when reached. 0 = unlimited.', 'prautoblogger' ),
|
||||
],
|
||||
),
|
||||
|
||||
// ── Publishing ──────────────────────────────────────────────
|
||||
[
|
||||
array(
|
||||
'id' => 'prautoblogger_auto_publish',
|
||||
'label' => __( 'Auto-Publish', 'prautoblogger' ),
|
||||
'type' => 'toggle',
|
||||
'section' => 'prautoblogger_publishing',
|
||||
'default' => '0',
|
||||
'description' => __( 'Automatically publish editor-approved posts. When off, all posts go to the Review Queue as drafts.', 'prautoblogger' ),
|
||||
],
|
||||
[
|
||||
),
|
||||
array(
|
||||
'id' => 'prautoblogger_default_author',
|
||||
'label' => __( 'Default Author', 'prautoblogger' ),
|
||||
'type' => 'author_select',
|
||||
'section' => 'prautoblogger_publishing',
|
||||
'default' => 0,
|
||||
'description' => __( 'WordPress user assigned as author of generated posts.', 'prautoblogger' ),
|
||||
],
|
||||
[
|
||||
),
|
||||
array(
|
||||
'id' => 'prautoblogger_default_category',
|
||||
'label' => __( 'Default Category', 'prautoblogger' ),
|
||||
'type' => 'category_select',
|
||||
'section' => 'prautoblogger_publishing',
|
||||
'default' => 0,
|
||||
'description' => __( 'Fallback category for generated posts.', 'prautoblogger' ),
|
||||
],
|
||||
[
|
||||
),
|
||||
array(
|
||||
'id' => 'prautoblogger_log_level',
|
||||
'label' => __( 'Log Level', 'prautoblogger' ),
|
||||
'type' => 'select',
|
||||
'section' => 'prautoblogger_publishing',
|
||||
'default' => 'info',
|
||||
'options' => [
|
||||
'options' => array(
|
||||
'error' => __( 'Error — only failures', 'prautoblogger' ),
|
||||
'warning' => __( 'Warning — errors + warnings', 'prautoblogger' ),
|
||||
'info' => __( 'Info — key events (recommended)', 'prautoblogger' ),
|
||||
'debug' => __( 'Debug — everything (verbose)', 'prautoblogger' ),
|
||||
],
|
||||
),
|
||||
'description' => __( 'Controls detail level in the Activity Log.', 'prautoblogger' ),
|
||||
],
|
||||
),
|
||||
|
||||
// ── Analytics ───────────────────────────────────────────────
|
||||
[
|
||||
array(
|
||||
'id' => 'prautoblogger_ga4_property_id',
|
||||
'label' => __( 'GA4 Property ID', 'prautoblogger' ),
|
||||
'type' => 'text',
|
||||
'section' => 'prautoblogger_analytics',
|
||||
'description' => __( 'Your GA4 Property ID — digits only, e.g. 123456789 (the code adds the "properties/" prefix internally). Find it in GA4 → Admin → Property details. Leave blank to skip GA4.', 'prautoblogger' ),
|
||||
],
|
||||
[
|
||||
),
|
||||
array(
|
||||
'id' => 'prautoblogger_ga4_credentials_json',
|
||||
'label' => __( 'Service Account JSON', 'prautoblogger' ),
|
||||
'type' => 'password',
|
||||
'section' => 'prautoblogger_analytics',
|
||||
'description' => __( 'Paste the full JSON key file for a service account with Analytics read access.', 'prautoblogger' ),
|
||||
],
|
||||
),
|
||||
|
||||
// ── Display ────────────────────────────────────────────────
|
||||
[
|
||||
array(
|
||||
'id' => 'prautoblogger_article_font_family',
|
||||
'label' => __( 'Article Font', 'prautoblogger' ),
|
||||
'type' => 'select',
|
||||
@@ -177,8 +177,8 @@ class PRAutoBlogger_Settings_Fields_Extended {
|
||||
'default' => 'default',
|
||||
'options' => PRAutoBlogger_Article_Typography::get_font_choices(),
|
||||
'description' => __( 'Font family used for generated article body text. Serif fonts improve long-form readability.', 'prautoblogger' ),
|
||||
],
|
||||
[
|
||||
),
|
||||
array(
|
||||
'id' => 'prautoblogger_article_font_size',
|
||||
'label' => __( 'Article Font Size (px)', 'prautoblogger' ),
|
||||
'type' => 'number',
|
||||
@@ -187,34 +187,34 @@ class PRAutoBlogger_Settings_Fields_Extended {
|
||||
'min' => 0,
|
||||
'max' => 32,
|
||||
'description' => __( 'Base font size for article body text. 0 uses the theme default (13px). Recommended: 16–18px for comfortable reading.', 'prautoblogger' ),
|
||||
],
|
||||
[
|
||||
),
|
||||
array(
|
||||
'id' => 'prautoblogger_table_borders',
|
||||
'label' => __( 'Table Borders', 'prautoblogger' ),
|
||||
'type' => 'toggle',
|
||||
'section' => 'prautoblogger_display',
|
||||
'default' => '1',
|
||||
'description' => __( 'Add visible borders, padding, and alternating row colors to tables in generated articles.', 'prautoblogger' ),
|
||||
],
|
||||
),
|
||||
|
||||
// ── Images ─────────────────────────────────────────────────
|
||||
[
|
||||
array(
|
||||
'id' => 'prautoblogger_image_enabled',
|
||||
'label' => __( 'Enable Image Generation', 'prautoblogger' ),
|
||||
'type' => 'toggle',
|
||||
'section' => 'prautoblogger_images',
|
||||
'default' => '0',
|
||||
'description' => __( 'Generate images for each published article.', 'prautoblogger' ),
|
||||
],
|
||||
[
|
||||
),
|
||||
array(
|
||||
'id' => 'prautoblogger_image_b_enabled',
|
||||
'label' => __( 'Generate Second Image (B)', 'prautoblogger' ),
|
||||
'type' => 'toggle',
|
||||
'section' => 'prautoblogger_images',
|
||||
'default' => '1',
|
||||
'description' => __( 'Generate a second image from source data for A/B testing. Disabling saves one image generation + one LLM prompt rewrite per article.', 'prautoblogger' ),
|
||||
],
|
||||
[
|
||||
),
|
||||
array(
|
||||
'id' => 'prautoblogger_image_model',
|
||||
'label' => __( 'Image Model', 'prautoblogger' ),
|
||||
'type' => 'model_select',
|
||||
@@ -223,39 +223,39 @@ class PRAutoBlogger_Settings_Fields_Extended {
|
||||
'capability' => 'image_generation',
|
||||
'description' => __( 'Pick an image model. The provider (Runware or OpenRouter) is derived from the model registry on save, so mismatched pairs are no longer possible.', 'prautoblogger' ),
|
||||
'badge' => __( 'Quality', 'prautoblogger' ),
|
||||
],
|
||||
[
|
||||
),
|
||||
array(
|
||||
'id' => 'prautoblogger_runware_api_key',
|
||||
'label' => __( 'Runware API Key', 'prautoblogger' ),
|
||||
'type' => 'password',
|
||||
'section' => 'prautoblogger_images',
|
||||
'description' => __( 'Required when using a Runware FLUX model. Get your key at runware.ai/signup.', 'prautoblogger' ),
|
||||
'icon' => '🔑',
|
||||
],
|
||||
[
|
||||
),
|
||||
array(
|
||||
'id' => 'prautoblogger_image_style_suffix',
|
||||
'label' => __( 'Style Suffix', 'prautoblogger' ),
|
||||
'type' => 'textarea',
|
||||
'section' => 'prautoblogger_images',
|
||||
'default' => PRAUTOBLOGGER_DEFAULT_IMAGE_STYLE_SUFFIX,
|
||||
'description' => __( 'Appended to every image prompt. Controls visual look. Changing mid-run causes visible style drift.', 'prautoblogger' ),
|
||||
],
|
||||
[
|
||||
),
|
||||
array(
|
||||
'id' => 'prautoblogger_image_prompt_instructions',
|
||||
'label' => __( 'Image Prompt Instructions', 'prautoblogger' ),
|
||||
'type' => 'textarea',
|
||||
'section' => 'prautoblogger_images',
|
||||
'default' => PRAutoBlogger_Image_Prompt_Builder::REWRITER_SYSTEM_PROMPT,
|
||||
'description' => __( 'System prompt given to the rewriter LLM that turns each article into a SCENE + CAPTION for the image generator. Changing this reshapes the look of all future images. Leave blank to use the default.', 'prautoblogger' ),
|
||||
],
|
||||
[
|
||||
),
|
||||
array(
|
||||
'id' => 'prautoblogger_image_nsfw_retry',
|
||||
'label' => __( 'Retry NSFW-Blocked Images', 'prautoblogger' ),
|
||||
'type' => 'toggle',
|
||||
'section' => 'prautoblogger_images',
|
||||
'default' => '1',
|
||||
'description' => __( 'When the provider rejects an image prompt as NSFW, retry once with a generic fallback scene built from the article title. Disable to fail fast if the filter gets trigger-happy.', 'prautoblogger' ),
|
||||
],
|
||||
];
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,53 +24,53 @@ class PRAutoBlogger_Settings_Fields {
|
||||
* @return array<string, array{title: string, icon: string, description: string}>
|
||||
*/
|
||||
public static function get_sections(): array {
|
||||
return [
|
||||
'prautoblogger_api' => [
|
||||
return array(
|
||||
'prautoblogger_api' => array(
|
||||
'title' => __( 'API Keys', 'prautoblogger' ),
|
||||
'icon' => 'dashicons-admin-network',
|
||||
'description' => __( 'Connect your external services. Keys are encrypted at rest.', 'prautoblogger' ),
|
||||
],
|
||||
'prautoblogger_models' => [
|
||||
),
|
||||
'prautoblogger_models' => array(
|
||||
'title' => __( 'AI Models', 'prautoblogger' ),
|
||||
'icon' => 'dashicons-superhero-alt',
|
||||
'description' => __( 'Choose which models power each stage of the pipeline.', 'prautoblogger' ),
|
||||
],
|
||||
'prautoblogger_content' => [
|
||||
),
|
||||
'prautoblogger_content' => array(
|
||||
'title' => __( 'Content', 'prautoblogger' ),
|
||||
'icon' => 'dashicons-edit-large',
|
||||
'description' => __( 'Control tone, length, pipeline mode, and topic guardrails.', 'prautoblogger' ),
|
||||
],
|
||||
'prautoblogger_sources' => [
|
||||
),
|
||||
'prautoblogger_sources' => array(
|
||||
'title' => __( 'Sources', 'prautoblogger' ),
|
||||
'icon' => 'dashicons-rss',
|
||||
'description' => __( 'Configure where PRAutoBlogger finds trending topics.', 'prautoblogger' ),
|
||||
],
|
||||
'prautoblogger_schedule' => [
|
||||
),
|
||||
'prautoblogger_schedule' => array(
|
||||
'title' => __( 'Schedule & Budget', 'prautoblogger' ),
|
||||
'icon' => 'dashicons-calendar-alt',
|
||||
'description' => __( 'Set daily generation schedule, volume, and spending limits.', 'prautoblogger' ),
|
||||
],
|
||||
'prautoblogger_publishing' => [
|
||||
),
|
||||
'prautoblogger_publishing' => array(
|
||||
'title' => __( 'Publishing', 'prautoblogger' ),
|
||||
'icon' => 'dashicons-megaphone',
|
||||
'description' => __( 'Control how generated content is published.', 'prautoblogger' ),
|
||||
],
|
||||
'prautoblogger_analytics' => [
|
||||
),
|
||||
'prautoblogger_analytics' => array(
|
||||
'title' => __( 'Analytics', 'prautoblogger' ),
|
||||
'icon' => 'dashicons-chart-area',
|
||||
'description' => __( 'Connect Google Analytics 4 for post performance scoring.', 'prautoblogger' ),
|
||||
],
|
||||
'prautoblogger_display' => [
|
||||
),
|
||||
'prautoblogger_display' => array(
|
||||
'title' => __( 'Display', 'prautoblogger' ),
|
||||
'icon' => 'dashicons-editor-textcolor',
|
||||
'description' => __( 'Control how generated articles look on the frontend — fonts, sizes, and table styling.', 'prautoblogger' ),
|
||||
],
|
||||
'prautoblogger_images' => [
|
||||
),
|
||||
'prautoblogger_images' => array(
|
||||
'title' => __( 'Images', 'prautoblogger' ),
|
||||
'icon' => 'dashicons-format-image',
|
||||
'description' => __( 'Image provider, model, and style controls. OpenRouter reuses your existing API key.', 'prautoblogger' ),
|
||||
],
|
||||
];
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -88,25 +88,25 @@ class PRAutoBlogger_Settings_Fields {
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
private static function get_core_fields(): array {
|
||||
return [
|
||||
return array(
|
||||
// ── API Keys ────────────────────────────────────────────────
|
||||
[
|
||||
array(
|
||||
'id' => 'prautoblogger_openrouter_api_key',
|
||||
'label' => __( 'OpenRouter API Key', 'prautoblogger' ),
|
||||
'type' => 'password',
|
||||
'section' => 'prautoblogger_api',
|
||||
'description' => __( 'Get your key at openrouter.ai/keys', 'prautoblogger' ),
|
||||
'icon' => '🔑',
|
||||
],
|
||||
[
|
||||
),
|
||||
array(
|
||||
'id' => 'prautoblogger_reddit_source_status',
|
||||
'label' => __( 'Reddit Data Source', 'prautoblogger' ),
|
||||
'type' => 'source_status',
|
||||
'section' => 'prautoblogger_api',
|
||||
'description' => __( 'Uses Reddit RSS (primary) and .json (fallback). No API key required.', 'prautoblogger' ),
|
||||
'icon' => '📡',
|
||||
],
|
||||
[
|
||||
),
|
||||
array(
|
||||
'id' => 'prautoblogger_ai_gateway_base_url',
|
||||
'label' => __( 'Cloudflare AI Gateway URL', 'prautoblogger' ),
|
||||
'type' => 'text',
|
||||
@@ -114,8 +114,8 @@ class PRAutoBlogger_Settings_Fields {
|
||||
'default' => '',
|
||||
'description' => __( 'Optional. Route OpenRouter calls through a Cloudflare AI Gateway for caching, cost logging, and rate limiting. Format: https://gateway.ai.cloudflare.com/v1/{account_id}/{gateway_id}/openrouter — leave blank to call OpenRouter directly.', 'prautoblogger' ),
|
||||
'icon' => '☁️',
|
||||
],
|
||||
[
|
||||
),
|
||||
array(
|
||||
'id' => 'prautoblogger_ai_gateway_cache_ttl',
|
||||
'label' => __( 'AI Gateway Cache TTL (seconds)', 'prautoblogger' ),
|
||||
'type' => 'number',
|
||||
@@ -124,10 +124,10 @@ class PRAutoBlogger_Settings_Fields {
|
||||
'min' => 0,
|
||||
'max' => 2592000,
|
||||
'description' => __( 'How long Cloudflare may serve cached responses for identical LLM calls. 0 disables caching. Only used when a gateway URL is set above. Safe values: 0 for article generation (always fresh), 3600+ for repeated classification/scoring calls.', 'prautoblogger' ),
|
||||
],
|
||||
),
|
||||
|
||||
// ── AI Models ───────────────────────────────────────────────
|
||||
[
|
||||
array(
|
||||
'id' => 'prautoblogger_analysis_model',
|
||||
'label' => __( 'Analysis Model', 'prautoblogger' ),
|
||||
'type' => 'model_select',
|
||||
@@ -136,8 +136,8 @@ class PRAutoBlogger_Settings_Fields {
|
||||
'capability' => 'text→text',
|
||||
'description' => __( 'Used for source analysis. Pick a cheap, fast model.', 'prautoblogger' ),
|
||||
'badge' => __( 'Low cost', 'prautoblogger' ),
|
||||
],
|
||||
[
|
||||
),
|
||||
array(
|
||||
'id' => 'prautoblogger_writing_model',
|
||||
'label' => __( 'Writing Model', 'prautoblogger' ),
|
||||
'type' => 'model_select',
|
||||
@@ -146,8 +146,8 @@ class PRAutoBlogger_Settings_Fields {
|
||||
'capability' => 'text→text',
|
||||
'description' => __( 'Used for article generation. Quality matters here.', 'prautoblogger' ),
|
||||
'badge' => __( 'Quality', 'prautoblogger' ),
|
||||
],
|
||||
[
|
||||
),
|
||||
array(
|
||||
'id' => 'prautoblogger_editor_model',
|
||||
'label' => __( 'Editor Model', 'prautoblogger' ),
|
||||
'type' => 'model_select',
|
||||
@@ -156,110 +156,110 @@ class PRAutoBlogger_Settings_Fields {
|
||||
'capability' => 'text→text',
|
||||
'description' => __( 'Used for the chief editor review pass.', 'prautoblogger' ),
|
||||
'badge' => __( 'Quality', 'prautoblogger' ),
|
||||
],
|
||||
),
|
||||
|
||||
// ── Content ─────────────────────────────────────────────────
|
||||
[
|
||||
array(
|
||||
'id' => 'prautoblogger_niche_description',
|
||||
'label' => __( 'Niche Description', 'prautoblogger' ),
|
||||
'type' => 'textarea',
|
||||
'section' => 'prautoblogger_content',
|
||||
'description' => __( 'Describe your site\'s niche. Guides content analysis and generation.', 'prautoblogger' ),
|
||||
],
|
||||
[
|
||||
),
|
||||
array(
|
||||
'id' => 'prautoblogger_analysis_instructions',
|
||||
'label' => __( 'Analysis Instructions', 'prautoblogger' ),
|
||||
'type' => 'textarea',
|
||||
'section' => 'prautoblogger_content',
|
||||
'default' => '',
|
||||
'description' => __( 'Custom instructions for the topic analysis LLM. Steers how source data is evaluated and which ideas get surfaced — e.g. "Prioritize questions over news. Ignore price-related discussions."', 'prautoblogger' ),
|
||||
],
|
||||
[
|
||||
),
|
||||
array(
|
||||
'id' => 'prautoblogger_tone',
|
||||
'label' => __( 'Content Tone', 'prautoblogger' ),
|
||||
'type' => 'select',
|
||||
'section' => 'prautoblogger_content',
|
||||
'default' => 'informational',
|
||||
'options' => [
|
||||
'options' => array(
|
||||
'informational' => __( 'Informational', 'prautoblogger' ),
|
||||
'conversational' => __( 'Conversational', 'prautoblogger' ),
|
||||
'professional' => __( 'Professional', 'prautoblogger' ),
|
||||
'casual' => __( 'Casual', 'prautoblogger' ),
|
||||
'authoritative' => __( 'Authoritative', 'prautoblogger' ),
|
||||
],
|
||||
],
|
||||
[
|
||||
),
|
||||
),
|
||||
array(
|
||||
'id' => 'prautoblogger_writing_pipeline',
|
||||
'label' => __( 'Writing Pipeline', 'prautoblogger' ),
|
||||
'type' => 'select',
|
||||
'section' => 'prautoblogger_content',
|
||||
'default' => 'multi_step',
|
||||
'options' => [
|
||||
'options' => array(
|
||||
'multi_step' => __( 'Multi-step (outline → draft → polish)', 'prautoblogger' ),
|
||||
'single_pass' => __( 'Single-pass (one LLM call)', 'prautoblogger' ),
|
||||
],
|
||||
],
|
||||
[
|
||||
),
|
||||
),
|
||||
array(
|
||||
'id' => 'prautoblogger_min_word_count',
|
||||
'label' => __( 'Min Word Count', 'prautoblogger' ),
|
||||
'type' => 'number',
|
||||
'section' => 'prautoblogger_content',
|
||||
'default' => 800,
|
||||
'min' => 200,
|
||||
],
|
||||
[
|
||||
),
|
||||
array(
|
||||
'id' => 'prautoblogger_max_word_count',
|
||||
'label' => __( 'Max Word Count', 'prautoblogger' ),
|
||||
'type' => 'number',
|
||||
'section' => 'prautoblogger_content',
|
||||
'default' => 2000,
|
||||
'min' => 500,
|
||||
],
|
||||
[
|
||||
),
|
||||
array(
|
||||
'id' => 'prautoblogger_writing_instructions',
|
||||
'label' => __( 'Writing Instructions', 'prautoblogger' ),
|
||||
'type' => 'textarea',
|
||||
'section' => 'prautoblogger_content',
|
||||
'default' => '',
|
||||
'description' => __( 'Custom instructions appended to the LLM\'s system prompt when writing articles. Use this to steer style, structure, voice, and formatting — e.g. "Always open with a hook question. Use short paragraphs. Cite at least two studies."', 'prautoblogger' ),
|
||||
],
|
||||
[
|
||||
),
|
||||
array(
|
||||
'id' => 'prautoblogger_editor_instructions',
|
||||
'label' => __( 'Editor Instructions', 'prautoblogger' ),
|
||||
'type' => 'textarea',
|
||||
'section' => 'prautoblogger_content',
|
||||
'default' => '',
|
||||
'description' => __( 'Custom instructions for the chief editor LLM review pass. Steers what the editor looks for and how it revises — e.g. "Be strict about medical disclaimers. Reject any article under 800 words."', 'prautoblogger' ),
|
||||
],
|
||||
[
|
||||
),
|
||||
array(
|
||||
'id' => 'prautoblogger_topic_exclusions',
|
||||
'label' => __( 'Topic Exclusions', 'prautoblogger' ),
|
||||
'type' => 'textarea',
|
||||
'section' => 'prautoblogger_content',
|
||||
'description' => __( 'Comma-separated topics to never write about.', 'prautoblogger' ),
|
||||
],
|
||||
),
|
||||
|
||||
// ── Sources ─────────────────────────────────────────────────
|
||||
[
|
||||
array(
|
||||
'id' => 'prautoblogger_target_subreddits',
|
||||
'label' => __( 'Target Subreddits', 'prautoblogger' ),
|
||||
'type' => 'textarea',
|
||||
'section' => 'prautoblogger_sources',
|
||||
'description' => __( 'Comma-separated, without r/. E.g.: peptides, Nootropics, biohackers', 'prautoblogger' ),
|
||||
],
|
||||
[
|
||||
),
|
||||
array(
|
||||
'id' => 'prautoblogger_enabled_sources',
|
||||
'label' => __( 'Enabled Sources', 'prautoblogger' ),
|
||||
'type' => 'checkboxes',
|
||||
'section' => 'prautoblogger_sources',
|
||||
'options' => [
|
||||
'options' => array(
|
||||
'reddit' => __( 'Reddit (RSS + .json)', 'prautoblogger' ),
|
||||
'llm_research' => __( 'LLM Deep Research (reasoning model)', 'prautoblogger' ),
|
||||
],
|
||||
),
|
||||
'default' => '["reddit"]',
|
||||
'description' => __( 'Select which platforms to monitor for topics.', 'prautoblogger' ),
|
||||
],
|
||||
[
|
||||
),
|
||||
array(
|
||||
'id' => 'prautoblogger_pullpush_cache_ttl',
|
||||
'label' => __( 'Research Cache (hours)', 'prautoblogger' ),
|
||||
'type' => 'number',
|
||||
@@ -268,21 +268,21 @@ class PRAutoBlogger_Settings_Fields {
|
||||
'min' => 1,
|
||||
'max' => 72,
|
||||
'description' => __( 'How long to cache Reddit research results before re-fetching. Saves bandwidth on low-traffic sites.', 'prautoblogger' ),
|
||||
],
|
||||
[
|
||||
),
|
||||
array(
|
||||
'id' => 'prautoblogger_reddit_time_filter',
|
||||
'label' => __( 'Reddit Time Window', 'prautoblogger' ),
|
||||
'type' => 'select',
|
||||
'section' => 'prautoblogger_sources',
|
||||
'default' => 'day',
|
||||
'options' => [
|
||||
'options' => array(
|
||||
'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' ),
|
||||
],
|
||||
[
|
||||
),
|
||||
array(
|
||||
'id' => 'prautoblogger_reddit_posts_per_subreddit',
|
||||
'label' => __( 'Posts per Subreddit', 'prautoblogger' ),
|
||||
'type' => 'number',
|
||||
@@ -291,7 +291,7 @@ class PRAutoBlogger_Settings_Fields {
|
||||
'min' => 5,
|
||||
'max' => 100,
|
||||
'description' => __( 'Maximum posts to fetch per subreddit per collection run.', 'prautoblogger' ),
|
||||
],
|
||||
];
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user