Plugin Directory

Changeset 3441218


Ignore:
Timestamp:
01/16/2026 07:13:03 PM (3 months ago)
Author:
samukbg
Message:

Tagging version 1.1.3

Location:
rss-to-post-generator
Files:
8 edited
1 copied

Legend:

Unmodified
Added
Removed
  • rss-to-post-generator/tags/1.1.3/assets/js/admin.js

    r3415485 r3441218  
    606606        });
    607607
     608        // Handler for article size selection
     609        $('#article-size').on('change', function() {
     610            const size = $(this).val();
     611            const $feedbackSpan = $('#article-size-save-feedback');
     612
     613            $feedbackSpan.text('Saving...').removeClass('success error').addClass('saving');
     614
     615            $.ajax({
     616                url: rss2post_ajax.ajax_url,
     617                type: 'POST',
     618                data: {
     619                    action: 'rss2post_save_article_size',
     620                    nonce: rss2post_ajax.nonce,
     621                    article_size: size
     622                },
     623                success: function(response) {
     624                    if (response.success) {
     625                        $feedbackSpan.text('Saved!').removeClass('saving error').addClass('success');
     626                    } else {
     627                        $feedbackSpan.text('Error: ' + (response.data.message || 'Could not save.')).removeClass('saving success').addClass('error');
     628                    }
     629                },
     630                error: function() {
     631                    $feedbackSpan.text('Error: AJAX request failed.').removeClass('saving success').addClass('error');
     632                },
     633                complete: function() {
     634                    setTimeout(function() {
     635                        $feedbackSpan.text('').removeClass('saving success error');
     636                    }, 3000);
     637                }
     638            });
     639        });
     640
    608641        $('#save-tags-button').on('click', function() {
    609642            const selectedTags = [];
     
    741774        });
    742775       
     776        $('#runs-per-day, #articles-per-day').on('input', function() {
     777            let val = $(this).val();
     778            // Allow empty string to let user clear and retype
     779            if (val === '') return;
     780           
     781            // Remove any non-digit characters
     782            val = val.replace(/[^0-9]/g, '');
     783           
     784            let num = parseInt(val);
     785            if (isNaN(num)) num = '';
     786            else if (num < 1) num = 1;
     787            else if (num > 10) num = 10;
     788           
     789            // Update value if it changed (handle strings vs numbers carefully)
     790            if (val !== '' && num !== parseInt($(this).val())) {
     791                 $(this).val(num);
     792            } else if (val !== $(this).val()) {
     793                 // Covers the case where non-digits were removed
     794                 $(this).val(val);
     795            }
     796        });
     797
     798        // Ensure valid value on blur
     799        $('#runs-per-day, #articles-per-day').on('blur', function() {
     800            let val = parseInt($(this).val());
     801            if (isNaN(val) || val < 1) {
     802                $(this).val(1);
     803            } else if (val > 10) {
     804                $(this).val(10);
     805            }
     806        });
     807
     808        $('#save-automated-settings-button').on('click', saveAutomatedSettings);
     809
    743810        $('input[name="image_source"]').on('change', function() {
    744811            if ($(this).val() === 'pexels') {
     
    898965    // The more complete one defined earlier in the file will be used.
    899966
     967    function saveAutomatedSettings() {
     968        const runsPerDay = parseInt($('#runs-per-day').val()) || 2;
     969        const articlesPerDay = parseInt($('#articles-per-day').val()) || 10;
     970       
     971        if (runsPerDay < 1 || runsPerDay > 10) {
     972            alert('Runs per day must be between 1 and 10.');
     973            return;
     974        }
     975        if (articlesPerDay < 1 || articlesPerDay > 10) {
     976            alert('Articles per day must be between 1 and 10.');
     977            return;
     978        }
     979
     980        const $button = $('#save-automated-settings-button');
     981        const $feedback = $('#save-automated-settings-feedback');
     982       
     983        $button.prop('disabled', true).text('Saving...');
     984        $feedback.text('Saving...').removeClass('success error').addClass('saving');
     985       
     986        $.ajax({
     987            url: rss2post_ajax.ajax_url,
     988            type: 'POST',
     989            data: {
     990                action: 'rss2post_update_automated_settings',
     991                nonce: rss2post_ajax.nonce,
     992                runs_per_day: runsPerDay,
     993                articles_per_day: articlesPerDay
     994            },
     995            success: function(response) {
     996                if (response.success) {
     997                    $feedback.text('Settings saved!').removeClass('saving error').addClass('success');
     998                } else {
     999                    $feedback.text('Error: ' + (response.data.message || 'Could not save.')).removeClass('saving success').addClass('error');
     1000                }
     1001            },
     1002            error: function() {
     1003                $feedback.text('Error: Failed to save settings.').removeClass('saving success').addClass('error');
     1004            },
     1005            complete: function() {
     1006                $button.prop('disabled', false).text('Save Automation Settings');
     1007                setTimeout(function() {
     1008                    $feedback.text('').removeClass('saving success error');
     1009                }, 3000);
     1010            }
     1011        });
     1012    }
     1013
    9001014    function toggleAutomatedPosting() {
    9011015        const isEnabled = $(this).is(':checked');
    9021016        const feeds = $('#rss-feeds').val().trim();
     1017        const runsPerDay = parseInt($('#runs-per-day').val()) || 2;
     1018        const articlesPerDay = parseInt($('#articles-per-day').val()) || 10;
    9031019
    9041020        if (userTier !== 'pro' && userTier !== 'lifetime' && isEnabled) {
     
    9111027            if (!feeds) {
    9121028                alert('Please enter at least one RSS feed URL in the "RSS Feed URLs" section before enabling automated posting.');
     1029                $(this).prop('checked', false);
     1030                return;
     1031            }
     1032            if (articlesPerDay > 10) {
     1033                alert('The maximum amount of generation + posts is 10 per day.');
    9131034                $(this).prop('checked', false);
    9141035                return;
     
    9321053                enabled: isEnabled,
    9331054                feeds: feeds,
     1055                runs_per_day: runsPerDay,
     1056                articles_per_day: articlesPerDay,
    9341057                current_username: $('#wp-username').val().trim(),
    9351058                current_password: $('#wp-password').val().trim()
  • rss-to-post-generator/tags/1.1.3/includes/class-admin.php

    r3438847 r3441218  
    1616        add_action('rss2post_automated_hourly_job', array($this, 'do_automated_posting_job')); // Retained in case it's a valid, albeit unused, hook for some users
    1717        add_action('rss2post_automated_12h_job', array($this, 'do_automated_posting_job'));
     18        add_action('rss2post_automated_job', array($this, 'do_automated_posting_job'));
    1819        add_action('wp_ajax_rss2post_save_credentials', array($this, 'ajax_save_credentials'));
    1920        add_action('wp_ajax_rss2post_save_language', array($this, 'ajax_save_language'));
     21        add_action('wp_ajax_rss2post_save_article_size', array($this, 'ajax_save_article_size'));
    2022        add_action('wp_ajax_rss2post_update_automated_feeds', array($this, 'ajax_update_automated_feeds'));
     23        add_action('wp_ajax_rss2post_update_automated_settings', array($this, 'ajax_update_automated_settings'));
    2124        add_action('wp_ajax_rss2post_create_stripe_checkout', array($this, 'ajax_create_stripe_checkout'));
    2225        add_action('wp_ajax_rss2post_verify_payment', array($this, 'ajax_verify_payment_and_upgrade'));
     
    3033        add_action('wp_ajax_rss2post_save_api_keys', array($this, 'ajax_save_api_keys'));
    3134        add_action('rss2post_daily_subscription_check', array($this, 'check_all_pro_subscriptions'));
     35        add_filter('cron_schedules', array($this, 'add_custom_cron_schedules'));
    3236
    3337        if (!wp_next_scheduled('rss2post_daily_subscription_check')) {
    3438            wp_schedule_event(time(), 'daily', 'rss2post_daily_subscription_check');
    3539        }
     40    }
     41
     42    public function add_custom_cron_schedules($schedules) {
     43        $settings = get_option('rss2post_settings', array());
     44        $runs_per_day = isset($settings['runs_per_day']) ? (int)$settings['runs_per_day'] : 2;
     45        if ($runs_per_day < 1) $runs_per_day = 1;
     46        if ($runs_per_day > 10) $runs_per_day = 10;
     47       
     48        $interval = 86400 / $runs_per_day;
     49       
     50        $schedules['rss2post_custom_schedule'] = array(
     51            'interval' => $interval,
     52            'display'  => "Every {$interval} seconds (RSS2Post Custom)"
     53        );
     54        return $schedules;
    3655    }
    3756
     
    412431            wp_clear_scheduled_hook('rss2post_automated_hourly_job');
    413432            wp_clear_scheduled_hook('rss2post_automated_12h_job');
     433            wp_clear_scheduled_hook('rss2post_automated_job');
    414434            return;
    415435        }
     
    419439            return;
    420440        }
     441
     442        // --- New Distribution Logic Start ---
     443        $runs_per_day = isset($settings['runs_per_day']) ? (int)$settings['runs_per_day'] : 2;
     444        $articles_per_day = isset($settings['articles_per_day']) ? (int)$settings['articles_per_day'] : 10;
     445       
     446        // Load daily stats
     447        $daily_stats = get_option('rss2post_daily_stats', array(
     448            'date' => '',
     449            'runs_completed' => 0,
     450            'articles_posted' => 0
     451        ));
     452       
     453        $current_date = current_time('Y-m-d');
     454       
     455        // Reset stats if new day
     456        if ($daily_stats['date'] !== $current_date) {
     457            RSS2Post::log("New day detected (Old: {$daily_stats['date']}, New: {$current_date}). Resetting daily stats.", 'info');
     458            $daily_stats = array(
     459                'date' => $current_date,
     460                'runs_completed' => 0,
     461                'articles_posted' => 0
     462            );
     463        }
     464       
     465        // Check limits
     466        if ($daily_stats['runs_completed'] >= $runs_per_day) {
     467            RSS2Post::log("Daily run limit reached ({$daily_stats['runs_completed']}/{$runs_per_day}). Job exiting.", 'info');
     468            return;
     469        }
     470       
     471        if ($daily_stats['articles_posted'] >= $articles_per_day) {
     472            RSS2Post::log("Daily article limit reached ({$daily_stats['articles_posted']}/{$articles_per_day}). Job exiting.", 'info');
     473            // We still increment runs_completed to keep the schedule "ticking" correctly relative to the day?
     474            // Actually, if we skip because of article limit, we effectively consume a run slot without posting.
     475            $daily_stats['runs_completed']++;
     476            update_option('rss2post_daily_stats', $daily_stats);
     477            return;
     478        }
     479       
     480        // Calculate articles for this run
     481        $remaining_runs = $runs_per_day - $daily_stats['runs_completed'];
     482        // Safety check to avoid division by zero (though runs_completed check above should prevent this)
     483        if ($remaining_runs <= 0) $remaining_runs = 1;
     484       
     485        $remaining_articles = $articles_per_day - $daily_stats['articles_posted'];
     486        if ($remaining_articles < 0) $remaining_articles = 0;
     487       
     488        // Distribute remaining articles over remaining runs (ceil ensures we don't under-post)
     489        $articles_to_post_now = (int) ceil($remaining_articles / $remaining_runs);
     490       
     491        RSS2Post::log("Run #{$daily_stats['runs_completed']} of {$runs_per_day}. Posted today: {$daily_stats['articles_posted']}/{$articles_per_day}. Planning to post {$articles_to_post_now} articles.", 'info');
     492       
     493        if ($articles_to_post_now <= 0) {
     494            $daily_stats['runs_completed']++;
     495            update_option('rss2post_daily_stats', $daily_stats);
     496            return;
     497        }
     498       
     499        $max_articles_per_run = $articles_to_post_now;
     500        // --- New Distribution Logic End ---
    421501
    422502        $automated_feeds = isset($settings['automated_rss_feeds']) && is_array($settings['automated_rss_feeds']) ? $settings['automated_rss_feeds'] : array();
     
    550630        // Step 2: Round-robin distribution across feeds
    551631        $articles_to_post = array();
    552         $max_articles_per_run = 6; // Maximum total articles to post per run
     632        // $max_articles_per_run is set above based on daily distribution
    553633        $posted_count = 0;
    554634        $feed_indices = array();
     
    594674        // Step 3: Post the selected articles and track which feeds successfully posted
    595675        $successfully_posted_by_feed = array(); // Track successful posts per feed
     676        $actually_posted_count = 0;
    596677
    597678        foreach ($articles_to_post as $single_article_to_post) {
     
    602683
    603684            $content_language = isset($settings['content_language']) ? $settings['content_language'] : 'en';
     685            $article_size = isset($settings['article_size']) ? $settings['article_size'] : 'Small';
    604686
    605687            $data_for_api = array(
     
    615697                'available_tags' => $all_site_tags,
    616698                'content_language' => $content_language,
     699                'article_size' => $article_size,
    617700                'automated_tag_assign' => isset($settings['automated_tag_assign']) ? (bool)$settings['automated_tag_assign'] : false,
    618701                'auto_category_assign' => $auto_category_assign,
     
    658741                    }
    659742                    $successfully_posted_by_feed[$feed_url_key][] = $single_article_to_post['original_guid'];
     743                    $actually_posted_count++;
    660744                }
    661745            } else {
     
    665749            wp_cache_delete('alloptions', 'options');
    666750        }
     751       
     752        // Update stats
     753        $daily_stats['runs_completed']++;
     754        $daily_stats['articles_posted'] += $actually_posted_count;
     755        update_option('rss2post_daily_stats', $daily_stats);
     756        RSS2Post::log("Stats updated. Runs: {$daily_stats['runs_completed']}, Articles posted today: {$daily_stats['articles_posted']}", 'info');
    667757
    668758        // Step 4: Update last processed GUIDs ONLY for feeds that successfully posted articles
     
    699789        }
    700790        $new_status = isset($_POST['enabled']) && sanitize_text_field(wp_unslash($_POST['enabled'])) === 'true';
     791
     792        // Save frequency and articles settings if provided
     793        if (isset($_POST['runs_per_day'])) {
     794            $runs = (int) $_POST['runs_per_day'];
     795            if ($runs < 1) $runs = 1;
     796            if ($runs > 10) $runs = 10;
     797            $settings['runs_per_day'] = $runs;
     798        }
     799
     800        if (isset($_POST['articles_per_day'])) {
     801            $articles = (int) $_POST['articles_per_day'];
     802            if ($articles < 1) $articles = 1;
     803            if ($articles > 10) $articles = 10;
     804            $settings['articles_per_day'] = $articles;
     805        }
    701806       
    702807        // Always update feeds when toggling (both enabling and re-enabling)
     
    771876            wp_clear_scheduled_hook('rss2post_automated_hourly_job');
    772877            wp_clear_scheduled_hook('rss2post_automated_12h_job');
     878            wp_clear_scheduled_hook('rss2post_automated_job');
    773879
    774880            RSS2Post::log('Running automated posting job immediately upon enabling.', 'info');
     
    780886            }
    781887
    782             RSS2Post::log('Attempting to schedule rss2post_automated_12h_job.', 'info');
    783             if (!wp_next_scheduled('rss2post_automated_12h_job')) {
    784                 RSS2Post::log('No existing rss2post_automated_12h_job found. Proceeding to schedule.', 'info');
    785                 // Schedule the first run for a short time from now to make testing easier, then 'twicedaily' will take over.
    786                 // Or, use time() if the first run should be in 12 hours.
    787                 // For testing visibility, let's schedule it for 1 minute from now.
    788                 // $first_run_time = time() + 60; // For quick testing
    789                 // For production, it should align with 'twicedaily' logic, so time() is fine, WP handles the first run based on 'twicedaily'.
    790                 $scheduled = wp_schedule_event(time(), 'twicedaily', 'rss2post_automated_12h_job');
     888            RSS2Post::log('Attempting to schedule rss2post_automated_job.', 'info');
     889            if (!wp_next_scheduled('rss2post_automated_job')) {
     890                $scheduled = wp_schedule_event(time(), 'rss2post_custom_schedule', 'rss2post_automated_job');
    791891                if ($scheduled === false) {
    792                     RSS2Post::log('wp_schedule_event for rss2post_automated_12h_job FAILED.', 'error');
     892                    RSS2Post::log('wp_schedule_event for rss2post_automated_job FAILED.', 'error');
    793893                } else {
    794                     RSS2Post::log('wp_schedule_event for rss2post_automated_12h_job SUCCEEDED. Check cron tools.', 'info');
    795                 }
    796             } else {
    797                 RSS2Post::log('rss2post_automated_12h_job is already scheduled. Next run at timestamp: ' . wp_next_scheduled('rss2post_automated_12h_job'), 'info');
     894                    RSS2Post::log('wp_schedule_event for rss2post_automated_job SUCCEEDED with rss2post_custom_schedule.', 'info');
     895                }
    798896            }
    799897           
    800             // Let's get the event details again after attempting to schedule
    801             $event_after_schedule = wp_get_scheduled_event('rss2post_automated_12h_job');
    802             if ($event_after_schedule) {
    803                 RSS2Post::log('Verification with wp_get_scheduled_event for rss2post_automated_12h_job: Hook=' . $event_after_schedule->hook . ', Timestamp=' . $event_after_schedule->timestamp . ' (Next run: ' . get_date_from_gmt(gmdate('Y-m-d H:i:s', $event_after_schedule->timestamp), 'Y-m-d H:i:s') . '), Schedule=' . $event_after_schedule->schedule, 'info');
    804             } else {
    805                 RSS2Post::log('Verification with wp_get_scheduled_event for rss2post_automated_12h_job: No event found. This is unexpected if scheduling succeeded.', 'error');
    806             }
    807             wp_send_json_success(array('message' => 'Automated posting enabled. Please check your WordPress cron events list and debug logs for scheduling details.'));
     898            wp_send_json_success(array('message' => 'Automated posting enabled.'));
    808899        } else {
    809900            wp_clear_scheduled_hook('rss2post_automated_hourly_job');
    810901            wp_clear_scheduled_hook('rss2post_automated_12h_job');
    811             RSS2Post::log('Automated posting disabled and cron job rss2post_automated_12h_job cleared.', 'info');
     902            wp_clear_scheduled_hook('rss2post_automated_job');
     903            RSS2Post::log('Automated posting disabled and cron jobs cleared.', 'info');
    812904            wp_send_json_success(array('message' => 'Automated posting disabled.'));
    813905        }
     906    }
     907
     908    public function ajax_update_automated_settings() {
     909        check_ajax_referer('rss2post_nonce', 'nonce');
     910        if (!current_user_can('manage_options')) {
     911            wp_send_json_error(array('message' => 'Permission denied.'));
     912            return;
     913        }
     914
     915        $settings = get_option('rss2post_settings', array());
     916
     917        $runs = isset($_POST['runs_per_day']) ? (int)$_POST['runs_per_day'] : 2;
     918        if ($runs < 1) $runs = 1;
     919        if ($runs > 10) $runs = 10;
     920
     921        $articles = isset($_POST['articles_per_day']) ? (int)$_POST['articles_per_day'] : 10;
     922        if ($articles < 1) $articles = 1;
     923        if ($articles > 10) $articles = 10;
     924
     925        $settings['runs_per_day'] = $runs;
     926        $settings['articles_per_day'] = $articles;
     927        update_option('rss2post_settings', $settings);
     928
     929        // If automation is enabled, reschedule to apply new interval
     930        if (isset($settings['automated_posting_enabled']) && $settings['automated_posting_enabled']) {
     931             wp_clear_scheduled_hook('rss2post_automated_job');
     932             wp_schedule_event(time(), 'rss2post_custom_schedule', 'rss2post_automated_job');
     933             RSS2Post::log("Rescheduled automated job due to settings change. Runs: {$runs}, Articles per day: {$articles}", 'info');
     934        }
     935
     936        wp_send_json_success(array('message' => 'Automation settings saved.'));
    814937    }
    815938
     
    10461169    }
    10471170
     1171    public function ajax_save_article_size() {
     1172        check_ajax_referer('rss2post_nonce', 'nonce');
     1173       
     1174        if (!current_user_can('manage_options')) {
     1175            wp_send_json_error(array('message' => 'Permission denied.'));
     1176            return;
     1177        }
     1178       
     1179        $size = isset($_POST['article_size']) ? sanitize_text_field(wp_unslash($_POST['article_size'])) : '';
     1180       
     1181        if (empty($size)) {
     1182            wp_send_json_error(array('message' => 'Article size is required.'));
     1183            return;
     1184        }
     1185       
     1186        $valid_sizes = array('Small', 'Medium', 'Long');
     1187        if (!in_array($size, $valid_sizes)) {
     1188            wp_send_json_error(array('message' => 'Invalid article size.'));
     1189            return;
     1190        }
     1191       
     1192        $settings = get_option('rss2post_settings', array());
     1193        $settings['article_size'] = $size;
     1194        update_option('rss2post_settings', $settings);
     1195       
     1196        RSS2Post::log("Article size updated to: {$size}", 'info');
     1197        wp_send_json_success(array('message' => 'Article size saved successfully.'));
     1198    }
     1199
    10481200    public function ajax_update_automated_feeds() {
    10491201        check_ajax_referer('rss2post_nonce', 'nonce');
     
    12441396       
    12451397        $automated_posting_enabled = isset($settings['automated_posting_enabled']) ? (bool) $settings['automated_posting_enabled'] : false;
     1398        $runs_per_day = isset($settings['runs_per_day']) ? (int)$settings['runs_per_day'] : 2;
     1399        $articles_per_day = isset($settings['articles_per_day']) ? (int)$settings['articles_per_day'] : 10;
    12461400        $user_credits = isset($settings['user_credits']) ? (int)$settings['user_credits'] : 10;
    12471401        $content_language = isset($settings['content_language']) ? $settings['content_language'] : 'en';
     1402        $article_size = isset($settings['article_size']) ? $settings['article_size'] : 'Small';
    12481403       
    12491404        $payment_status = null;
     
    14891644
    14901645                <div class="rss2post-section">
    1491                     <h2>5. Language Settings</h2>
    1492                     <p class="description">Select the language for AI-generated content. This affects the language of titles, content, categories, and tags.</p>
     1646                    <h2>5. Content Settings</h2>
     1647                    <p class="description">Configure the language and length of your AI-generated content.</p>
    14931648                    <table class="form-table">
    14941649                        <tr>
     
    15171672                                <p class="description">Generated blog posts will be written in the selected language.</p>
    15181673                                <span id="language-save-feedback" style="margin-left: 10px;"></span>
     1674                            </td>
     1675                        </tr>
     1676                        <tr>
     1677                            <th><label for="article-size">Article Size</label></th>
     1678                            <td>
     1679                                <select id="article-size" name="article_size" class="regular-text">
     1680                                    <option value="Small" <?php selected($article_size, 'Small'); ?>>Small (Standard)</option>
     1681                                    <option value="Medium" <?php selected($article_size, 'Medium'); ?>>Medium (2x Length)</option>
     1682                                    <option value="Long" <?php selected($article_size, 'Long'); ?>>Long (3x Length)</option>
     1683                                </select>
     1684                                <p class="description">Choose the length of the generated articles.</p>
     1685                                <span id="article-size-save-feedback" style="margin-left: 10px;"></span>
    15191686                            </td>
    15201687                        </tr>
     
    15811748                            </td>
    15821749                        </tr>
     1750                        <tr class="automated-settings-row">
     1751                            <th><label for="runs-per-day">Posting Frequency (Runs per day)</label></th>
     1752                            <td>
     1753                                <input type="number" id="runs-per-day" min="1" max="10" value="<?php echo esc_attr($runs_per_day); ?>" class="small-text" <?php disabled($user_tier === 'free'); ?>>
     1754                                <p class="description">How many times per day the automated posting job should run (Max 10).</p>
     1755                            </td>
     1756                        </tr>
     1757                        <tr class="automated-settings-row">
     1758                            <th><label for="articles-per-day">Articles per Day</label></th>
     1759                            <td>
     1760                                <input type="number" id="articles-per-day" min="1" max="10" value="<?php echo esc_attr($articles_per_day); ?>" class="small-text" <?php disabled($user_tier === 'free'); ?>>
     1761                                <p class="description">Total articles to generate and post per day (Max 10). The plugin will automatically distribute these across your daily runs.</p>
     1762                            </td>
     1763                        </tr>
     1764                        <tr class="automated-settings-row">
     1765                            <th></th>
     1766                            <td>
     1767                                <button type="button" id="save-automated-settings-button" class="button" <?php disabled($user_tier === 'free'); ?>>Save Automation Settings</button>
     1768                                <span id="save-automated-settings-feedback" style="margin-left: 10px;"></span>
     1769                            </td>
     1770                        </tr>
    15831771                    </table>
    15841772                </div>
     
    19212109       
    19222110        $content_language = isset($settings['content_language']) ? $settings['content_language'] : 'en';
    1923        
     2111        $article_size = isset($settings['article_size']) ? $settings['article_size'] : 'Small';
     2112       
     2113        // Fetch all tags to send to backend to avoid creating new ones
     2114        $all_site_tags = get_tags(array('hide_empty' => 0, 'fields' => 'names'));
     2115
    19242116        $data = array(
    19252117            'pexels_api_key' => isset($settings['pexels_api_key']) ? $settings['pexels_api_key'] : '',
     
    19322124            'available_categories' => $all_category_names, // All categories for content matching
    19332125            'selected_categories' => $selected_category_names, // Mandatory categories from user selection
    1934             'available_tags' => array(), // Tags are now handled by the backend
     2126            'available_tags' => $all_site_tags, // Send all existing tags
    19352127            'content_language' => $content_language,
     2128            'article_size' => $article_size,
    19362129            'automated_tag_assign' => isset($settings['automated_tag_assign']) ? (bool)$settings['automated_tag_assign'] : false,
    19372130            'auto_category_assign' => $auto_category_assign,
  • rss-to-post-generator/tags/1.1.3/readme.txt

    r3438914 r3441218  
    44Requires at least: 5.6
    55Tested up to: 6.8
    6 Stable tag: 1.1.2
     6Stable tag: 1.1.3
    77License: GPLv2 or later
    88License URI: http://www.gnu.org/licenses/gpl-2.0.html
     
    138138== Changelog ==
    139139
     140= 1.1.3 =
     141* **Feature**: Added customizable automated posting frequency and article limits per day.
     142* **Feature**: Added article size option (Small, Medium, Long) for content generation.
     143* **Improvement**: Enhanced tag handling to use existing tags instead of creating new ones.
     144* **Improvement**: Automated posting now intelligently distributes articles throughout the day based on user settings.
     145
    140146= 1.1.2 =
    141147* Resolved PHP fatal error due to function redeclaration.
  • rss-to-post-generator/tags/1.1.3/rss2post.php

    r3438914 r3441218  
    33 * Plugin Name: RSS to Post Generator
    44 * Description: Generate blog posts from RSS feeds using AI content generation
    5  * Version: 1.1.2
     5 * Version: 1.1.3
    66 * Author: Samuel Bezerra Gomes
    77 * License: GPL v2 or later
     
    1515
    1616// Define plugin constants
    17 define('RSS2POST_VERSION', '1.1.2');
     17define('RSS2POST_VERSION', '1.1.3');
    1818define('RSS2POST_PLUGIN_DIR', plugin_dir_path(__FILE__));
    1919define('RSS2POST_PLUGIN_URL', plugin_dir_url(__FILE__));
  • rss-to-post-generator/trunk/assets/js/admin.js

    r3415485 r3441218  
    606606        });
    607607
     608        // Handler for article size selection
     609        $('#article-size').on('change', function() {
     610            const size = $(this).val();
     611            const $feedbackSpan = $('#article-size-save-feedback');
     612
     613            $feedbackSpan.text('Saving...').removeClass('success error').addClass('saving');
     614
     615            $.ajax({
     616                url: rss2post_ajax.ajax_url,
     617                type: 'POST',
     618                data: {
     619                    action: 'rss2post_save_article_size',
     620                    nonce: rss2post_ajax.nonce,
     621                    article_size: size
     622                },
     623                success: function(response) {
     624                    if (response.success) {
     625                        $feedbackSpan.text('Saved!').removeClass('saving error').addClass('success');
     626                    } else {
     627                        $feedbackSpan.text('Error: ' + (response.data.message || 'Could not save.')).removeClass('saving success').addClass('error');
     628                    }
     629                },
     630                error: function() {
     631                    $feedbackSpan.text('Error: AJAX request failed.').removeClass('saving success').addClass('error');
     632                },
     633                complete: function() {
     634                    setTimeout(function() {
     635                        $feedbackSpan.text('').removeClass('saving success error');
     636                    }, 3000);
     637                }
     638            });
     639        });
     640
    608641        $('#save-tags-button').on('click', function() {
    609642            const selectedTags = [];
     
    741774        });
    742775       
     776        $('#runs-per-day, #articles-per-day').on('input', function() {
     777            let val = $(this).val();
     778            // Allow empty string to let user clear and retype
     779            if (val === '') return;
     780           
     781            // Remove any non-digit characters
     782            val = val.replace(/[^0-9]/g, '');
     783           
     784            let num = parseInt(val);
     785            if (isNaN(num)) num = '';
     786            else if (num < 1) num = 1;
     787            else if (num > 10) num = 10;
     788           
     789            // Update value if it changed (handle strings vs numbers carefully)
     790            if (val !== '' && num !== parseInt($(this).val())) {
     791                 $(this).val(num);
     792            } else if (val !== $(this).val()) {
     793                 // Covers the case where non-digits were removed
     794                 $(this).val(val);
     795            }
     796        });
     797
     798        // Ensure valid value on blur
     799        $('#runs-per-day, #articles-per-day').on('blur', function() {
     800            let val = parseInt($(this).val());
     801            if (isNaN(val) || val < 1) {
     802                $(this).val(1);
     803            } else if (val > 10) {
     804                $(this).val(10);
     805            }
     806        });
     807
     808        $('#save-automated-settings-button').on('click', saveAutomatedSettings);
     809
    743810        $('input[name="image_source"]').on('change', function() {
    744811            if ($(this).val() === 'pexels') {
     
    898965    // The more complete one defined earlier in the file will be used.
    899966
     967    function saveAutomatedSettings() {
     968        const runsPerDay = parseInt($('#runs-per-day').val()) || 2;
     969        const articlesPerDay = parseInt($('#articles-per-day').val()) || 10;
     970       
     971        if (runsPerDay < 1 || runsPerDay > 10) {
     972            alert('Runs per day must be between 1 and 10.');
     973            return;
     974        }
     975        if (articlesPerDay < 1 || articlesPerDay > 10) {
     976            alert('Articles per day must be between 1 and 10.');
     977            return;
     978        }
     979
     980        const $button = $('#save-automated-settings-button');
     981        const $feedback = $('#save-automated-settings-feedback');
     982       
     983        $button.prop('disabled', true).text('Saving...');
     984        $feedback.text('Saving...').removeClass('success error').addClass('saving');
     985       
     986        $.ajax({
     987            url: rss2post_ajax.ajax_url,
     988            type: 'POST',
     989            data: {
     990                action: 'rss2post_update_automated_settings',
     991                nonce: rss2post_ajax.nonce,
     992                runs_per_day: runsPerDay,
     993                articles_per_day: articlesPerDay
     994            },
     995            success: function(response) {
     996                if (response.success) {
     997                    $feedback.text('Settings saved!').removeClass('saving error').addClass('success');
     998                } else {
     999                    $feedback.text('Error: ' + (response.data.message || 'Could not save.')).removeClass('saving success').addClass('error');
     1000                }
     1001            },
     1002            error: function() {
     1003                $feedback.text('Error: Failed to save settings.').removeClass('saving success').addClass('error');
     1004            },
     1005            complete: function() {
     1006                $button.prop('disabled', false).text('Save Automation Settings');
     1007                setTimeout(function() {
     1008                    $feedback.text('').removeClass('saving success error');
     1009                }, 3000);
     1010            }
     1011        });
     1012    }
     1013
    9001014    function toggleAutomatedPosting() {
    9011015        const isEnabled = $(this).is(':checked');
    9021016        const feeds = $('#rss-feeds').val().trim();
     1017        const runsPerDay = parseInt($('#runs-per-day').val()) || 2;
     1018        const articlesPerDay = parseInt($('#articles-per-day').val()) || 10;
    9031019
    9041020        if (userTier !== 'pro' && userTier !== 'lifetime' && isEnabled) {
     
    9111027            if (!feeds) {
    9121028                alert('Please enter at least one RSS feed URL in the "RSS Feed URLs" section before enabling automated posting.');
     1029                $(this).prop('checked', false);
     1030                return;
     1031            }
     1032            if (articlesPerDay > 10) {
     1033                alert('The maximum amount of generation + posts is 10 per day.');
    9131034                $(this).prop('checked', false);
    9141035                return;
     
    9321053                enabled: isEnabled,
    9331054                feeds: feeds,
     1055                runs_per_day: runsPerDay,
     1056                articles_per_day: articlesPerDay,
    9341057                current_username: $('#wp-username').val().trim(),
    9351058                current_password: $('#wp-password').val().trim()
  • rss-to-post-generator/trunk/includes/class-admin.php

    r3438847 r3441218  
    1616        add_action('rss2post_automated_hourly_job', array($this, 'do_automated_posting_job')); // Retained in case it's a valid, albeit unused, hook for some users
    1717        add_action('rss2post_automated_12h_job', array($this, 'do_automated_posting_job'));
     18        add_action('rss2post_automated_job', array($this, 'do_automated_posting_job'));
    1819        add_action('wp_ajax_rss2post_save_credentials', array($this, 'ajax_save_credentials'));
    1920        add_action('wp_ajax_rss2post_save_language', array($this, 'ajax_save_language'));
     21        add_action('wp_ajax_rss2post_save_article_size', array($this, 'ajax_save_article_size'));
    2022        add_action('wp_ajax_rss2post_update_automated_feeds', array($this, 'ajax_update_automated_feeds'));
     23        add_action('wp_ajax_rss2post_update_automated_settings', array($this, 'ajax_update_automated_settings'));
    2124        add_action('wp_ajax_rss2post_create_stripe_checkout', array($this, 'ajax_create_stripe_checkout'));
    2225        add_action('wp_ajax_rss2post_verify_payment', array($this, 'ajax_verify_payment_and_upgrade'));
     
    3033        add_action('wp_ajax_rss2post_save_api_keys', array($this, 'ajax_save_api_keys'));
    3134        add_action('rss2post_daily_subscription_check', array($this, 'check_all_pro_subscriptions'));
     35        add_filter('cron_schedules', array($this, 'add_custom_cron_schedules'));
    3236
    3337        if (!wp_next_scheduled('rss2post_daily_subscription_check')) {
    3438            wp_schedule_event(time(), 'daily', 'rss2post_daily_subscription_check');
    3539        }
     40    }
     41
     42    public function add_custom_cron_schedules($schedules) {
     43        $settings = get_option('rss2post_settings', array());
     44        $runs_per_day = isset($settings['runs_per_day']) ? (int)$settings['runs_per_day'] : 2;
     45        if ($runs_per_day < 1) $runs_per_day = 1;
     46        if ($runs_per_day > 10) $runs_per_day = 10;
     47       
     48        $interval = 86400 / $runs_per_day;
     49       
     50        $schedules['rss2post_custom_schedule'] = array(
     51            'interval' => $interval,
     52            'display'  => "Every {$interval} seconds (RSS2Post Custom)"
     53        );
     54        return $schedules;
    3655    }
    3756
     
    412431            wp_clear_scheduled_hook('rss2post_automated_hourly_job');
    413432            wp_clear_scheduled_hook('rss2post_automated_12h_job');
     433            wp_clear_scheduled_hook('rss2post_automated_job');
    414434            return;
    415435        }
     
    419439            return;
    420440        }
     441
     442        // --- New Distribution Logic Start ---
     443        $runs_per_day = isset($settings['runs_per_day']) ? (int)$settings['runs_per_day'] : 2;
     444        $articles_per_day = isset($settings['articles_per_day']) ? (int)$settings['articles_per_day'] : 10;
     445       
     446        // Load daily stats
     447        $daily_stats = get_option('rss2post_daily_stats', array(
     448            'date' => '',
     449            'runs_completed' => 0,
     450            'articles_posted' => 0
     451        ));
     452       
     453        $current_date = current_time('Y-m-d');
     454       
     455        // Reset stats if new day
     456        if ($daily_stats['date'] !== $current_date) {
     457            RSS2Post::log("New day detected (Old: {$daily_stats['date']}, New: {$current_date}). Resetting daily stats.", 'info');
     458            $daily_stats = array(
     459                'date' => $current_date,
     460                'runs_completed' => 0,
     461                'articles_posted' => 0
     462            );
     463        }
     464       
     465        // Check limits
     466        if ($daily_stats['runs_completed'] >= $runs_per_day) {
     467            RSS2Post::log("Daily run limit reached ({$daily_stats['runs_completed']}/{$runs_per_day}). Job exiting.", 'info');
     468            return;
     469        }
     470       
     471        if ($daily_stats['articles_posted'] >= $articles_per_day) {
     472            RSS2Post::log("Daily article limit reached ({$daily_stats['articles_posted']}/{$articles_per_day}). Job exiting.", 'info');
     473            // We still increment runs_completed to keep the schedule "ticking" correctly relative to the day?
     474            // Actually, if we skip because of article limit, we effectively consume a run slot without posting.
     475            $daily_stats['runs_completed']++;
     476            update_option('rss2post_daily_stats', $daily_stats);
     477            return;
     478        }
     479       
     480        // Calculate articles for this run
     481        $remaining_runs = $runs_per_day - $daily_stats['runs_completed'];
     482        // Safety check to avoid division by zero (though runs_completed check above should prevent this)
     483        if ($remaining_runs <= 0) $remaining_runs = 1;
     484       
     485        $remaining_articles = $articles_per_day - $daily_stats['articles_posted'];
     486        if ($remaining_articles < 0) $remaining_articles = 0;
     487       
     488        // Distribute remaining articles over remaining runs (ceil ensures we don't under-post)
     489        $articles_to_post_now = (int) ceil($remaining_articles / $remaining_runs);
     490       
     491        RSS2Post::log("Run #{$daily_stats['runs_completed']} of {$runs_per_day}. Posted today: {$daily_stats['articles_posted']}/{$articles_per_day}. Planning to post {$articles_to_post_now} articles.", 'info');
     492       
     493        if ($articles_to_post_now <= 0) {
     494            $daily_stats['runs_completed']++;
     495            update_option('rss2post_daily_stats', $daily_stats);
     496            return;
     497        }
     498       
     499        $max_articles_per_run = $articles_to_post_now;
     500        // --- New Distribution Logic End ---
    421501
    422502        $automated_feeds = isset($settings['automated_rss_feeds']) && is_array($settings['automated_rss_feeds']) ? $settings['automated_rss_feeds'] : array();
     
    550630        // Step 2: Round-robin distribution across feeds
    551631        $articles_to_post = array();
    552         $max_articles_per_run = 6; // Maximum total articles to post per run
     632        // $max_articles_per_run is set above based on daily distribution
    553633        $posted_count = 0;
    554634        $feed_indices = array();
     
    594674        // Step 3: Post the selected articles and track which feeds successfully posted
    595675        $successfully_posted_by_feed = array(); // Track successful posts per feed
     676        $actually_posted_count = 0;
    596677
    597678        foreach ($articles_to_post as $single_article_to_post) {
     
    602683
    603684            $content_language = isset($settings['content_language']) ? $settings['content_language'] : 'en';
     685            $article_size = isset($settings['article_size']) ? $settings['article_size'] : 'Small';
    604686
    605687            $data_for_api = array(
     
    615697                'available_tags' => $all_site_tags,
    616698                'content_language' => $content_language,
     699                'article_size' => $article_size,
    617700                'automated_tag_assign' => isset($settings['automated_tag_assign']) ? (bool)$settings['automated_tag_assign'] : false,
    618701                'auto_category_assign' => $auto_category_assign,
     
    658741                    }
    659742                    $successfully_posted_by_feed[$feed_url_key][] = $single_article_to_post['original_guid'];
     743                    $actually_posted_count++;
    660744                }
    661745            } else {
     
    665749            wp_cache_delete('alloptions', 'options');
    666750        }
     751       
     752        // Update stats
     753        $daily_stats['runs_completed']++;
     754        $daily_stats['articles_posted'] += $actually_posted_count;
     755        update_option('rss2post_daily_stats', $daily_stats);
     756        RSS2Post::log("Stats updated. Runs: {$daily_stats['runs_completed']}, Articles posted today: {$daily_stats['articles_posted']}", 'info');
    667757
    668758        // Step 4: Update last processed GUIDs ONLY for feeds that successfully posted articles
     
    699789        }
    700790        $new_status = isset($_POST['enabled']) && sanitize_text_field(wp_unslash($_POST['enabled'])) === 'true';
     791
     792        // Save frequency and articles settings if provided
     793        if (isset($_POST['runs_per_day'])) {
     794            $runs = (int) $_POST['runs_per_day'];
     795            if ($runs < 1) $runs = 1;
     796            if ($runs > 10) $runs = 10;
     797            $settings['runs_per_day'] = $runs;
     798        }
     799
     800        if (isset($_POST['articles_per_day'])) {
     801            $articles = (int) $_POST['articles_per_day'];
     802            if ($articles < 1) $articles = 1;
     803            if ($articles > 10) $articles = 10;
     804            $settings['articles_per_day'] = $articles;
     805        }
    701806       
    702807        // Always update feeds when toggling (both enabling and re-enabling)
     
    771876            wp_clear_scheduled_hook('rss2post_automated_hourly_job');
    772877            wp_clear_scheduled_hook('rss2post_automated_12h_job');
     878            wp_clear_scheduled_hook('rss2post_automated_job');
    773879
    774880            RSS2Post::log('Running automated posting job immediately upon enabling.', 'info');
     
    780886            }
    781887
    782             RSS2Post::log('Attempting to schedule rss2post_automated_12h_job.', 'info');
    783             if (!wp_next_scheduled('rss2post_automated_12h_job')) {
    784                 RSS2Post::log('No existing rss2post_automated_12h_job found. Proceeding to schedule.', 'info');
    785                 // Schedule the first run for a short time from now to make testing easier, then 'twicedaily' will take over.
    786                 // Or, use time() if the first run should be in 12 hours.
    787                 // For testing visibility, let's schedule it for 1 minute from now.
    788                 // $first_run_time = time() + 60; // For quick testing
    789                 // For production, it should align with 'twicedaily' logic, so time() is fine, WP handles the first run based on 'twicedaily'.
    790                 $scheduled = wp_schedule_event(time(), 'twicedaily', 'rss2post_automated_12h_job');
     888            RSS2Post::log('Attempting to schedule rss2post_automated_job.', 'info');
     889            if (!wp_next_scheduled('rss2post_automated_job')) {
     890                $scheduled = wp_schedule_event(time(), 'rss2post_custom_schedule', 'rss2post_automated_job');
    791891                if ($scheduled === false) {
    792                     RSS2Post::log('wp_schedule_event for rss2post_automated_12h_job FAILED.', 'error');
     892                    RSS2Post::log('wp_schedule_event for rss2post_automated_job FAILED.', 'error');
    793893                } else {
    794                     RSS2Post::log('wp_schedule_event for rss2post_automated_12h_job SUCCEEDED. Check cron tools.', 'info');
    795                 }
    796             } else {
    797                 RSS2Post::log('rss2post_automated_12h_job is already scheduled. Next run at timestamp: ' . wp_next_scheduled('rss2post_automated_12h_job'), 'info');
     894                    RSS2Post::log('wp_schedule_event for rss2post_automated_job SUCCEEDED with rss2post_custom_schedule.', 'info');
     895                }
    798896            }
    799897           
    800             // Let's get the event details again after attempting to schedule
    801             $event_after_schedule = wp_get_scheduled_event('rss2post_automated_12h_job');
    802             if ($event_after_schedule) {
    803                 RSS2Post::log('Verification with wp_get_scheduled_event for rss2post_automated_12h_job: Hook=' . $event_after_schedule->hook . ', Timestamp=' . $event_after_schedule->timestamp . ' (Next run: ' . get_date_from_gmt(gmdate('Y-m-d H:i:s', $event_after_schedule->timestamp), 'Y-m-d H:i:s') . '), Schedule=' . $event_after_schedule->schedule, 'info');
    804             } else {
    805                 RSS2Post::log('Verification with wp_get_scheduled_event for rss2post_automated_12h_job: No event found. This is unexpected if scheduling succeeded.', 'error');
    806             }
    807             wp_send_json_success(array('message' => 'Automated posting enabled. Please check your WordPress cron events list and debug logs for scheduling details.'));
     898            wp_send_json_success(array('message' => 'Automated posting enabled.'));
    808899        } else {
    809900            wp_clear_scheduled_hook('rss2post_automated_hourly_job');
    810901            wp_clear_scheduled_hook('rss2post_automated_12h_job');
    811             RSS2Post::log('Automated posting disabled and cron job rss2post_automated_12h_job cleared.', 'info');
     902            wp_clear_scheduled_hook('rss2post_automated_job');
     903            RSS2Post::log('Automated posting disabled and cron jobs cleared.', 'info');
    812904            wp_send_json_success(array('message' => 'Automated posting disabled.'));
    813905        }
     906    }
     907
     908    public function ajax_update_automated_settings() {
     909        check_ajax_referer('rss2post_nonce', 'nonce');
     910        if (!current_user_can('manage_options')) {
     911            wp_send_json_error(array('message' => 'Permission denied.'));
     912            return;
     913        }
     914
     915        $settings = get_option('rss2post_settings', array());
     916
     917        $runs = isset($_POST['runs_per_day']) ? (int)$_POST['runs_per_day'] : 2;
     918        if ($runs < 1) $runs = 1;
     919        if ($runs > 10) $runs = 10;
     920
     921        $articles = isset($_POST['articles_per_day']) ? (int)$_POST['articles_per_day'] : 10;
     922        if ($articles < 1) $articles = 1;
     923        if ($articles > 10) $articles = 10;
     924
     925        $settings['runs_per_day'] = $runs;
     926        $settings['articles_per_day'] = $articles;
     927        update_option('rss2post_settings', $settings);
     928
     929        // If automation is enabled, reschedule to apply new interval
     930        if (isset($settings['automated_posting_enabled']) && $settings['automated_posting_enabled']) {
     931             wp_clear_scheduled_hook('rss2post_automated_job');
     932             wp_schedule_event(time(), 'rss2post_custom_schedule', 'rss2post_automated_job');
     933             RSS2Post::log("Rescheduled automated job due to settings change. Runs: {$runs}, Articles per day: {$articles}", 'info');
     934        }
     935
     936        wp_send_json_success(array('message' => 'Automation settings saved.'));
    814937    }
    815938
     
    10461169    }
    10471170
     1171    public function ajax_save_article_size() {
     1172        check_ajax_referer('rss2post_nonce', 'nonce');
     1173       
     1174        if (!current_user_can('manage_options')) {
     1175            wp_send_json_error(array('message' => 'Permission denied.'));
     1176            return;
     1177        }
     1178       
     1179        $size = isset($_POST['article_size']) ? sanitize_text_field(wp_unslash($_POST['article_size'])) : '';
     1180       
     1181        if (empty($size)) {
     1182            wp_send_json_error(array('message' => 'Article size is required.'));
     1183            return;
     1184        }
     1185       
     1186        $valid_sizes = array('Small', 'Medium', 'Long');
     1187        if (!in_array($size, $valid_sizes)) {
     1188            wp_send_json_error(array('message' => 'Invalid article size.'));
     1189            return;
     1190        }
     1191       
     1192        $settings = get_option('rss2post_settings', array());
     1193        $settings['article_size'] = $size;
     1194        update_option('rss2post_settings', $settings);
     1195       
     1196        RSS2Post::log("Article size updated to: {$size}", 'info');
     1197        wp_send_json_success(array('message' => 'Article size saved successfully.'));
     1198    }
     1199
    10481200    public function ajax_update_automated_feeds() {
    10491201        check_ajax_referer('rss2post_nonce', 'nonce');
     
    12441396       
    12451397        $automated_posting_enabled = isset($settings['automated_posting_enabled']) ? (bool) $settings['automated_posting_enabled'] : false;
     1398        $runs_per_day = isset($settings['runs_per_day']) ? (int)$settings['runs_per_day'] : 2;
     1399        $articles_per_day = isset($settings['articles_per_day']) ? (int)$settings['articles_per_day'] : 10;
    12461400        $user_credits = isset($settings['user_credits']) ? (int)$settings['user_credits'] : 10;
    12471401        $content_language = isset($settings['content_language']) ? $settings['content_language'] : 'en';
     1402        $article_size = isset($settings['article_size']) ? $settings['article_size'] : 'Small';
    12481403       
    12491404        $payment_status = null;
     
    14891644
    14901645                <div class="rss2post-section">
    1491                     <h2>5. Language Settings</h2>
    1492                     <p class="description">Select the language for AI-generated content. This affects the language of titles, content, categories, and tags.</p>
     1646                    <h2>5. Content Settings</h2>
     1647                    <p class="description">Configure the language and length of your AI-generated content.</p>
    14931648                    <table class="form-table">
    14941649                        <tr>
     
    15171672                                <p class="description">Generated blog posts will be written in the selected language.</p>
    15181673                                <span id="language-save-feedback" style="margin-left: 10px;"></span>
     1674                            </td>
     1675                        </tr>
     1676                        <tr>
     1677                            <th><label for="article-size">Article Size</label></th>
     1678                            <td>
     1679                                <select id="article-size" name="article_size" class="regular-text">
     1680                                    <option value="Small" <?php selected($article_size, 'Small'); ?>>Small (Standard)</option>
     1681                                    <option value="Medium" <?php selected($article_size, 'Medium'); ?>>Medium (2x Length)</option>
     1682                                    <option value="Long" <?php selected($article_size, 'Long'); ?>>Long (3x Length)</option>
     1683                                </select>
     1684                                <p class="description">Choose the length of the generated articles.</p>
     1685                                <span id="article-size-save-feedback" style="margin-left: 10px;"></span>
    15191686                            </td>
    15201687                        </tr>
     
    15811748                            </td>
    15821749                        </tr>
     1750                        <tr class="automated-settings-row">
     1751                            <th><label for="runs-per-day">Posting Frequency (Runs per day)</label></th>
     1752                            <td>
     1753                                <input type="number" id="runs-per-day" min="1" max="10" value="<?php echo esc_attr($runs_per_day); ?>" class="small-text" <?php disabled($user_tier === 'free'); ?>>
     1754                                <p class="description">How many times per day the automated posting job should run (Max 10).</p>
     1755                            </td>
     1756                        </tr>
     1757                        <tr class="automated-settings-row">
     1758                            <th><label for="articles-per-day">Articles per Day</label></th>
     1759                            <td>
     1760                                <input type="number" id="articles-per-day" min="1" max="10" value="<?php echo esc_attr($articles_per_day); ?>" class="small-text" <?php disabled($user_tier === 'free'); ?>>
     1761                                <p class="description">Total articles to generate and post per day (Max 10). The plugin will automatically distribute these across your daily runs.</p>
     1762                            </td>
     1763                        </tr>
     1764                        <tr class="automated-settings-row">
     1765                            <th></th>
     1766                            <td>
     1767                                <button type="button" id="save-automated-settings-button" class="button" <?php disabled($user_tier === 'free'); ?>>Save Automation Settings</button>
     1768                                <span id="save-automated-settings-feedback" style="margin-left: 10px;"></span>
     1769                            </td>
     1770                        </tr>
    15831771                    </table>
    15841772                </div>
     
    19212109       
    19222110        $content_language = isset($settings['content_language']) ? $settings['content_language'] : 'en';
    1923        
     2111        $article_size = isset($settings['article_size']) ? $settings['article_size'] : 'Small';
     2112       
     2113        // Fetch all tags to send to backend to avoid creating new ones
     2114        $all_site_tags = get_tags(array('hide_empty' => 0, 'fields' => 'names'));
     2115
    19242116        $data = array(
    19252117            'pexels_api_key' => isset($settings['pexels_api_key']) ? $settings['pexels_api_key'] : '',
     
    19322124            'available_categories' => $all_category_names, // All categories for content matching
    19332125            'selected_categories' => $selected_category_names, // Mandatory categories from user selection
    1934             'available_tags' => array(), // Tags are now handled by the backend
     2126            'available_tags' => $all_site_tags, // Send all existing tags
    19352127            'content_language' => $content_language,
     2128            'article_size' => $article_size,
    19362129            'automated_tag_assign' => isset($settings['automated_tag_assign']) ? (bool)$settings['automated_tag_assign'] : false,
    19372130            'auto_category_assign' => $auto_category_assign,
  • rss-to-post-generator/trunk/readme.txt

    r3438914 r3441218  
    44Requires at least: 5.6
    55Tested up to: 6.8
    6 Stable tag: 1.1.2
     6Stable tag: 1.1.3
    77License: GPLv2 or later
    88License URI: http://www.gnu.org/licenses/gpl-2.0.html
     
    138138== Changelog ==
    139139
     140= 1.1.3 =
     141* **Feature**: Added customizable automated posting frequency and article limits per day.
     142* **Feature**: Added article size option (Small, Medium, Long) for content generation.
     143* **Improvement**: Enhanced tag handling to use existing tags instead of creating new ones.
     144* **Improvement**: Automated posting now intelligently distributes articles throughout the day based on user settings.
     145
    140146= 1.1.2 =
    141147* Resolved PHP fatal error due to function redeclaration.
  • rss-to-post-generator/trunk/rss2post.php

    r3438914 r3441218  
    33 * Plugin Name: RSS to Post Generator
    44 * Description: Generate blog posts from RSS feeds using AI content generation
    5  * Version: 1.1.2
     5 * Version: 1.1.3
    66 * Author: Samuel Bezerra Gomes
    77 * License: GPL v2 or later
     
    1515
    1616// Define plugin constants
    17 define('RSS2POST_VERSION', '1.1.2');
     17define('RSS2POST_VERSION', '1.1.3');
    1818define('RSS2POST_PLUGIN_DIR', plugin_dir_path(__FILE__));
    1919define('RSS2POST_PLUGIN_URL', plugin_dir_url(__FILE__));
Note: See TracChangeset for help on using the changeset viewer.